XAML Aesthetics: Value Converters

The article presents generalized approaches to use when writing XAML-code.

>> Read in English

IValueConverter Data Binding XAML WPF UWP Xamarin Forms UI SwitchConverter KeyToValueConverter InlineConverter AggregateConverter ResourceDictionary

 together with the data binding engine are important components in the development of user interfaces based on  XAML. Value converters imply the presence of logic in a separate class that implements the interface  IValueConverter. Typically, the class name reflects the functional purpose, and the instances are declared in the markup.

Switch Converter & Key To Value Converter

In practice, many converters have trivial logic values similar in structure to  (?:) or designs  if-elseswitch-case-default. However, there are generalized templates  KeyToValueConverter and  SwitchConverterthat allow you to avoid adding classes of the same type in structure to the project by declaring boolean values ​​and branches directly in the markup.

Concept

<KeyToValueConverter
	Key="KeyForMatching"
	Value="ValueIfKeyMatched"
	ByDefault="ValueIfKeyNotMatched" />

<SwitchConverter
	ByDefault="ValueZ">
	<Case
		Key="KeyA"
		Value="ValueA" />
	<Case
		Key="KeyB"
		Value="ValueB" />
	<Case
		Key="KeyC"
		Value="ValueC" />
</SwitchConverter>

Application

<KeyToValueConverter
	x:Key="TrueToVisibleConverter"
	Key="True"
	Value="Visible"
	ByDefault="Collapsed" />
	
<ProgressBar
	Visibility="{Binding IsBusy, Converter={StaticResource TrueToVisibleConverter}}" />
<SwitchConverter
	x:Key="CodeToBackgroundConverter"
	ByDefault="White">
	<Case
		Key="R"
		Value="Red" />
	<Case
		Key="G"
		Value="Green" />
	<Case
		Key="B"
		Value="Blue" />
</SwitchConverter>
	
<Control
	Background="{Binding Code, Converter={StaticResource CodeToBackgroundConverter}}" />

KeyToValueConverter -  Key, ,  Value,  ByDefault.

SwitchConverter -  Case   Key,  Case ,  ¨C31C, ¨C90C¨C32C, .

 Value  ByDefault  , , .

 KeyToValueConverter   ConverterParameter   KeySource

<KeyToValueConverter
	x:Key="EqualsToHiddenConverter"
	KeySource="ConverterParameter"
	Value="Collapsed"
	ByDefault="Visible" />
	
<Control
	Visiblity="{Binding Items.Count, ConverterParameter=0, Converter={StaticResource EqualsToHiddenConverter}}" />

<TextBlock
	Visiblity="{Binding Text, ConverterParameter='Hide Me', Converter={StaticResource EqualsToHiddenConverter}}" />

 KeySource  :

Manual (by default) -  Key  ,

ConverterParameter -  ConverterParameter  ,

PreferManual -  manual Key  ,  ConverterParameter

PreferConverterParameter -  ConverterParameter  , manual Key

,  SwitchConverter   Case   TypedCase,

<SwitchConverter
	ByDefault="Undefined value">
	<TypedCase
		Key="system:String"
		Value="String value" />
	<Case
		Key="0"
		Value="Zero" />
	<Case
		Key="1"
		Value="One" />
	<TypedCase
		Key="system:Int32"
		Value="Int32 value" />
</SwitchConverter>

.  SwitchConverter   DiagnosticKey, ,  Trace 

var diagnosticMessage = matchedCase.Is()
	? $"{DiagnosticKey}: '{matchedValue}' matched by key '{matchedCase.Key}' for '{value}' and converted to '{convertedValue}'"
	: $"{DiagnosticKey}: The default value '{matchedValue}' matched for '{value}' and converted to '{convertedValue}'";

Trace.WriteLine(diagnosticMessage);
<SwitchConverter
	DiagnosticKey="UniqDiagnosticKey"
	x:Key="CodeToBackgroundConverter"
	ByDefault="White">
	...
</SwitchConverter>

Dependency Value Converter

Key, Value ByDefault (Dependency Properties), Cases DependencyObject. , , Binding,

<KeyToValueConverter
	Key="AnyKey"
	Value="{Binding MatchedValue, Source={StaticResource AnyResource}}"
	ByDefault="{Binding DefaultValue, Source={StaticResource AnyResource}}" />
		
<KeyToValueConverter
	Key="AnyKey"
	Value="{Localizing MatchedTitle}"
	ByDefault="{Localizing DefaultTitle}" />

Inline Converter

, IValueConverter, code-behind .

, .

, code-behind Converting ConvertingBack

<Grid>
	<Grid.Resources>
		<InlineConverter
			x:Key="ComplexInlineConverter"
			Converting="InlineConverter_OnConverting"
			ConvertingBack="InlineConverter_OnConverting" />
	</Grid.Resources>
	
	<TextBlock
		Text="{Binding Number, Converter={StaticResource InlineConverter}}"/>
</Grid>
private void InlineConverter_OnConverting(object sender, ConverterEventArgs e)
{
	// e.Value - access to input value
	// this.DataContext - access to Data Context or another properties of the view
	// access to child visual elements of this root view
	e.ConvertedValue = // set output value
		$"DataContext: {DataContext}, Converter Value: {e.Value}";
}

private void InlineConverter_OnConvertingBack(object sender, ConverterEventArgs e)
{
	// ...
}

Aggregate Converter

, .

<AggregateConverter>
	<StepAConverter />
	<StepBConverter />
	<StepCConverter />
</AggregateConverter>

App.xaml

It is useful to place generic value converters in a separate Resource Dictionary, and then merge them as global resources in the App.xaml file. This allows you to reuse value converters in different representations without re-declaring them.

<Application
	xmlns="http://xamarin.com/schemas/2014/forms"
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	x:Class="Any.App">
	<Application.Resources>
		<ResourceDictionary>
			<ResourceDictionary.MergedDictionaries>
				<ResourceDictionary
						Source="AppConverters.xaml" />
				...
			</ResourceDictionary.MergedDictionaries>
		</ResourceDictionary>
	</Application.Resources>
</Application>

Ace Framework

Examples of implementation of the presented converters can be found in the Ace Framework gitlab bitbucket library

With gratitude for your attention and interest!




All Articles