Xaml Universal App and built-in navigation
Lets start work on the project from fundamental part - video player written with C#/Xaml in Universal Windows Platform (UWP) that takes advantage of .Net Core Framework.
UWP Xaml has couple significant differences compared to the worn out WPF implementation... however, because i always use navigation delivered by 3rd party MVVM frameworks and have never had a chance to examine native navigation concept behind Xaml, i have decided to explore it in details and check how does it adapt into MVVM pattern - only for my own satisfaction ;)
Frame and Page
Navigation concept in Xaml is based on two controls Frame and Page. Frame element is host for Page type, maintains its lifetime and stores navigation history.
To perform navigation to next page (basically that means loading Page into Frame.Content) we have to call Navigate method that takes type of our destination page.
Navigate method possess some restrictions and not all of them are visible at a first glance:
- Navigation works only for Page type so referenced type must be inherited from Page type.
- contextParameter is type of Object so you can pass there anything you want, however you have to remember that:
- contextParameter should be a basic type (string, char, numeric, GUID) if you want to support GetNavigationState - it returns serialized navigation state, useful when application can be suspended and resumed (and no, you cannot mark your class with [serializable] attribute because it is no present in UWP ;). [DataContract] and [DataMember] also don't give positive results)
- even if you don't want to recreate navigation state after suspension you shouldn't populate contextParameter with large objects. Navigation history retains references to all contextParameters that have been used during each navigation, so large object allows you easier impact the memory.
What happens after Frame.Navigate()?
By default each page (or page subclass) is instantiated every time when Frame.Navigate() targets it, also at the same time previous page is disposed. That means that all your UI changes will be lost after navigating away. To change this behavior you have to simply use property Page.NavigationCacheMode that can take three values:
- Disabled: this is default behavior,
- Enabled: Page will be stored in frame cache as long as it doesn't exceed frame cache size,
- Required: Page will be stored always, independently of cache size, it also doesn't count to cache size
The frame maintains a history of pages it has navigated to. You can return to previous page using Frame.GoBack() and revert this step with similar method Frame.GoFroward(). Collections of available previous and forward navigation history are available via Frame.BackStack and Frame.ForwardStack. The sum of pages stored on both stacks can be set by Frame.CacheSize (by default it's 10 pages)
Single Frame MVVM
As you could already see entire navigation is tightly coupled with UI controls and they don't exactly fit to the MVVM conception. As long as you can live with statically defined navigation and it doesn't require interaction with VM/M business logic you don't have to bother yourself. However only small apllications have chances to avoid it. Maintenance of View component directly in ViewModel doesn't sound like a wonderful layers separation? You can find example of simple NavigationService and NavigationServiceProvider at github/sandbox
Nested Frames MVVM
Situation complicates itself when you would like to have more than one frame in your application and additionaly you would like to have single navigation history for all of frames. In WPF implementation, tracking of navigation history between nested frames was quite simple. You had to set up JournalOwnership property on child frame. by default in WPF every frame has been setup as JournalOwnership.OwnsJournal and you only had to switch it to JournalOwnership.UsesParentJournal. Unfortunately JournalOwnership is not present in UWP Frame implementation, so we have to deal with nested navigation by own.
You can find naive NestedNavigationService implementation example at github/sandbox