📜 ⬆️ ⬇️

Xaml context injectors

In the previous part, we became familiar with binding extensions and figured out how to use them in practice, for example, for localization. Today we will continue to study the features of the Aero Framework library and consider a rather interesting topic about the injection of data context into the xaml- layout of views , and at the same time we apply the knowledge from the previous article.

In practice, the following task is often encountered: to associate a view model that is stored in a unity container with one or more of its views (screens). Typically, such a binding occurs in behaind code, with the result that the view is set to the desired value in the DataContext property.

In many cases it works well, but with this approach certain nuances and difficulties are associated. For example, they concern context menus and other pop-up animation, since it does not enter the visual tree, and therefore, the main data context becomes inaccessible for it. Another case is related to the operation of list elements when the context is already a list element, but there is a need to use another source of binding. The third option arises when one view works with several view models at once.
')
All these difficulties can be solved in one way or another, but there is a universal and very simple way to solve them beautifully. About him and will be discussed.


To begin with we will be defined with terminology. Each elementary visual control is a small atomic representation . Complex complex representations are built on the basis of simple ones, forming a tree structure ( visual tree ), where each node is also a representation up to the root. We will distinguish a special kind of representations - screens , which in one form or another support navigation and are often the root.

Let there be a single store Store , from which the necessary instance of the object is extracted by key. The idea is to use the xaml markup extension to provide the ability to extract an arbitrary instance of an object and inject it further as a data context into any node of the visual tree.

Everything looks very simple:

<Control DataContext="{Store Key=viewModels:AppViewModel}"/> 

 <Control> <Control.DataContext> <Store Key=viewModels:AppViewModel> </Control.DataContext> </Control> 

Accessing view models from C # code is also extremely easy:

 var appViewModel = Store.Get<AppViewModel>(); var userViewModel = Store.Get<IUserViewModel>(); 

Moreover, WPF allows you to inject, even in binding!

 <Slider DataContext="{Store Key=viewModels:MapViewModel}" Minimum="{Bindind MinimumZoomValue}" Maximum="{Binding MaximumZoomValue}" Value="{Binding ZoomValue, Mode=TwoWay}" Visibility="{Binding ShowSlider, Source="{Store Key=viewModels:SettingsViewModel}", Converter={StaticResource TrueToVisibleConverter}}"/> 

Pay attention to the line Visibility = "{Binding ShowSlider, Source =" {Store Key = viewModels: SettingsViewModel} " ... ... such flexibility is hard to achieve even with the help of a Behain-code, and our record was very concise.

For the sake of fairness, it should be noted that markup parsers on many other platforms require prefixes, and also do not support extensions nested in each other, but this problem is solved simply by using the Binding Extension :

 <Slider DataContext="{m:Store Key=viewModels:MapViewModel}" Minimum="{Bindind MinimumZoomValue}" Maximum="{Binding MaximumZoomValue}" Value="{Binding ZoomValue, Mode=TwoWay}" Visibility="{m:StoreBinding Path=ShowSlider, StoreKey=viewModels:SettingsViewModel, Converter={StaticResource TrueToVisibleConverter}}"/> 

For implementation details, refer to the source code of the Aero Framework library , everything is very transparent and understandable. The key is usually the type of the view model or the type of interface that it implements, but nothing prohibits the use of any others.

That is, to associate an application screen (page or window) is just a few lines:

 <!--WP7, WP8, WPF--> <Page xmlns:viewModels="clr-namespace:AeroPlayer.ViewModels" DataContext="{m:Store Key=viewModels:SongViewModel}"> ... </Page> 

 <!--WPF--> <Window xmlns:viewModels="clr-namespace:AeroPlayer.ViewModels" DataContext="{Store viewModels:SongViewModel}"> ... </Window> 

 <!--Windows Store, WP8.1--> <Page xmlns:viewModels="using:AeroPlayer.ViewModels"> <Page.DataContext> <Store Key=viewModels:AppViewModel> </Page.DataContext> ... </Page> 

And no behaind code! With context menus, now everything is very elegant:

 <ContextMenu DataContext="{Store viewModels:AppViewModel}"> ... </ContextMenu> 

But how beautifully such situations are solved:

 <ListBox DataContext={Store viewModels:AppViewModel} ItemsSource={Binding Persons}> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding FirstName}"/> <TextBlock Text="{Binding LastName}"/> <TextBlock Text="{Binding Age}" Visibility="{StoreBinding Path=ShowDetails, StoreKey=viewModels:SettingsViewModel, Converter={StaticResource TrueToVisibleConverter}}"/> </StackPanel> </DataTemplate> <ListBox.ItemTemplate> </ListBox> 

I hope that you already wanted to apply the trip considered in practice. This is the implementation of the Direct Injections Principle , which is proposed in the article . Note that a single twist model may have several representations, but the opposite situation, when presented with several twist models at once, is rare in Prague because of the technical difficulties described above. But with the help of direct injections, the view -model-representation relationship easily expands from one to many to many to many.

Thank you for your interest!

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


All Articles