using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Navigation; using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Common; using ModernKeePass.Domain.Interfaces; namespace ModernKeePass.Views.BasePages { public class LayoutAwarePageBase: Page { /// /// NavigationHelper is used on each page to aid in navigation and /// process lifetime management /// public NavigationHelper NavigationHelper { get; } public ListView ListView { get; set; } public virtual CollectionViewSource ListViewSource { get; set; } public virtual IHasSelectableObject Model { get; set; } public LayoutAwarePageBase() { // Setup the navigation helper NavigationHelper = new NavigationHelper(this); NavigationHelper.LoadState += navigationHelper_LoadState; NavigationHelper.SaveState += navigationHelper_SaveState; // Setup the logical page navigation components that allow // the page to only show one pane at a time. NavigationHelper.GoBackCommand = new RelayCommand(GoBack, CanGoBack); // Start listening for Window size changes // to change from showing two panes to showing a single pane Window.Current.SizeChanged += Window_SizeChanged; InvalidateVisualState(); } protected void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { // Invalidate the view state when logical page navigation is in effect, as a change // in selection may cause a corresponding change in the current logical page. When // an item is selected this has the effect of changing from displaying the item list // to showing the selected item's details. When the selection is cleared this has the // opposite effect. if (!UsingLogicalPageNavigation()) return; NavigationHelper.GoBackCommand.RaiseCanExecuteChanged(); InvalidateVisualState(); } /// /// Populates the page with content passed during navigation. Any saved state is also /// provided when recreating a page from a prior session. /// /// /// The source of the event; typically /// /// Event data that provides both the navigation parameter passed to /// when this page was initially requested and /// a dictionary of state preserved by this page during an earlier /// session. The state will be null the first time a page is visited. protected void navigationHelper_LoadState(object sender, LoadStateEventArgs e) { if (e.PageState == null) { // When this is a new page, select the first item automatically unless logical page // navigation is being used (see the logical page navigation #region below.) if (!UsingLogicalPageNavigation()) { ListViewSource.View?.MoveCurrentToFirst(); } } else { // Restore the previously saved state associated with this page if (e.PageState.ContainsKey("SelectedItem")) { ListViewSource.View?.MoveCurrentTo(e.PageState["SelectedItem"]); } } } /// /// Preserves state associated with this page in case the application is suspended or the /// page is discarded from the navigation cache. Values must conform to the serialization /// requirements of . /// /// The source of the event; typically /// Event data that provides an empty dictionary to be populated with /// serializable state. protected void navigationHelper_SaveState(object sender, SaveStateEventArgs e) { if (ListViewSource.View != null) { e.PageState["SelectedItem"] = Model?.SelectedItem; } } #region Logical page navigation // The split page is designed so that when the Window does have enough space to show // both the list and the details, only one pane will be shown at at time. // // This is all implemented with a single physical page that can represent two logical // pages. The code below achieves this goal without making the user aware of the // distinction. protected const int MinimumWidthForSupportingTwoPanes = 768; /// /// Invoked to determine whether the page should act as one logical page or two. /// /// True if the window should show act as one logical page, false /// otherwise. protected bool UsingLogicalPageNavigation() { return Window.Current.Bounds.Width < MinimumWidthForSupportingTwoPanes; } /// /// Invoked with the Window changes size /// /// The current Window /// Event data that describes the new size of the Window protected void Window_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e) { InvalidateVisualState(); } protected bool CanGoBack() { if (UsingLogicalPageNavigation() && ListView.SelectedItem != null) { return true; } return NavigationHelper.CanGoBack(); } protected void GoBack() { if (UsingLogicalPageNavigation() && ListView.SelectedItem != null) { // When logical page navigation is in effect and there's a selected item that // item's details are currently displayed. Clearing the selection will return to // the item list. From the user's point of view this is a logical backward // navigation. ListView.SelectedItem = null; } else { NavigationHelper.GoBack(); } } protected void InvalidateVisualState() { var visualState = DetermineVisualState(); VisualStateManager.GoToState(this, visualState, false); NavigationHelper.GoBackCommand.RaiseCanExecuteChanged(); } /// /// Invoked to determine the name of the visual state that corresponds to an application /// view state. /// /// The name of the desired visual state. This is the same as the name of the /// view state except when there is a selected item in portrait and snapped views where /// this additional logical page is represented by adding a suffix of _Detail. protected string DetermineVisualState() { if (!UsingLogicalPageNavigation()) return "PrimaryView"; // Update the back button's enabled state when the view state changes var logicalPageBack = UsingLogicalPageNavigation() && ListView?.SelectedItem != null; return logicalPageBack ? "SinglePane_Detail" : "SinglePane"; } #endregion #region NavigationHelper registration /// The methods provided in this section are simply used to allow /// NavigationHelper to respond to the page's navigation methods. /// /// Page specific logic should be placed in event handlers for the /// /// and . /// The navigation parameter is available in the LoadState method /// in addition to page state preserved during an earlier session. protected override void OnNavigatedTo(NavigationEventArgs e) { NavigationHelper.OnNavigatedTo(e); } protected override void OnNavigatedFrom(NavigationEventArgs e) { NavigationHelper.OnNavigatedFrom(e); } #endregion } }