Gary Kindel
Gary Kindel

Reputation: 17699

How to find WPF Page in navigation history to prevent opening the page again

How do I determine if a WPF page is still in the navigation history of WPF frame and then navigate back to it?

Summary:

My WPF application uses a single MainWindow containing two Frames one for menu layout and the other for display content. I am using MVVM pattern with content displayed as WPF pages as views with as little code as possible behind each view/page.

The content frame (shown in red) has a NavigationUI visible. enter image description here

XAML:

enter image description here

Here is typical code to create WPF page and display it in the main window from a static helper class:

public static void ShowPeriodicTable()
{
  var page = new Views.PeriodicTable();
  (Application.Current.MainWindow as MainWindow).ContentArea.Navigate(page);
}

This code will load the same page over and over even if it is already loaded.

I've made the following change to detect if the current page displayed in the MainWindow.ContentArea (frame) before creating and navigating to the page.

public static void ShowPeriodicTable()
    {                   
        var currentPage = ((DRC_SQLITE.MainWindow)Application.Current.MainWindow).ContentArea.Content;
        if (currentPage == null || (currentPage != null && currentPage.GetType().Name != "PeriodicTable"))
        {
            var page = new Views.PeriodicTable();
            (Application.Current.MainWindow as MainWindow).ContentArea.Navigate(page);
        }
    }

Question:

How do detect if the page that is about to be opened exists in the navigation history but is not the current page in the frame. In the image below, the settingPage was opened twice. This is the scenario I want to eliminate to better manage memory usage of the application.

enter image description here

Upvotes: 1

Views: 2022

Answers (1)

Andy
Andy

Reputation: 12276

Pages are rarely used in commercial apps. As I understand it, they were really intended for xbap ( wpf in a browser ). It's much more usual to have usercontrols hosted in a contentcontrol. In which case there are two commonly used alternatives

1) View First. Hold a dictionary of usercontrols keyed by type. https://gallery.technet.microsoft.com/WPF-Navigation-Basic-Sample-11f10c74

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Navigate(typeof(HomeView));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)e.OriginalSource;
            Navigate(btn.Tag as Type);
        }

        private void Navigate (Type viewType)
        {
            UserControl uc;
            if (Views.ContainsKey(viewType))
            {
                uc = Views[viewType];
            }
            else
            {
                uc = (UserControl)Activator.CreateInstance(viewType);
                Views[viewType] = uc;
            }
            NavigationParent.Content = uc;
        }
        private Dictionary<Type, UserControl> Views = new Dictionary<Type, UserControl>();
    }

Proponents of this point out that navigation is arguably a view responsibility.

2) Viewmodel first.

Bind the content of a contentcontrol to a property in your mainwindowviewmodel. Switch this out to a viewmodel per view and template that into a usercontrol. You then control retaining state by retaining a reference to a viewmodel for each view.

If you really want to stick with what you have then Frame.BackStack is the collection of entries in the journal. You could iterate that and check type of each object. I think it's actually a reference to a page. I have never seen this approach used in a commercial app.

Upvotes: 1

Related Questions