Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using Windows.Foundation;
- using Windows.Foundation.Collections;
- using Windows.System;
- using Windows.UI.Core;
- using Windows.UI.ViewManagement;
- using Windows.UI.Xaml;
- using Windows.UI.Xaml.Controls;
- using Windows.UI.Xaml.Navigation;
- namespace Accountable.Common
- {
- /// <summary>
- /// Typical implementation of Page that provides several important conveniences:
- /// <list type="bullet">
- /// <item>
- /// <description>Application view state to visual state mapping</description>
- /// </item>
- /// <item>
- /// <description>GoBack, GoForward, and GoHome event handlers</description>
- /// </item>
- /// <item>
- /// <description>Mouse and keyboard shortcuts for navigation</description>
- /// </item>
- /// <item>
- /// <description>State management for navigation and process lifetime management</description>
- /// </item>
- /// <item>
- /// <description>A default view model</description>
- /// </item>
- /// </list>
- /// </summary>
- [Windows.Foundation.Metadata.WebHostHidden]
- public class LayoutAwarePage : Page
- {
- /// <summary>
- /// Identifies the <see cref="DefaultViewModel"/> dependency property.
- /// </summary>
- public static readonly DependencyProperty DefaultViewModelProperty =
- DependencyProperty.Register("DefaultViewModel", typeof(IObservableMap<String, Object>),
- typeof(LayoutAwarePage), null);
- private List<Control> _layoutAwareControls;
- /// <summary>
- /// Initializes a new instance of the <see cref="LayoutAwarePage"/> class.
- /// </summary>
- public LayoutAwarePage()
- {
- if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) return;
- // Create an empty default view model
- this.DefaultViewModel = new ObservableDictionary<String, Object>();
- // When this page is part of the visual tree make two changes:
- // 1) Map application view state to visual state for the page
- // 2) Handle keyboard and mouse navigation requests
- this.Loaded += (sender, e) =>
- {
- this.StartLayoutUpdates(sender, e);
- // Keyboard and mouse navigation only apply when occupying the entire window
- if (this.ActualHeight == Window.Current.Bounds.Height &&
- this.ActualWidth == Window.Current.Bounds.Width)
- {
- // Listen to the window directly so focus isn't required
- Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated +=
- CoreDispatcher_AcceleratorKeyActivated;
- Window.Current.CoreWindow.PointerPressed +=
- this.CoreWindow_PointerPressed;
- }
- };
- // Undo the same changes when the page is no longer visible
- this.Unloaded += (sender, e) =>
- {
- this.StopLayoutUpdates(sender, e);
- Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated -=
- CoreDispatcher_AcceleratorKeyActivated;
- Window.Current.CoreWindow.PointerPressed -=
- this.CoreWindow_PointerPressed;
- };
- }
- /// <summary>
- /// An implementation of <see cref="IObservableMap<String, Object>"/> designed to be
- /// used as a trivial view model.
- /// </summary>
- protected IObservableMap<String, Object> DefaultViewModel
- {
- get
- {
- return this.GetValue(DefaultViewModelProperty) as IObservableMap<String, Object>;
- }
- set
- {
- this.SetValue(DefaultViewModelProperty, value);
- }
- }
- #region Navigation support
- /// <summary>
- /// Invoked as an event handler to navigate backward in the page's associated
- /// <see cref="Frame"/> until it reaches the top of the navigation stack.
- /// </summary>
- /// <param name="sender">Instance that triggered the event.</param>
- /// <param name="e">Event data describing the conditions that led to the event.</param>
- protected virtual void GoHome(object sender, RoutedEventArgs e)
- {
- // Use the navigation frame to return to the topmost page
- if (this.Frame != null)
- {
- while (this.Frame.CanGoBack) this.Frame.GoBack();
- }
- }
- /// <summary>
- /// Invoked as an event handler to navigate backward in the navigation stack
- /// associated with this page's <see cref="Frame"/>.
- /// </summary>
- /// <param name="sender">Instance that triggered the event.</param>
- /// <param name="e">Event data describing the conditions that led to the
- /// event.</param>
- protected virtual void GoBack(object sender, RoutedEventArgs e)
- {
- // Use the navigation frame to return to the previous page
- if (this.Frame != null && this.Frame.CanGoBack) this.Frame.GoBack();
- }
- /// <summary>
- /// Invoked as an event handler to navigate forward in the navigation stack
- /// associated with this page's <see cref="Frame"/>.
- /// </summary>
- /// <param name="sender">Instance that triggered the event.</param>
- /// <param name="e">Event data describing the conditions that led to the
- /// event.</param>
- protected virtual void GoForward(object sender, RoutedEventArgs e)
- {
- // Use the navigation frame to move to the next page
- if (this.Frame != null && this.Frame.CanGoForward) this.Frame.GoForward();
- }
- /// <summary>
- /// Invoked on every keystroke, including system keys such as Alt key combinations, when
- /// this page is active and occupies the entire window. Used to detect keyboard navigation
- /// between pages even when the page itself doesn't have focus.
- /// </summary>
- /// <param name="sender">Instance that triggered the event.</param>
- /// <param name="args">Event data describing the conditions that led to the event.</param>
- private void CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher sender,
- AcceleratorKeyEventArgs args)
- {
- var virtualKey = args.VirtualKey;
- // Only investigate further when Left, Right, or the dedicated Previous or Next keys
- // are pressed
- if ((args.EventType == CoreAcceleratorKeyEventType.SystemKeyDown ||
- args.EventType == CoreAcceleratorKeyEventType.KeyDown) &&
- (virtualKey == VirtualKey.Left || virtualKey == VirtualKey.Right ||
- (int)virtualKey == 166 || (int)virtualKey == 167))
- {
- var coreWindow = Window.Current.CoreWindow;
- var downState = CoreVirtualKeyStates.Down;
- bool menuKey = (coreWindow.GetKeyState(VirtualKey.Menu) & downState) == downState;
- bool controlKey = (coreWindow.GetKeyState(VirtualKey.Control) & downState) == downState;
- bool shiftKey = (coreWindow.GetKeyState(VirtualKey.Shift) & downState) == downState;
- bool noModifiers = !menuKey && !controlKey && !shiftKey;
- bool onlyAlt = menuKey && !controlKey && !shiftKey;
- if (((int)virtualKey == 166 && noModifiers) ||
- (virtualKey == VirtualKey.Left && onlyAlt))
- {
- // When the previous key or Alt+Left are pressed navigate back
- args.Handled = true;
- this.GoBack(this, new RoutedEventArgs());
- }
- else if (((int)virtualKey == 167 && noModifiers) ||
- (virtualKey == VirtualKey.Right && onlyAlt))
- {
- // When the next key or Alt+Right are pressed navigate forward
- args.Handled = true;
- this.GoForward(this, new RoutedEventArgs());
- }
- }
- }
- /// <summary>
- /// Invoked on every mouse click, touch screen tap, or equivalent interaction when this
- /// page is active and occupies the entire window. Used to detect browser-style next and
- /// previous mouse button clicks to navigate between pages.
- /// </summary>
- /// <param name="sender">Instance that triggered the event.</param>
- /// <param name="args">Event data describing the conditions that led to the event.</param>
- private void CoreWindow_PointerPressed(CoreWindow sender,
- PointerEventArgs args)
- {
- var properties = args.CurrentPoint.Properties;
- // Ignore button chords with the left, right, and middle buttons
- if (properties.IsLeftButtonPressed || properties.IsRightButtonPressed ||
- properties.IsMiddleButtonPressed) return;
- // If back or foward are pressed (but not both) navigate appropriately
- bool backPressed = properties.IsXButton1Pressed;
- bool forwardPressed = properties.IsXButton2Pressed;
- if (backPressed ^ forwardPressed)
- {
- args.Handled = true;
- if (backPressed) this.GoBack(this, new RoutedEventArgs());
- if (forwardPressed) this.GoForward(this, new RoutedEventArgs());
- }
- }
- #endregion
- #region Visual state switching
- /// <summary>
- /// Invoked as an event handler, typically on the <see cref="FrameworkElement.Loaded"/>
- /// event of a <see cref="Control"/> within the page, to indicate that the sender should
- /// start receiving visual state management changes that correspond to application view
- /// state changes.
- /// </summary>
- /// <param name="sender">Instance of <see cref="Control"/> that supports visual state
- /// management corresponding to view states.</param>
- /// <param name="e">Event data that describes how the request was made.</param>
- /// <remarks>The current view state will immediately be used to set the corresponding
- /// visual state when layout updates are requested. A corresponding
- /// <see cref="FrameworkElement.Unloaded"/> event handler connected to
- /// <see cref="StopLayoutUpdates"/> is strongly encouraged. Instances of
- /// <see cref="LayoutAwarePage"/> automatically invoke these handlers in their Loaded and
- /// Unloaded events.</remarks>
- /// <seealso cref="DetermineVisualState"/>
- /// <seealso cref="InvalidateVisualState"/>
- public void StartLayoutUpdates(object sender, RoutedEventArgs e)
- {
- var control = sender as Control;
- if (control == null) return;
- if (this._layoutAwareControls == null)
- {
- // Start listening to view state changes when there are controls interested in updates
- Window.Current.SizeChanged += this.WindowSizeChanged;
- this._layoutAwareControls = new List<Control>();
- }
- this._layoutAwareControls.Add(control);
- // Set the initial visual state of the control
- VisualStateManager.GoToState(control, DetermineVisualState(ApplicationView.Value), false);
- }
- private void WindowSizeChanged(object sender, WindowSizeChangedEventArgs e)
- {
- this.InvalidateVisualState();
- }
- /// <summary>
- /// Invoked as an event handler, typically on the <see cref="FrameworkElement.Unloaded"/>
- /// event of a <see cref="Control"/>, to indicate that the sender should start receiving
- /// visual state management changes that correspond to application view state changes.
- /// </summary>
- /// <param name="sender">Instance of <see cref="Control"/> that supports visual state
- /// management corresponding to view states.</param>
- /// <param name="e">Event data that describes how the request was made.</param>
- /// <remarks>The current view state will immediately be used to set the corresponding
- /// visual state when layout updates are requested.</remarks>
- /// <seealso cref="StartLayoutUpdates"/>
- public void StopLayoutUpdates(object sender, RoutedEventArgs e)
- {
- var control = sender as Control;
- if (control == null || this._layoutAwareControls == null) return;
- this._layoutAwareControls.Remove(control);
- if (this._layoutAwareControls.Count == 0)
- {
- // Stop listening to view state changes when no controls are interested in updates
- this._layoutAwareControls = null;
- Window.Current.SizeChanged -= this.WindowSizeChanged;
- }
- }
- /// <summary>
- /// Translates <see cref="ApplicationViewState"/> values into strings for visual state
- /// management within the page. The default implementation uses the names of enum values.
- /// Subclasses may override this method to control the mapping scheme used.
- /// </summary>
- /// <param name="viewState">View state for which a visual state is desired.</param>
- /// <returns>Visual state name used to drive the
- /// <see cref="VisualStateManager"/></returns>
- /// <seealso cref="InvalidateVisualState"/>
- protected virtual string DetermineVisualState(ApplicationViewState viewState)
- {
- return viewState.ToString();
- }
- /// <summary>
- /// Updates all controls that are listening for visual state changes with the correct
- /// visual state.
- /// </summary>
- /// <remarks>
- /// Typically used in conjunction with overriding <see cref="DetermineVisualState"/> to
- /// signal that a different value may be returned even though the view state has not
- /// changed.
- /// </remarks>
- public void InvalidateVisualState()
- {
- if (this._layoutAwareControls != null)
- {
- string visualState = DetermineVisualState(ApplicationView.Value);
- foreach (var layoutAwareControl in this._layoutAwareControls)
- {
- VisualStateManager.GoToState(layoutAwareControl, visualState, false);
- }
- }
- }
- #endregion
- #region Process lifetime management
- private String _pageKey;
- /// <summary>
- /// Invoked when this page is about to be displayed in a Frame.
- /// </summary>
- /// <param name="e">Event data that describes how this page was reached. The Parameter
- /// property provides the group to be displayed.</param>
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- // Returning to a cached page through navigation shouldn't trigger state loading
- if (this._pageKey != null) return;
- var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
- this._pageKey = "Page-" + this.Frame.BackStackDepth;
- if (e.NavigationMode == NavigationMode.New)
- {
- // Clear existing state for forward navigation when adding a new page to the
- // navigation stack
- var nextPageKey = this._pageKey;
- int nextPageIndex = this.Frame.BackStackDepth;
- while (frameState.Remove(nextPageKey))
- {
- nextPageIndex++;
- nextPageKey = "Page-" + nextPageIndex;
- }
- // Pass the navigation parameter to the new page
- this.LoadState(e.Parameter, null);
- }
- else
- {
- // Pass the navigation parameter and preserved page state to the page, using
- // the same strategy for loading suspended state and recreating pages discarded
- // from cache
- this.LoadState(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey]);
- }
- }
- /// <summary>
- /// Invoked when this page will no longer be displayed in a Frame.
- /// </summary>
- /// <param name="e">Event data that describes how this page was reached. The Parameter
- /// property provides the group to be displayed.</param>
- protected override void OnNavigatedFrom(NavigationEventArgs e)
- {
- var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
- var pageState = new Dictionary<String, Object>();
- this.SaveState(pageState);
- frameState[_pageKey] = pageState;
- }
- /// <summary>
- /// Populates the page with content passed during navigation. Any saved state is also
- /// provided when recreating a page from a prior session.
- /// </summary>
- /// <param name="navigationParameter">The parameter value passed to
- /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
- /// </param>
- /// <param name="pageState">A dictionary of state preserved by this page during an earlier
- /// session. This will be null the first time a page is visited.</param>
- protected virtual void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
- {
- }
- /// <summary>
- /// 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 <see cref="SuspensionManager.SessionState"/>.
- /// </summary>
- /// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
- protected virtual void SaveState(Dictionary<String, Object> pageState)
- {
- }
- #endregion
- /// <summary>
- /// Implementation of IObservableMap that supports reentrancy for use as a default view
- /// model.
- /// </summary>
- private class ObservableDictionary<K, V> : IObservableMap<K, V>
- {
- private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs<K>
- {
- public ObservableDictionaryChangedEventArgs(CollectionChange change, K key)
- {
- this.CollectionChange = change;
- this.Key = key;
- }
- public CollectionChange CollectionChange { get; private set; }
- public K Key { get; private set; }
- }
- private Dictionary<K, V> _dictionary = new Dictionary<K, V>();
- public event MapChangedEventHandler<K, V> MapChanged;
- private void InvokeMapChanged(CollectionChange change, K key)
- {
- var eventHandler = MapChanged;
- if (eventHandler != null)
- {
- eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key));
- }
- }
- public void Add(K key, V value)
- {
- this._dictionary.Add(key, value);
- this.InvokeMapChanged(CollectionChange.ItemInserted, key);
- }
- public void Add(KeyValuePair<K, V> item)
- {
- this.Add(item.Key, item.Value);
- }
- public bool Remove(K key)
- {
- if (this._dictionary.Remove(key))
- {
- this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
- return true;
- }
- return false;
- }
- public bool Remove(KeyValuePair<K, V> item)
- {
- V currentValue;
- if (this._dictionary.TryGetValue(item.Key, out currentValue) &&
- Object.Equals(item.Value, currentValue) && this._dictionary.Remove(item.Key))
- {
- this.InvokeMapChanged(CollectionChange.ItemRemoved, item.Key);
- return true;
- }
- return false;
- }
- public V this[K key]
- {
- get
- {
- return this._dictionary[key];
- }
- set
- {
- this._dictionary[key] = value;
- this.InvokeMapChanged(CollectionChange.ItemChanged, key);
- }
- }
- public void Clear()
- {
- var priorKeys = this._dictionary.Keys.ToArray();
- this._dictionary.Clear();
- foreach (var key in priorKeys)
- {
- this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
- }
- }
- public ICollection<K> Keys
- {
- get { return this._dictionary.Keys; }
- }
- public bool ContainsKey(K key)
- {
- return this._dictionary.ContainsKey(key);
- }
- public bool TryGetValue(K key, out V value)
- {
- return this._dictionary.TryGetValue(key, out value);
- }
- public ICollection<V> Values
- {
- get { return this._dictionary.Values; }
- }
- public bool Contains(KeyValuePair<K, V> item)
- {
- return this._dictionary.Contains(item);
- }
- public int Count
- {
- get { return this._dictionary.Count; }
- }
- public bool IsReadOnly
- {
- get { return false; }
- }
- public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
- {
- return this._dictionary.GetEnumerator();
- }
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return this._dictionary.GetEnumerator();
- }
- public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
- {
- int arraySize = array.Length;
- foreach (var pair in this._dictionary)
- {
- if (arrayIndex >= arraySize) break;
- array[arrayIndex++] = pair;
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement