I'm used to WinForms, so this isn't behaving quite how I'd expect. In the code below, ActiveView is a Frame, Register is Page. I want to load the register page into the ActiveView and then change the text on a button. Even though the page loads, the debugger says that ActiveView.Content == null in SetCloseButtonText. Why is that?
private void btnRegister_Click(object sender, RoutedEventArgs e)
{
SwapActiveView(Register);
}
public void SwapActiveView(Page NewPage)
{
if (ActiveView.Content == null || !ActiveView.Content.Equals(NewPage))
{
if (ActiveView.Content != null)
{
PreviousViews.Add((Page)ActiveView.Content);
}
ActiveView.Content = NewPage;
}
else
{
ActiveView.Content = NewPage;
}
SetCloseButtonText();
}
private void SetCloseButtonText()
{
if (PreviousViews.Count == 0 && ActiveView.Content == null)
{
tbCloseButton.Text = "Close";
}
else
{
tbCloseButton.Text = "Back";
}
}
I ended up finding the answer. A frame navigates asynchronously, whether you call the navigate function or just change the content. So I simply needed to add the method and call my function then.
private void ActiveView_Navigated(object sender, NavigationEventArgs e)
{
SetCloseButtonText();
}
Related
I need to ignore a page while navigating back, I tried the following:
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
{
if (args.NavigationMode == NavigationMode.Back)
{
args.Cancel = true;
NavigationService.Navigate(typeof(MainPage));
} else {
args.Cancel = false;
}
await Task.CompletedTask;
}
On the page that I am navigating away from, but it seems to only cancel the navigation.
What is the best way to tackle this issue?
In the page that you want to ignore, you can add the following method. This will allow to remove the current page from the navigation history.
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// here NavigationService is supposed to be an instance of Template10 INavigationService
var backStack = NavigationService.FrameFacade.BackStack;
if (backStack.Count > 0 && backStack.Last().SourcePageType == this.GetType())
{
backStack.Remove(backStack.Last());
}
base.OnNavigatedFrom(e);
}
Not sure if anyone can be of help here, but I have a comboxbox bound to a viewmodel property, on a form who's value is set by an event. It is working in house, but there is one client where the event fires, the value is set (I know because I added some logging), but their screen is not updated. I have a copy of the database, and I mirror the steps and it works. Any ideas why that could be happening? I included the code below, but it is pretty basic.
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TriggerId")
{
Method();
}
}
private void Method()
{
ComboBoxSelectedProperty = null;
if (TriggerId != null)
{
var object = Work.ObjectStore.GetById((int)TriggerId);
if (object != null)
{
ComboBoxSelectedProperty = Work.AssociatedObjectStore.GetByObjenct(object);
}
NotifyPropertyChanged("ComboboxSourceSource");
}
}
}
I need to restore the scroll position of a GridView in my windows app. I'm trying to find the right time to call ScrollViewer.ScrollToHorizontalOffset() and have it succeed.
If I call it in the OnNavigatedTo override, it has no effect:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
DataContext = LoadModel();
RestoreScrollPos();
}
If I call it in the Loaded handler for the page, it has no effect.
private void onPageLoaded(object sender, RoutedEventArgs e)
{
DataContext = LoadModel();
RestoreScrollPos();
}
If I do something like the following, then it works but it is jarring because the GridView is first drawn at scroll position 0 and then snaps to the new scroll position.
var dontAwaitHere =
Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
delegate()
{
RestoreScrollPos();
});
If I try to repro this behavior from the default visual studio GridView project, it seems to work most of the time, but I did see it not work once. I believe there is some sort of race condition, and I suspect I am putting it in the wrong place.
QUESTION = Where should I call RestoreScrollPos() Or where should I look to debug this?
private void RestoreScrollPos()
{
var scrollViewer = findScrollViewer(itemGridView);
if (scrollViewer != null)
{
scrollViewer.ScrollToHorizontalOffset(100000.0); // TODO test
}
}
public static ScrollViewer findScrollViewer(DependencyObject el)
{
ScrollViewer retValue = findDescendant<ScrollViewer>(el);
return retValue;
}
public static tType findDescendant<tType>(DependencyObject el)
where tType : DependencyObject
{
tType retValue = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(el);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(el, i);
if (child is tType)
{
retValue = (tType)child;
break;
}
retValue = findDescendant<tType>(child);
if (retValue != null)
{
break;
}
}
return retValue;
}
You should call RestoreScrollPos only after the grid has finished loading:
public MyPageConstructor()
{
this.InitializeComponent();
this.itemGridView.Loaded += (s,e) => itemGridView_Loaded(s, e);
}
private void itemGridView_Loaded(object sender, RoutedEventArgs e)
{
RestoreScrollPos();
}
As to where to load the data, you should try in LoadState:
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
DataContext = LoadModel();
base.LoadState(navigationParameter, pageState);
}
I started an WPF application in a mvvm way. The main window contains a frame-control for navigating through differnt pages. For this I use a simple NavigationService for now:
public class NavigationService : INavigationService
{
private Frame _mainFrame;
#region INavigationService Member
public event NavigatingCancelEventHandler Navigating;
public void NavigateTo(Uri uri)
{
if(EnsureMainFrame())
{
_mainFrame.Navigate(uri);
}
}
public void GoBack()
{
if(EnsureMainFrame() && _mainFrame.CanGoBack)
{
_mainFrame.GoBack();
}
}
#endregion
private bool EnsureMainFrame()
{
if(_mainFrame != null)
{
return true;
}
var mainWindow = (System.Windows.Application.Current.MainWindow as MainWindow);
if(mainWindow != null)
{
_mainFrame = mainWindow.NavigationFrame;
if(_mainFrame != null)
{
// Could be null if the app runs inside a design tool
_mainFrame.Navigating += (s, e) =>
{
if (Navigating != null)
{
Navigating(s, e);
}
};
return true;
}
}
return false;
}
}
On Page1 a button press forces the navigation to Page2 using th NavigationService.
On Page2 there is a TextBox. If the TextBox is focused i can use ALT + left arrow key to navigate back to Page1. How can I disable this behavior?
I tried setting KeyboardNavigation.DirectionalNavigation="None" in the frame-control and also in the TextBox-Control without success.
Add the following event handler to the textbox to disable the alt + left navigation:
private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
{
if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
&& (Keyboard.IsKeyDown(Key.Left)))
{
e.Handled = true;
}
}
XAML
<TextBox ... KeyDown="textBox1_PreviewKeyDown" />
EDIT: changed to PreviewKeyDown in order to capture arrow key events
Suppose that, after a click on a asp:button, I'd like to store the actual "view", with selected value for each controllers, like asp:checkbox, or input inserted by users on inputbox.
Than, I change page, with a link such as "back to the previous page". I'd like so "restore" the old actual form, before leaving it.
Is it possible with .NET? Or I need to implements all controls in variables and put them in session?
There are several ways how you can persist or pass values between ASP.NET pages.
Have a look here for more informations.
Because you've mentioned that you have "tons" of controls to store and restore, i've tried to find a way to automatize this process.
Here's my approach which uses Session as storage to persist all controls' values of type IPostBackDataHandler. Not really tested but hopefully helpful anyway.
using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
public class FormStorage
{
private System.Web.UI.Page _page = null;
public Dictionary<String, Dictionary<String, String>> storage
{
get { return (Dictionary<String, Dictionary<String, String>>)_page.Session["FormStorage"]; }
set { _page.Session["FormStorage"] = value; }
}
public FormStorage(System.Web.UI.Page page)
{
_page = page;
initHandler();
if (this.storage == null)
{
this.storage = new Dictionary<String, Dictionary<String, String>>();
}
if(!this.storage.ContainsKey(_page.Request.Path))
this.storage.Add(_page.Request.Path, new Dictionary<String, String>());
}
private void initHandler()
{
_page.Init += Init;
_page.Load += Load;
}
private void Init(Object sender, EventArgs e)
{
loadForm();
}
private void Load(Object sender, EventArgs e)
{
saveForm();
}
private void loadForm()
{
var pageStorage = storage[_page.Request.Path];
var e = pageStorage.GetEnumerator();
while (e.MoveNext())
{
loadControl(e.Current.Key, e.Current.Value);
}
}
private void loadControl(String ID, String value)
{
Control control = findControlRecursively(_page, ID);
if (control != null)
{
setControlValue(control, value);
}
}
private void setControlValue(Control control, String value)
{
if (control is ITextControl)
{
ITextControl txt = (ITextControl)control;
txt.Text = value == null ? "" : value;
}
else if (control is ICheckBoxControl)
{
ICheckBoxControl chk = (ICheckBoxControl)control;
chk.Checked = value != null;
}
else if (control is ListControl)
{
ListControl ddl = (ListControl)control;
if (value == null)
ddl.SelectedIndex = -1;
else
ddl.SelectedValue = value;
}
}
public void saveForm()
{
saveControlRecursively(this._page);
}
private void saveControlRecursively(Control rootControl)
{
if (rootControl is IPostBackDataHandler)
{
var postBackData = _page.Request.Form[rootControl.ID];
storage[_page.Request.Path][rootControl.ID] = postBackData;
}
if (rootControl.HasControls())
foreach (Control subControl in rootControl.Controls)
saveControlRecursively(subControl);
}
private static Control findControlRecursively(Control rootControl, String idToFind)
{
if (rootControl.ID == idToFind)
return rootControl;
foreach (Control subControl in rootControl.Controls)
{
Control controlToReturn = findControlRecursively(subControl, idToFind);
if (controlToReturn != null)
{
return controlToReturn;
}
}
return null;
}
}
Here's an examplary implementation in a page that redirects to another page:
private FormStorage storage;
protected void Page_PreInit(object sender, EventArgs e)
{
storage = new FormStorage(this);
}
protected void BtnRedirect_Click(object sender, EventArgs e)
{
Response.Redirect("RedirectForm.aspx");
}
Note that it loads and saves implicitely on Page_Load/Page_PreRender. Therefor the FormStorage instance must be created in Page_PreInit.
If you want to change values after Page_Load programmatically, you need to call storage.saveForm() manually (for example in Page_PreRender) to ensure that these values are overridden, because FormStorage will auto-save all postback data in Page_Load.
Edit: The history.go approach of sh4nx0r is probably better since it's more scalable. My approach uses the Session and would not be appropriate for an internet-application with a huge amount of (possible) users.
One (small) advantage of mine is that it would work even when javascript is disabled. One larger advantage is that you can control to what page you want to redirect. You can even restore values across multiple redirects.
You don't need sessions to do this. Just make use of JavaScript.
Consider you have two pages Page1.aspx and Page2.aspx.
You have textboxes , checkboxes , radiobuttons on Page1.aspx and you have a button "Submit". By clicking the button it takes to Page2.aspx.
Just have a button "Back to Previous Page" on Page2.aspx , which on clicking takes you to Page1.aspx with all those values still being there. To do this, Add this line on your Page2.aspx page_load event
protected void Page_Load(object sender, EventArgs e)
{
btnBackButton.Attributes.Add("onClick", "javascript:window.history.go(-1);return false;");
}