📜 ⬆️ ⬇️

Improving xaml: Bindable Converters, Switch Converter, Sets

It must be admitted that the xaml code is partly verbose, which sometimes causes some discomfort during development. In this article we will look at optimizations that will help to significantly improve the organization of the markup and make it more readable. This is especially true of working with converters, which are inherently associated with the data binding mechanism.

We will need some knowledge from past articles , in particular, an understanding of the principle of direct injections .


Bindable Converters

Sooner or later, many xaml developers are faced with the question of whether it is possible to create a converter that supports the binding of any parameters? But even if in addition to implementing the IValueConverter interface, deriving from the DependencyObject class and declaring the DependencyProperty in the converter, the binding will not work in most cases, since the converter is not part of the visual tree ! Of course, it is possible to go even further and create a hybrid controller-converter, imperceptibly placed on the performance, but such an exotic solution can hardly be called beautiful, and the range of its application is limited.
')
But the principle of direct injections comes to the rescue, because nothing prevents to apply StoreBinding to Dependency Converter .

<BooleanConverter x:Key="BindableConverter" OnTrue="Value1" OnFalse="Value2" OnNull="{StoreBinding StoreKey=viewModels: SettingsViewModel, Path=AnyValue3}"/> 

All ingenious is simple!

Note that in this way it is impossible to bind a converter to an element of the visual tree, even if they are on the same presentation. But such a problem is also solved, for example, by using the Attached Property of the control and creating the corresponding extension of the binding.

 <ToggleButton a:SourceKey="MyToogleButton"> 

 <BooleanConverter x:Key="BindableConverter" OnTrue="Value1" OnFalse="Value2" OnNull="{RemoteBinding SourceKey=MyToogleButton, Path=IsChecked}"/> 

Moreover, this option will work, even if the control and the converter are not on the same presentation! It is only necessary to be careful with its implementation, to use for storage a weak reference to the control, in order to avoid memory leaks.

Switch converter

Often in large projects it is necessary to create many different types of converters of the same type, the logic of which is very similar to the behavior of the if-else and switch statements , for example, for various enums. But in fact, in such cases, it suffices to limit the application of the universal Switch Converter :

 <SwitchConverter Default="ResultValue0" x:Key="ValueConverter1"> <Case Key="KeyValue1" Value="ResultValue1"/> <Case Key="KeyValue2" Value="ResultValue2"/> </SwitchConverter> 

Moreover, the properties of this converter (including Case designs) are Dependency , that is, available for binding using StoreBinding ! In addition, Type Mode is supported when the key is not the value of the object itself, but its type:

 <SwitchConverter TypeMode="True" Default="{StaticResource DefaultDataTemplate}" x:Key="InfoConverter"> <Case Type="local:Person" Value="{StaticResource PersonDataTemplate}"/> <Case Type="local:PersonGroup" Value="{StaticResource PersonGroupDataTemplate}"/> </SwitchConverter> 

It turns out that such a converter can easily be used as a DataTemplateSelector even where the latter is not supported! The versatility of Switch Converter allows you to cover a huge number of cases, you just need to apply a little imagination to it.

 <c:SwitchConverter Default="{StaticResource ControlTemplate0}" x:Key="TemplateSelectorConverter"> <m:Case Key='Value1' Value="{StaticResource ControlTemplate1}"/> <m:Case Key='Value2' Value="{StaticResource ControlTemplate2}"/> </c:SwitchConverter> 

 <ListBox ItemsSource="{Binding Items}"> <ListBox.ItemTemplate> <DataTemplate> <ContentControl Template="{Binding DataType, Converter={StaticResource TemplateSelectorConverter}}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> 

Global Resources

Since I started talking about converters, then it is worth telling how best to organize work with them. First of all, the most common need to bring in a separate resource dictionary:

 <!--AppConverters .xaml--> <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <BooleanConverter x:Key="NullToTrueConverter" OnNull="True" OnNotNull="False"/> <BooleanConverter x:Key="NullToFalseConverter" OnNull="False" OnNotNull="True"/> <BooleanConverter x:Key="NullToVisibleConverter" OnNull="Visible" OnNotNull="Collapsed"/> <BooleanConverter x:Key="NullToCollapsedConverter" OnNull="Collapsed" OnNotNull="Visible"/> <BooleanConverter x:Key="TrueToFalseConverter" OnTrue="False" OnFalse="True" OnNull="True"/> <BooleanConverter x:Key="FalseToTrueConverter" OnTrue="False" OnFalse="True" OnNull="False"/> <BooleanConverter x:Key="TrueToVisibleConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/> <BooleanConverter x:Key="TrueToCollapsedConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Visible"/> <BooleanConverter x:Key="FalseToVisibleConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed"/> <BooleanConverter x:Key="FalseToCollapsedConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Visible"/> <EqualsConverter x:Key="EqualsToCollapsedConverter" OnEqual="Collapsed" OnNotEqual="Visible"/> <EqualsConverter x:Key="EqualsToVisibleConverter" OnEqual="Visible" OnNotEqual="Collapsed"/> <EqualsConverter x:Key="EqualsToFalseConverter" OnEqual="False" OnNotEqual="True"/> <EqualsConverter x:Key="EqualsToTrueConverter" OnEqual="True" OnNotEqual="False"/> <AnyConverter x:Key="AnyToCollapsedConverter" OnAny="Collapsed" OnNotAny="Vsible"/> <AnyConverter x:Key="AnyToVisibleConverter" OnAny="Visible" OnNotAny="Collapsed"/> <AnyConverter x:Key="AnyToFalseConverter" OnAny="False" OnNotAny="True"/> <AnyConverter x:Key="AnyToTrueConverter" OnAny="True" OnNotAny="False"/> </ResourceDictionary> 

After that, you must directly or indirectly merge this dictionary with resources in App.xaml , which will allow you to use the basic converters in almost any xaml- files of the application without additional actions. It is useful to make such an addition to the global resources of an application for any more or less common things: colors, brushes, patterns and styles — which makes it very easy to implement, for example, the theme change mechanisms in the application.

 <Application x:Class="Sparrow.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Views/AppView.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <!--<ResourceDictionary Source="AppConverters.xaml"/>--> <ResourceDictionary Source="AppStore.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application> 

Sets

A very useful technique is the use in the xaml of the universal Set collection, which is applicable in many cases. It allows you to avoid "multi-storey structures", to make general points and make the markup much more accurate, as well as pass several arguments to the team.

 public class Set : ObservableCollection<object> { } 


 <Set x:Key="EditMenuSet" x:Shared="False"> <MenuItem Header="{Localizing Undo}" Command="Undo"/> <MenuItem Header="{Localizing Redo}" Command="Redo"/> <Separator/> <MenuItem Header="{Localizing Cut}" Command="Cut"/> <MenuItem Header="{Localizing Copy}" Command="Copy"/> <MenuItem Header="{Localizing Paste}" Command="Paste"/> <MenuItem Header="{Localizing Delete}" Command="Delete"/> <Separator/> <MenuItem Header="{Localizing SelectAll}" Command="SelectAll"/> </Set> 

 <MenuItem Header="{Localizing Edit}" ItemsSource="{StaticResource EditMenuSet}"/> 


 <Set x:Key="ParameterSet"> <system:String>/Views/AnyView.xaml</system:String> <system:String>SecondParaneter</system:String> </Set> 

 <Button Content="{Localizing GoToAnyView}" Command="{Context Key=GoTo}" CommandParameter="{StaticResource ParameterSet}"> 

Thank you for attention!

Appeal of the author

I hope that all these articles are interesting and useful to developers. I especially want to note the mutual consistency of the proposed concepts among themselves, they seem to complement and reinforce each other. I think that those people who study the materials well enough will be able to apply them in their work and appreciate them.

It is foolish to hide this knowledge or take payment for them, because even to perceive and realize them is not so simple. Perhaps after a while, someone will understand how many resources have helped to preserve these simple tips in their form, and will want to thank the creator of the Aero Framework library, because donation details are available on this page . Perhaps some even wish to go even further into the study and purchase the source code of real projects (of course, for individual developers their cost may seem tangible, but for companies it is a symbolic fee).

Therefore, even if you use the library for free, then please recommend it to your colleagues, friends and all those in the subject! Thank!

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


All Articles