📜 ⬆️ ⬇️

Cook MVVM for Windows Store Applications

When we started working on applications for Windows 8, we looked for the Model-View-ViewModel template support library (MVVM) for this platform. We spent some time on the Internet looking for one, but in the end we accepted the fact that there are no such libraries in nature yet (perhaps we were looking badly, but now this is not so important). The answer to the question "what to do?" Suggested itself ...



In the depths of our company, EastBanc Technologies, a special library was created (codename EBT.Mvvm ). The purpose of creation is to save time in the future when developing complex applications for Windows 8. The library includes both our own developments and some ideas and examples that we encountered during our searches.
')
So, what we have: we all remember that the main idea of ​​the template is to weaken the connection between the ViewModel (we will call the view model) and the View directly. The ideal state is when the code-behind of the view contains only the constructor with the InitializeComponent and, possibly, the code supporting visual behavior that cannot be defined via XAML. Thus, the developer gives a presentation to the designer, while he focuses on the work and testing of the application logic.

This article is aimed at developers who are already familiar with programming in C # and XAML under Windows 8. Below we provide descriptions of the main features of our library as examples of their use code and comments. So, let's go:

1. Base ViewModel Class

The first thing to start with when talking about the MVVM template is the base class for our view models. The main purpose is to support the INotifyPropertyChanged interface and convenient functions for automatic notification when changing properties. Usage example:

public class SimpleViewModel : ViewModel { private int _number; public int Number { get { return _number; } set { OnPropertyChange(ref _number, value); } } } 

Everything should be clear without comments. It should be added that there is a set of overloaded functions for automatic notification when a property changes. There is also a way to avoid writing a field altogether. This refers to the so-called backing field. An example is the _number field in the example code above. At the same time, you can continue to create properties with the support of automatic notification This is quite convenient if, in view of the model, we have many properties for binding. The example below shows how to make a property with this feature in mind (a field is not required).

 public string Text { get { return GetPropertyValue(() => Text); } set { SetPropertyValue(() => Text, value); } } 


2. Teams

The familiar and necessary handler of commands RelayCommand. Binds to the Command property of the ButtonBase base class (buttons, menu items, hyperlinks) and supports the ICommand interface. Thing irreplaceable and implemented for a long time. However, should be mentioned:

 public class SimpleViewModel : ViewModel { public SimpleViewModel() { SampleCommand = new RelayCommand(OnSample); } public RelayCommand SampleCommand { get; private set; } private void OnSample() { // TODO Do something here. } } 


 <Button Command="{Binding SampleCommand}" Content="Button Text" /> 


3. Binding event handlers

We added the ability to conveniently bind event handlers. MVVM implies that user interface event handling must occur on the side of the view model. Without a little trick, this is not possible. It consists in binding the attached property of the user interface element. Currently the library supports the processing of a large number of events. The list can be expanded if necessary by the developer himself. As an example, we give the handling of the Tapped event of the TextBlock element:

 public class SimpleViewModel { public SimpleViewModel() { TappedCommand = new EventCommand<Point>(OnTapped); } public IEventCommand TappedCommand { get; private set; } private void OnTapped(Point point) { TappedCommand.PreventBubbling = point.X < 100; } } 


 <TextBlock Mvvm:EventBinding.Tapped="{Binding TappedCommand}" Text="Tap me"/> 


Here you should pay attention to the line with TappedCommand.PreventBubbling = point.X <100. The fact is that we have provided the ability to cancel further event handling (Handled) by setting the corresponding flag.

At the moment, there is a support for the events: a , Loaded.

4. Support various screen modes

In our opinion, this is the most interesting feature of the library. For targeted applications targeted at tablets downright indispensable! Remember that there are four screen modes and that maintaining them all is a good tone. We have two mechanisms for changing the display of user interface elements depending on the current screen mode.



 <TextBlock behaviors:OrientationBehavior.Orientations="Landscape,Filled,Portrait" Text="Not snapped"/> <TextBlock behaviors:OrientationBehavior.Orientations="Snapped" Text="Snapped"/> 


The following example shows the change in the orientation of the list depending on the screen mode.

 <GridView ItemsSource="{Binding YourItems}"> <behaviors:OrientationBehavior.LandscapeStyle> <!-- This style will be applied in landscape, filled and portrait modes. --> <Style TargetType="ListViewBase"/> </behaviors:OrientationBehavior.LandscapeStyle> <behaviors:OrientationBehavior.SnappedStyle> <!-- This style will be applied in the snapped mode. --> <Style TargetType="ListViewBase"> <Style.Setters> <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/> <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled"/> <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> <Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto"/> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Vertical"/> </ItemsPanelTemplate> </Setter.Value> </Setter> </Style.Setters> </Style> </behaviors:OrientationBehavior.SnappedStyle> </GridView> 

The method of changing the style of an element is a very convenient and powerful feature. When using it, you must remember the following:


A pleasant consequence of using the style change method is that with this approach, using the ContentControl / ContentPresenter, you can change the view template completely! Below is how this is done:

 <Grid Name="main"> <ContentControl> <behaviors:OrientationBehavior.LandscapeStyle> <Style TargetType="ContentControl"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Grid> <TextBlock Text="Landscape"/> <!-- Something in landscape mode --> </Grid> </DataTemplate> </Setter.Value> </Setter> </Style> </behaviors:OrientationBehavior.LandscapeStyle> <behaviors:OrientationBehavior.PortraitStyle> <Style TargetType="ContentControl"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Grid> <TextBlock Text="Portrait"/> <!-- Something in portrait mode --> </Grid> </DataTemplate> </Setter.Value> </Setter> </Style> </behaviors:OrientationBehavior.PortraitStyle> <behaviors:OrientationBehavior.SnappedStyle> <Style TargetType="ContentControl"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Grid> <TextBlock Text="Snapped. Only text here"/> </Grid> </DataTemplate> </Setter.Value> </Setter> </Style> </behaviors:OrientationBehavior.SnappedStyle> </ContentControl> </Grid> 

For example, this way you can easily make the transition to snapped mode.

5. Calling View methods from ViewModel

Sometimes it is necessary to call the user interface methods from a model view. An example is the need to set the input focus on a given field. This can be done using our ControlWrapper:

 public class SimpleViewModel : ViewModel { public SimpleViewModel() { TextBoxWrapper = new ControlWrapper(); } public ControlWrapper TextBoxWrapper { get; private set; } public void GotoField() { TextBoxWrapper.Focus(); } } 


 <TextBox Mvvm:ElementBinder.Wrapper="{Binding TextBoxWrapper}"/> 


6. Event Triggers for Animation

This mechanism allows you to start an animation when an event occurs in a view item. And again, not a single line of code in the code-behind! The method is based on binding event handlers. In XAML, you need to define a special TriggerCommand command:

 <Grid> <FrameworkElement.Resources> <Storyboard x:Key="FadeOut"> <PointerDownThemeAnimation Storyboard.TargetName="MyElement"/> </Storyboard> <Storyboard x:Key="FadeIn"> <PointerUpThemeAnimation Storyboard.TargetName="MyElement"/> </Storyboard> </FrameworkElement.Resources> <Border x:Name="MyElement" Width="100" Height="100" Background="Red"> <mvvm:EventBinding.PointerPressed> <mvvm:TriggerCommand Storyboard="{StaticResource FadeOut}"/> </mvvm:EventBinding.PointerPressed> <mvvm:EventBinding.PointerReleased> <mvvm:TriggerCommand Storyboard="{StaticResource FadeIn}"/> </mvvm:EventBinding.PointerReleased> </Border> </Grid> 


7. Binding context menu

ContextMenuBehavior allows you to quickly and conveniently display the context menu on the right-click or tap on the touchscreen. In the view, you only need to make a link on the element for which the context menu will be called. And in the model to determine the list of commands and handlers:

 public class MyViewModel : ViewModel { private IList<UICommand> _contextMenuCommands; private string _text; public string Text { get { return _text; } set { OnPropertyChange(ref _text, value); } } public IList<UICommand> ContextMenuCommands { get { return _contextMenuCommands ?? (_contextMenuCommands = new List<UICommand> { new UICommand("Copy", OnCopy), new UICommand("Paste", OnPaste), }); } } private void OnCopy(IUICommand command) { var content = new DataPackage(); content.SetText(Text); Clipboard.SetContent(content); } private async void OnPaste(IUICommand command) { var content = Clipboard.GetContent(); Text = await content.GetTextAsync(); } } 


 <TextBlock behaviors:ContextMenuBehavior.Commands="{Binding ContextMenuCommands}" Text="{Binding Text}" MinWidth="300" Height="40"/> 




8. Binding a popup

PopupBehavior allows you to create popup display functionality by pressing the right mouse button or tap on a touchscreen. Everything should be clear from the sample code below:

 <TextBlock Text="Tap or right click here for more information" behaviors:PopupBehavior.Placement="Above"> <behaviors:PopupBehavior.Content> <DataTemplate> <TextBlock Text="More information..."/> </DataTemplate> </behaviors:PopupBehavior.Content> </TextBlock> 




9. Cross-page navigation

One of the problems for the developer is page navigation - it is not very convenient to keep the code-behind clean if transitions are made via Frame calls from a view. And almost always there is a need to handle Navigating and Navigated events in the view model.

To achieve our goals, we create the main model of our application:

 public class RootModel { public RootModel() { NavigationState = new NavigationState(); HomePageModel = new HomePageModel(this); } public NavigationState NavigationState { get; set; } public HomePageModel HomePageModel { get; set; } public bool CanGoBack { get { return NavigationState.CanGoBack; } } public void GoBack() { NavigationState.GoBack(); } public void GoToHomePage() { NavigationState.Navigate(typeof (HomePage)); } } 

When the application is launched, we set the main model as the context of the top-level element of the visual tree of objects and associate the wrapper NavigationState class with the frame.

 sealed partial class App : Application { ... public RootModel RootModel { get; private set; } protected override void OnLaunched(LaunchActivatedEventArgs args) { RootModel = new RootModel(); var frame = new Frame { DataContext = RootModel }; // Bind the NavigationState and the frame using the ElementBinder class. // You can also do this in XAML. ElementBinder.SetWrapper(frame, RootModel.NavigationState); Window.Current.Content = frame; Window.Current.Activate(); RootModel.GoToHomePage(); } } 

Now our HomePageModel view model can handle OnNavigating and OnNavigated events. As well as navigate to other pages through a saved link to _rootModel. Note that OnNavigating supports cancellation of the transition (the ref bool cancel parameter).

 public class HomePageModel : PageModel // Or implement IPageModel. { private RootModel _rootModel; // You can call _rootModel.NavigationState.Navigate(…) public HomePageModel(RootModel rootModel) { _rootModel = rootModel; } public override void OnNavigated() { // TODO Do something here to initialize/update your page. } public override void OnNavigating(ref bool cancel) { // TODO Do something here to clean up your page. } } 

In XAML, we set the correct DataContext of the page for correct binding.

 <Page x:Class="YourNamespace.HomePage" ... DataContext="{Binding HomePageModel}"> <!-- Your page content goes here --> </Page> 

Everything, the result is achieved. Now you can create pages and link them to view models. The latter will handle the OnNavigating and OnNavigated events and control the navigation.

10. Template for generating skeleton project

We have provided the ability to quickly create a framework for the project using our library. A project template is embedded in Visual Studio and appears in Windows Store projects. The template is also available in the online library of Visual Studio project templates.



Bye all

Well, it seems that this is enough for one article. In fact, though most, but not all features of our library were listed. There are also converters, saving and restoring state, assistant for the charm-panel. Habrachiteli will be able to learn the rest independently, having directly established and having used this project. So smoothly proceed to the next item:

Where can I download?

Interested workers will want to see the described library in action. Make it very easy. Our library is available for download as a Nuget Package . Also, our project is running on CodePlex .

The fastest way to install it in the studio is to use the search in 12 studios via Tools-> Extensions and Updates. Select Online and in the search box, type the keywords Windows 8 MVVM .



At last

“The EBT.Mvvm library is distributed on an“ as is ”basis, the developer is not responsible for possible consequences ...”

But seriously, we will be happy if our library helps application developers for the young Windows 8 platform save time to overcome the problems we had to face. To the best of our abilities and capabilities, we are constantly correcting and improving this software project. Your suggestions and comments can help us with this.

I would like to wish all the developers involved in development good luck. Let's create more applications for Windows Store!

Source: https://habr.com/ru/post/172839/


All Articles