Blue Flower

It must be admitted, xaml -code is somewhat verbose, sometimes causing some discomfort while working out. In this article we consider optimization, which will help to significantly improve the organization and layout to make it more readable. This is especially true of work with converters that are inherently connected with the data binding mechanism.

Нам понадобятся некоторые знания из прошлых статей, в частности, понимание принципа прямых инжекций.


Bindable Converters

Sooner or later, many xaml-developers faced with the question of whether to create a converter may support binding any parameters? But even if in addition to the implementation of an interface IValueConverter, to make the inheritance of a class DependencyObject and declare a converter DependencyProperty, the binding work in most cases will not be as converter is not part of the visual tree! Of course, it is possible to go even further and create a hybrid of the control of the converter, discreetly placed on a representation, but such exotic solutions can hardly be called beautiful, and the range of its application is limited.

But the rescue comes the principle of direct injections, because nothing prevents use 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 can not bind to the converter element visual tree, even if they are on the same view. But this problem can be solved, for example, using Attached Property at the controls and the creation of a corresponding expansion anchor.

<ToggleButton a:SourceKey="MyToogleButton">

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

Moreover, such an option will work even if the control and converter are not in one view! It is only necessary to be cautious with its implementation, used to store the weak link in the control not to get a memory leak.

Switch Converter

Often in large projects have to create many different similar classes converters, logic which is very similar to the behavior of the operators if-else and switch, for example, for various transfers (Enums). But in fact, in such cases it is sufficient to restrict the use of universal Switch Converter:

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

Moreover, the properties of the converter (including the construction of Case) is Dependency, that is available to bind with the help of StoreBinding! It also supports the Type Mode, when the key is not very meaning of the object, and 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 is easily applicable as DataTemplateSelector, even where the latter is not supported! Versatility Switch Converter allows you to cover a great number of cases, it is necessary only to apply to it a little imagination.

<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 the conversation turned on the converters, it is worth discussing how best to organize the work with them. First of all, the most common need to make 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 smerzhit this dictionary resource in the App.xaml, which will use the basic converters in almost any xaml Application File, without further action. This addition to the global resources of an application useful for producing any more or less total items: color brushes, patterns and styles - that helps realize very simple, for example, the mechanisms of change 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

Very helpful reception is to use xaml generic collection Set, applicable in many cases. It avoids the "multi-storey structures," to make general points and make the layout is much more accurate, and send multiple arguments in the command.

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!