362 строки
14 KiB
C#
362 строки
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Windows.Foundation;
|
|
using Windows.UI.Core;
|
|
using Windows.UI.Xaml;
|
|
using Windows.UI.Xaml.Automation;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Windows.UI.Xaml.Input;
|
|
using Windows.UI.Xaml.Navigation;
|
|
using ClientApp.Controls;
|
|
using ClientApp.Views;
|
|
|
|
namespace ClientApp
|
|
{
|
|
/// <summary>
|
|
/// The "chrome" layer of the app that provides top-level navigation with
|
|
/// proper keyboarding navigation.
|
|
/// </summary>
|
|
public sealed partial class AppShell : Page
|
|
{
|
|
private bool isPaddingAdded = false;
|
|
// Declare the top level nav items
|
|
private List<NavMenuItem> navlist = new List<NavMenuItem>(
|
|
new[]
|
|
{
|
|
new NavMenuItem()
|
|
{
|
|
Symbol = Symbol.Permissions,
|
|
Label = "Sign In",
|
|
DestPage = typeof(SignInPage)
|
|
},
|
|
new NavMenuItem()
|
|
{
|
|
Symbol = Symbol.People,
|
|
Label = "My Team",
|
|
DestPage = typeof(TeamPage)
|
|
},
|
|
});
|
|
|
|
public static AppShell Current = null;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the AppShell, sets the static 'Current' reference,
|
|
/// adds callbacks for Back requests and changes in the SplitView's DisplayMode, and
|
|
/// provide the nav menu list with the data to display.
|
|
/// </summary>
|
|
public AppShell()
|
|
{
|
|
this.InitializeComponent();
|
|
|
|
this.Loaded += (sender, args) =>
|
|
{
|
|
Current = this;
|
|
|
|
this.CheckTogglePaneButtonSizeChanged();
|
|
|
|
var titleBar = Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().TitleBar;
|
|
titleBar.IsVisibleChanged += TitleBar_IsVisibleChanged;
|
|
};
|
|
|
|
this.RootSplitView.RegisterPropertyChangedCallback(
|
|
SplitView.DisplayModeProperty,
|
|
(s, a) =>
|
|
{
|
|
// Ensure that we update the reported size of the TogglePaneButton when the SplitView's
|
|
// DisplayMode changes.
|
|
this.CheckTogglePaneButtonSizeChanged();
|
|
});
|
|
|
|
SystemNavigationManager.GetForCurrentView().BackRequested += SystemNavigationManager_BackRequested;
|
|
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
|
|
|
|
|
|
NavMenuList.ItemsSource = navlist;
|
|
}
|
|
|
|
public Frame AppFrame { get { return this.frame; } }
|
|
|
|
public string UserId { get; set; }
|
|
|
|
public bool IsSigned { get; set; }
|
|
|
|
/// <summary>
|
|
/// Invoked when window title bar visibility changes, such as after loading or in tablet mode
|
|
/// Ensures correct padding at window top, between title bar and app content
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="args"></param>
|
|
private void TitleBar_IsVisibleChanged(Windows.ApplicationModel.Core.CoreApplicationViewTitleBar sender, object args)
|
|
{
|
|
if (!this.isPaddingAdded && sender.IsVisible)
|
|
{
|
|
//add extra padding between window title bar and app content
|
|
double extraPadding = (Double)App.Current.Resources["DesktopWindowTopPadding"];
|
|
this.isPaddingAdded = true;
|
|
|
|
Thickness margin = NavMenuList.Margin;
|
|
NavMenuList.Margin = new Thickness(margin.Left, margin.Top + extraPadding, margin.Right, margin.Bottom);
|
|
margin = AppFrame.Margin;
|
|
AppFrame.Margin = new Thickness(margin.Left, margin.Top + extraPadding, margin.Right, margin.Bottom);
|
|
margin = TogglePaneButton.Margin;
|
|
TogglePaneButton.Margin = new Thickness(margin.Left, margin.Top + extraPadding, margin.Right, margin.Bottom);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default keyboard focus movement for any unhandled keyboarding
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void AppShell_KeyDown(object sender, KeyRoutedEventArgs e)
|
|
{
|
|
FocusNavigationDirection direction = FocusNavigationDirection.None;
|
|
switch (e.Key)
|
|
{
|
|
case Windows.System.VirtualKey.Left:
|
|
case Windows.System.VirtualKey.GamepadDPadLeft:
|
|
case Windows.System.VirtualKey.GamepadLeftThumbstickLeft:
|
|
case Windows.System.VirtualKey.NavigationLeft:
|
|
direction = FocusNavigationDirection.Left;
|
|
break;
|
|
case Windows.System.VirtualKey.Right:
|
|
case Windows.System.VirtualKey.GamepadDPadRight:
|
|
case Windows.System.VirtualKey.GamepadLeftThumbstickRight:
|
|
case Windows.System.VirtualKey.NavigationRight:
|
|
direction = FocusNavigationDirection.Right;
|
|
break;
|
|
|
|
case Windows.System.VirtualKey.Up:
|
|
case Windows.System.VirtualKey.GamepadDPadUp:
|
|
case Windows.System.VirtualKey.GamepadLeftThumbstickUp:
|
|
case Windows.System.VirtualKey.NavigationUp:
|
|
direction = FocusNavigationDirection.Up;
|
|
break;
|
|
|
|
case Windows.System.VirtualKey.Down:
|
|
case Windows.System.VirtualKey.GamepadDPadDown:
|
|
case Windows.System.VirtualKey.GamepadLeftThumbstickDown:
|
|
case Windows.System.VirtualKey.NavigationDown:
|
|
direction = FocusNavigationDirection.Down;
|
|
break;
|
|
}
|
|
|
|
if (direction != FocusNavigationDirection.None)
|
|
{
|
|
var control = FocusManager.FindNextFocusableElement(direction) as Control;
|
|
if (control != null)
|
|
{
|
|
control.Focus(FocusState.Keyboard);
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region BackRequested Handlers
|
|
|
|
private void SystemNavigationManager_BackRequested(object sender, BackRequestedEventArgs e)
|
|
{
|
|
bool handled = e.Handled;
|
|
this.BackRequested(ref handled);
|
|
e.Handled = handled;
|
|
}
|
|
|
|
private void BackRequested(ref bool handled)
|
|
{
|
|
// Get a hold of the current frame so that we can inspect the app back stack.
|
|
|
|
if (this.AppFrame == null)
|
|
return;
|
|
|
|
// Check to see if this is the top-most page on the app back stack.
|
|
if (this.AppFrame.CanGoBack && !handled)
|
|
{
|
|
// If not, set the event to handled and go back to the previous page in the app.
|
|
handled = true;
|
|
this.AppFrame.GoBack();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Navigation
|
|
|
|
/// <summary>
|
|
/// Navigate to the Page for the selected <paramref name="listViewItem"/>.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="listViewItem"></param>
|
|
private void NavMenuList_ItemInvoked(object sender, ListViewItem listViewItem)
|
|
{
|
|
foreach (var i in navlist)
|
|
{
|
|
i.IsSelected = false;
|
|
}
|
|
|
|
var item = (NavMenuItem)((NavMenuListView)sender).ItemFromContainer(listViewItem);
|
|
|
|
if (item != null)
|
|
{
|
|
item.IsSelected = true;
|
|
if (item.DestPage != null &&
|
|
item.DestPage != this.AppFrame.CurrentSourcePageType)
|
|
{
|
|
this.AppFrame.Navigate(item.DestPage, item.Arguments);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures the nav menu reflects reality when navigation is triggered outside of
|
|
/// the nav menu buttons.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void OnNavigatingToPage(object sender, NavigatingCancelEventArgs e)
|
|
{
|
|
if (e.NavigationMode == NavigationMode.Back)
|
|
{
|
|
var item = (from p in this.navlist where p.DestPage == e.SourcePageType select p).SingleOrDefault();
|
|
if (item == null && this.AppFrame.BackStackDepth > 0)
|
|
{
|
|
// In cases where a page drills into sub-pages then we'll highlight the most recent
|
|
// navigation menu item that appears in the BackStack
|
|
foreach (var entry in this.AppFrame.BackStack.Reverse())
|
|
{
|
|
item = (from p in this.navlist where p.DestPage == entry.SourcePageType select p).SingleOrDefault();
|
|
if (item != null)
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (var i in navlist)
|
|
{
|
|
i.IsSelected = false;
|
|
}
|
|
if (item != null)
|
|
{
|
|
item.IsSelected = true;
|
|
}
|
|
|
|
var container = (ListViewItem)NavMenuList.ContainerFromItem(item);
|
|
|
|
// While updating the selection state of the item prevent it from taking keyboard focus. If a
|
|
// user is invoking the back button via the keyboard causing the selected nav menu item to change
|
|
// then focus will remain on the back button.
|
|
if (container != null) container.IsTabStop = false;
|
|
NavMenuList.SetSelectedItem(container);
|
|
if (container != null) container.IsTabStop = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public Rect TogglePaneButtonRect
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// An event to notify listeners when the hamburger button may occlude other content in the app.
|
|
/// The custom "PageHeader" user control is using this.
|
|
/// </summary>
|
|
public event TypedEventHandler<AppShell, Rect> TogglePaneButtonRectChanged;
|
|
|
|
/// <summary>
|
|
/// Public method to allow pages to open SplitView's pane.
|
|
/// Used for custom app shortcuts like navigating left from page's left-most item
|
|
/// </summary>
|
|
public void OpenNavePane()
|
|
{
|
|
TogglePaneButton.IsChecked = true;
|
|
NavPaneDivider.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hides divider when nav pane is closed.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="args"></param>
|
|
private void RootSplitView_PaneClosed(SplitView sender, object args)
|
|
{
|
|
NavPaneDivider.Visibility = Visibility.Collapsed;
|
|
|
|
// Prevent focus from moving to elements when they're not visible on screen
|
|
FeedbackNavPaneButton.IsTabStop = false;
|
|
SettingsNavPaneButton.IsTabStop = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback when the SplitView's Pane is toggled closed. When the Pane is not visible
|
|
/// then the floating hamburger may be occluding other content in the app unless it is aware.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void TogglePaneButton_Unchecked(object sender, RoutedEventArgs e)
|
|
{
|
|
this.CheckTogglePaneButtonSizeChanged();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback when the SplitView's Pane is toggled opened.
|
|
/// Restores divider's visibility and ensures that margins around the floating hamburger are correctly set.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void TogglePaneButton_Checked(object sender, RoutedEventArgs e)
|
|
{
|
|
NavPaneDivider.Visibility = Visibility.Visible;
|
|
this.CheckTogglePaneButtonSizeChanged();
|
|
|
|
FeedbackNavPaneButton.IsTabStop = true;
|
|
SettingsNavPaneButton.IsTabStop = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check for the conditions where the navigation pane does not occupy the space under the floating
|
|
/// hamburger button and trigger the event.
|
|
/// </summary>
|
|
private void CheckTogglePaneButtonSizeChanged()
|
|
{
|
|
if (this.RootSplitView.DisplayMode == SplitViewDisplayMode.Inline ||
|
|
this.RootSplitView.DisplayMode == SplitViewDisplayMode.Overlay)
|
|
{
|
|
var transform = this.TogglePaneButton.TransformToVisual(this);
|
|
var rect = transform.TransformBounds(new Rect(0, 0, this.TogglePaneButton.ActualWidth, this.TogglePaneButton.ActualHeight));
|
|
this.TogglePaneButtonRect = rect;
|
|
}
|
|
else
|
|
{
|
|
this.TogglePaneButtonRect = new Rect();
|
|
}
|
|
|
|
var handler = this.TogglePaneButtonRectChanged;
|
|
if (handler != null)
|
|
{
|
|
// handler(this, this.TogglePaneButtonRect);
|
|
handler.DynamicInvoke(this, this.TogglePaneButtonRect);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable accessibility on each nav menu item by setting the AutomationProperties.Name on each container
|
|
/// using the associated Label of each item.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="args"></param>
|
|
private void NavMenuItemContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
|
|
{
|
|
if (!args.InRecycleQueue && args.Item != null && args.Item is NavMenuItem)
|
|
{
|
|
args.ItemContainer.SetValue(AutomationProperties.NameProperty, ((NavMenuItem)args.Item).Label);
|
|
}
|
|
else
|
|
{
|
|
args.ItemContainer.ClearValue(AutomationProperties.NameProperty);
|
|
}
|
|
}
|
|
}
|
|
}
|