Эстетика XAML: конвертеры значений

от автора

В статье представлены обобщённые подходы применения конвертеров значений при написании XAML-кода.

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

Конвертеры значений совместно с механизмом привязки данных являются важными компонентами в разработке пользовательских интерфейсов на основе XAML. Конвертеры значений подразумевают наличие логики, помещенной в отдельный класс, реализующий интерфейс IValueConverter. Обычно имя класса отражает функциональное назначение, а сами экземпляры декларируются в разметке.

Switch Converter & Key To Value Converter

На практике многие конвертеры значений имеют тривиальную логику схожую по структуре с тернарным оператором (?:) или конструкциям if-elseswitch-case-default. Однако существуют обобщенные шаблоны KeyToValueConverter и SwitchConverter, которые позволяют избежать добавления в проект однотипных по структуре классов путём декларирования логических значений и ветвлений непосредственно в разметке.

Концепция

<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>

Применение

<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

Обобщённые конвертеры значений полезно помещать в отдельный Resource Dictionary, а затем мержить их в качестве глобальных ресурсов в файл App.xaml. Это позволяет переиспользовать конвертеры значений в различных представлениях без их повторного декларирования.

<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

Примеры реализации представленных конвертеров можно найти в библиотеке Ace Framework gitlab bitbucket

С благодарностью за внимание и интерес!

ссылка на оригинал статьи https://habr.com/ru/post/526450/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *