How to remove Frame.BackStackDepth? - c#

I am developing a windows 8.1 app which includes several pages. I want to know how can I remove Frame.BackStackDepth so that when user press the back button the app will navigate to the first page.
I tried this but it remove the previous Frame only.
private void backButton_Click(object sender, RoutedEventArgs e)
{
this.Frame.BackStack.RemoveAt(this.Frame.BackStackDepth-1);
this.Frame.GoBack();
}

You can use this extension method:
public static void ResetBackStack(this Frame frame)
{
PageStackEntry mainPage = frame.BackStack.Where(b => b.SourcePageType == typeof(YourPageType)).FirstOrDefault();
frame.BackStack.Clear();
if (mainPage != null)
{
frame.BackStack.Add(mainPage);
}
}
Just override the BackPressed event inside your NavigationHelper class: call that extension method with your frame and then navigate back.
Or just put it inside your EventHandler:
private void backButton_Click(object sender, RoutedEventArgs e)
{
this.Frame.ResetBackStack();
this.Frame.GoBack();
}

As I know from the Windows Phone, you cannot remove backstack with a command or method. it is not such easy. You have to use loops till CanGoBack returns false. This is only way to do this.
I imagined your logic so, remove all pages in backstack except the first opened page. The loop continues up to exception.
Do not forget to define your first page name instead FirstPage.
Also please check the msdn link for more information.
https://social.msdn.microsoft.com/Forums/windowsapps/en-US/3819f389-3bfc-4c59-a919-272927fc9229/navigation-in-a-metro-application?forum=winappswithcsharp
Try this solution.
do
{
this.Frame.BackStack.RemoveAt(this.Frame.BackStackDepth - 1);
} while (Frame.CanGoBack && Frame.BackStack.Last(entry => entry.SourcePageType != typeof(FirstPage)) != null);
this.Frame.GoBack();

Related

Navigating to specific ContentFrame when refreshing NavigationView

I have an app which uses a NavigationView as a UI-control.
Now I have a SettingsPage where I can change the language of my UI between german and english.
I change the language with this code:
public static void German()
{
Log.Logger.Information("Language = German")
ApplicationLanguages.PrimaryLanguageOverride = "de-DE";
DataCollection.Current.LanguageChangedEvent.LanguageChanged();
}
The last line invokes an EventHandler which in turn invokes the following event on the MainPage.xaml.cs where the NavigationView is located.
public void ChangedLanguage(object source, EventHandlerBase e)
{
if (e.GetStatus())
{
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
Frame.Navigate(this.GetType());
ContentFrame.Navigate(typeof(SettingsPage));
}
}
As it's possible to see I just want to return the user to the SettingsPage after changing the language.
But I always return back to the initial HomePage that I use when starting the app.
The code I use for the NavigationView is derived from the official NavigationView sample provided by Microsoft.
Is there any possible way to do this? The only possible thing I can imagine is to set a flag after the first page loading and then always check if that flag is set. But then I have the problem that I still could only land at the SettingsPage because I would have to make it the else destination for the flag-check.
Would really appreciate a more dynamic way if thats even possible :/
Greeting,
Daniel
The NavigationView sample just is a simple code sample for your reference. You need to make some changes by your requirements.
I just want to return the user to the SettingsPage after changing the language. But I always return back to the initial HomePage that I use when starting the app.
I've used the code on the document to make a code sample for testing. There're two places will cause your question.
First, in the NavView_Loaded event handler, it will always sets the home page as the selected item. But when you change the language, you re-navigate to 'MainPage' and make 'ContentFrame' navigate to 'SettingsPage'. At this time, the On_Navigated event handler will be first called. Then, the NavView_Loaded event handler will be called. That's the reason why your app always will return to home page.
Second, event if the NavigationView's SelectedItem has been set. But the NavigationView's ItemInvoked event will not be fired. So, what you see actually is not home page, it's a blank Frame control. You could use SelectionChanged event instead of ItemInvoked event. The SelectionChanged event will be fired when you set a new value for the NavigationView's SelectedItem. See the following sample:
private void NavView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.IsSettingsSelected)
{
ContentFrame.Navigate(typeof(SettingsPage));
}
else
{
NavView_Navigate(args.SelectedItem as NavigationViewItem);
}
}
Then, let's back to your original question:
I can imagine is to set a flag after the first page loading and then always check if that flag is set. But then I have the problem that I still could only land at the SettingsPage because I would have to make it the else destination for the flag-check.
In my opinion, the flag will not affect you doing other things. You completely could define several flags. Please see my following code snippet for reference:
public static void German()
{
Log.Logger.Information("Language = German")
ApplicationLanguages.PrimaryLanguageOverride = "de-DE";
ApplicationData.Current.LocalSettings.Values["IsSwitchingLanguage"] = true;
DataCollection.Current.LanguageChangedEvent.LanguageChanged();
}
In your MainPage.xaml.cs:
public void ChangedLanguage(object source, EventHandlerBase e)
{
if (e.GetStatus())
{
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
Frame.Navigate(this.GetType());
}
}
In NavView_Loaded event handler, I made some changes:
private void NavView_Loaded(object sender, RoutedEventArgs e)
{
......
foreach (NavigationViewItemBase item in NavView.MenuItems)
{
var IsSwitchingLanguage = ApplicationData.Current.LocalSettings.Values["IsSwitchingLanguage"];
if (IsSwitchingLanguage != null)
{
if ((bool)IsSwitchingLanguage)
{
NavView.SelectedItem = NavView.SettingsItem as NavigationViewItem;
ApplicationData.Current.LocalSettings.Values["IsSwitchingLanguage"] = false;
break;
}
}
if (item is NavigationViewItem && item.Tag.ToString() == "home")
{
NavView.SelectedItem = item;
break;
}
}
......
}

When do items get reloaded in listview when navigating back using mvvm

I'm working on a UWP Windows 10 app. It contains a listview which redirects you to another page when an item is tapped I'm trying to get the last item to be re-selected when I navigate back to the previous page.
My listview's ItemSource is binded to an ObservableCollection and loads my items as expected but when I navigate back, it keeps the item selected but it's not visible.
I've read various articles:
Make ListView.ScrollIntoView Scroll the Item into the Center of the ListView
ListViewBase.ScrollIntoView methods
Windows 10 ScrollIntoView() is not scrolling to the items in the middle of a listview
And quite a few others, and I tried to use the extension from the last article mentioned above which provided various extensions to the Listview and I thought I'd be good to go but to no avail!
While investigating and trying various things out, I thought I'd make a call from
protected async override void OnNavigatedTo(NavigationEventArgs e)
and call the following code:
if (e.NavigationMode == NavigationMode.Back)
{
if (GetViewModel.SelectedChannel != null)
{
await this.lvwChannels.ScrollToIndex
(GetViewModel.SelectedIndex);
}
}
or
if (e.NavigationMode == NavigationMode.Back)
{
if (GetViewModel.SelectedChannel != null)
{
await this.lvwChannels.ScrollToItem
(GetViewModel.SelectedItem);
}
}
but it still not working.
This is when I noticed that my Listview.Items.count was returning 0 and yet the item are reloaded, but I assume they are being reloaded at a later stage so my question is:
but I noticed the following:
When stepping through the extension, the
public async static Task ScrollToIndex(this ListViewBase listViewBase, int index)
my listViewBase.Items.Count is 0?? Why?
When trying to find the ScrollViewer within the ListView, it doesn't find it?
When calling
var selectorItem = listViewBase.ContainerFromIndex(index) as SelectorItem;
selectorItem is null as it doesn't find anything based on the index. The index is the only thing that appears to be set correct i.e. 10, 23, 37
Can someone point me the right direction? I assume it's a timing issue and that my ObservableCollection hasn't been re-binded at this stage. I could re-bind at just before calling the ScrollIntoView, but that's still not going to resolve the fact that my listViewBase is null.
Any ideas?
Thanks.
It does seem to be a timing issue and while I'm not 100% sure it is the correct way to address the problem, it seem to have resolved it by calling the ScrollIntoView within the Loaded event:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
if (GetViewModel.SelectedChannel != null)
lvwChannels.ScrollIntoView(GetViewModel.SelectedChannel);
}
I guess I'm not breaking any MVVM rules either as it is dealing with the UI only at this stage.
When called from the Loaded event, both my ObservableCollection has been reloaded automatically since it is binded and the SelectedChannel is also set correctly and everything appears to be working as expected.
As mentioned in my reply below, by default, the selected items is now highlighted and displayed at the top of the list and I'd prefer if it was centered.
If there is a better way to do this, let me know but for now it will have to do.
Thanks
Where did you add your data to ObservableCollection? I'm not sure how you do this. By me, the following method works fine:
public sealed partial class MainPage : Page
{
ObservableCollection<listText> list = new ObservableCollection<listText>();
static int selectedcount = -1;
public MainPage()
{
this.InitializeComponent();
mylist.ItemsSource = list;
}
class listText
{
public string listtxt { get; set; }
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
list.Clear();
list.Add(new listText { listtxt = "Item1" });
list.Add(new listText { listtxt = "Item2" });
mylist.SelectedIndex = selectedcount;
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (mylist.SelectedIndex)
{
case 0:
selectedcount = 0;
this.Frame.Navigate(typeof(Item1));
break;
case 1:
selectedcount = 1;
this.Frame.Navigate(typeof(Item2));
break;
default:
selectedcount = 0;
this.Frame.Navigate(typeof(Item1));
break;
}
}
}
And the NavigateBack code I wrote in the App.xaml.cs like this:
private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
return;
// Navigate back if possible, and if the event has not
// already been handled .
if (rootFrame.CanGoBack && e.Handled == false)
{
e.Handled = true;
rootFrame.GoBack();
}
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
//#if DEBUG
// if (System.Diagnostics.Debugger.IsAttached)
// {
// this.DebugSettings.EnableFrameRateCounter = true;
// }
//#endif
Frame rootFrame = Window.Current.Content as Frame;
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += App_BackRequested;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
I add my data to the ObservableCollection in the OnNavigatedTo, this OnNavigatedTo will be called when it navi-back, so the count of my listview will not be 0.
Wish this helps you.

Navigation Source in Windows Phone

I have 2 pages in my application, A and B.
If I'm navigation from the outside of the application to A, I want to display a message box. If I'm navigation from B to A, I don't want to display anything.
Is there any way to identify in A the page which initiated navigation? i.e in A.Loaded (or any other event) I need something like
if(pageFromWhichIAmComingFrom == B)
OnNavigatedTo, OnNavigationFrom and OnNavigatedFrom don't seem to help me.
You could use the PhoneApplicationService class to store information about what page you were on last. For example, use OnNavigatedFrom on Page A:
void OnNavigatedFrom(object sender, Eventargs e)
{
PhoneApplicationService.Current.State["LastPage"] = "PageA";
}
And then check for that on the next page:
void OnNavigatedTo(object sender, Eventargs e)
{
if(PhoneApplicationService.Current.State["LastPage"].ToString() == "PageA")
{
// came from page A
}
else
{
// came from a different page
}
}
Hope this helps!
UPDATE:
One more thing I just saw that might be worth trying is using the NavigationService.BackStack property. I haven't tried this, but it seems like it should work. In your OnNavigatedTo event handler, you should be able to get the last entry from the stack to see your last page. This would be simpler and wouldn't require you to set any properties manually. Example:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var lastPage = NavigationService.BackStack.FirstOrDefault();
}
Found here.

Show other view at application first launch

I have inherited some Windows Phone code that I must change. I need to show the user a tutorial about how to use the application when he first launches it. However, I cannot manage to change the current view...
Here is my code:
public LoginView()
{
InitializeComponent();
this.DataContext = new LoginViewModel();
if (ApplicationFirstLaunched() == true)
{
NavigationManager.Current.Navigate(ApplicationView.DemoView);
}
}
The ApplicationFirstLaunched function works fine (I use IsolatedStorageSettings to store a boolean value), but the view never changes.. I thought that maybe the Navigate call was wrong so I created a button in my view and assigned its Click property to this function:
private void demoBtn_Click(object sender, RoutedEventArgs e)
{
NavigationManager.Current.Navigate(ApplicationView.DemoView);
}
When I click the button, the view changes and the tutorial pops up. What to do to show another view at first launch? Thanks
Navigate in the OnNavigatedTo method.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (ApplicationFirstLaunched() == true)
{
NavigationManager.Current.Navigate(ApplicationView.DemoView);
}
}

Triggering a "Click" event without user input

I have a Windows Forms Link Label, "Refresh", that refreshes the display.
In another part of my code, part of a separate windows form, I have a dialog that changes the data loaded into the display in the first place. After executing this other code, pressing "Refresh" updates the data correctly.
Is there a simple way for the dialog menu to "click" the "refresh" Link Label after it has finished altering the data?
Using Visual Studio 2008.
For button is really simple, just use:
button.PerformClick()
Anyway, I'd prefer to do something like:
private void button_Click(object sender, EventArgs e)
{
DoRefresh();
}
public void DoRefresh()
{
// refreshing code
}
and call DoRefresh() instead of PerformClick()
EDIT (according to OP changes):
You can still use my second solution, that is far preferable:
private void linkLabel_Click(object sender, EventArgs e)
{
DoRefresh();
}
public void DoRefresh()
{
// refreshing code
}
And from outside the form, you can call DoRefresh() as it is marked public.
But, if you really need to programmatically generate a click, just look at Yuriy-Faktorovich's Answer
You could call the PerformClick method. But Generally it is better to have the Click event of the button call a Refresh method you write. And the menu call that method as well. Otherwise your menu depends on the button being there.
Edit:
A LinkLabel implements the IButtonControl explicitly. So you could use:
((IButtonControl)button).PerformClick();
you can use a method to refrech display, the bouton_click and the dialogBox call this method
public void refrechDate()
{
}
private void button_click(...)
{
refrechData();
}
private void MyMethod()
{
// ...
// calling refresh
this.button1_Click(this.button1, EventArgs.Empty);
// ...
}
private void button1_Click(object sender, EventArgs e)
{
// refresh code
}

Categories