В статье представлены обобщённые подходы применения
конвертеров значений
при написании
XAML
-кода.
IValueConverter
Data Binding
XAML
WPF
UWP
Xamarin Forms
UI
SwitchConverter
KeyToValueConverter
InlineConverter
AggregateConverter
ResourceDictionary
Конвертеры значений
совместно с механизмом привязки
данных являются важными компонентами в разработке пользовательских
интерфейсов на основеXAML
. Конвертеры значений
подразумевают наличие логики, помещенной в отдельный класс,
реализующий интерфейсIValueConverter
. Обычно имя
класса отражает функциональное назначение, а сами экземпляры
декларируются в разметке.
Switch Converter & Key To Value Converter
На практике многие конвертеры значений имеют тривиальную логику
схожую по структуре стернарным оператором (?:)
или
конструкциямif-else
,switch-case-default
.
Однако существуют обобщенные
шаблоныKeyToValueConverter
иSwitchConverter
,
которые позволяют избежать добавления в проект однотипных по
структуре классов путём декларирования логических значений и
ветвлений непосредственно в разметке.
Концепция
<KeyToValueConverterKey="KeyForMatching"Value="ValueIfKeyMatched"ByDefault="ValueIfKeyNotMatched" /><SwitchConverterByDefault="ValueZ"><CaseKey="KeyA"Value="ValueA" /><CaseKey="KeyB"Value="ValueB" /><CaseKey="KeyC"Value="ValueC" /></SwitchConverter>
Применение
<KeyToValueConverterx:Key="TrueToVisibleConverter"Key="True"Value="Visible"ByDefault="Collapsed" /><ProgressBarVisibility="{Binding IsBusy, Converter={StaticResource TrueToVisibleConverter}}" />
<SwitchConverterx:Key="CodeToBackgroundConverter"ByDefault="White"><CaseKey="R"Value="Red" /><CaseKey="G"Value="Green" /><CaseKey="B"Value="Blue" /></SwitchConverter><ControlBackground="{Binding Code, Converter={StaticResource CodeToBackgroundConverter}}" />
KeyToValueConverter
- проверяет входное значение на
соответствие со значением из свойстваKey
, если
соответствие выполнено, то в качестве выходного берётся значение из
свойстваValue
, в противном случае из
свойстваByDefault
.
SwitchConverter
- выполняет поиск первого
соответствующегоCase
из списка по его ключу из
свойстваKey
, если
соответствующийCase
найден, то берётся заданное в нём
значение из свойстваC31C, в противном случае из свойстваC90CC32C,
заданного в самом конвертере значений.
Если свойствоValue
илиByDefault
явно не
задано, но выполняется соответствующее ему условие, то в таком
случае происходит обыкновенный проброс входного значения в качестве
выходного.
Также уKeyToValueConverter
иногда полезно задавать
ключ вConverterParameter
через
свойствоKeySource
<KeyToValueConverterx:Key="EqualsToHiddenConverter"KeySource="ConverterParameter"Value="Collapsed"ByDefault="Visible" /><ControlVisiblity="{Binding Items.Count, ConverterParameter=0, Converter={StaticResource EqualsToHiddenConverter}}" /><TextBlockVisiblity="{Binding Text, ConverterParameter='Hide Me', Converter={StaticResource EqualsToHiddenConverter}}" />
Для особых случаев уKeySource
возможны четыре режима
работы:
Manual
(by default
) - в качестве ключа
при проверке соответствия всегда используется значение из
свойстваKey
либо выполняется проброс значения, когда
оно не задано
ConverterParameter
- в качестве ключа всегда
используется значение из свойства
привязкиConverterParameter
либо выполняется проброс
значения, когда оно не задано
PreferManual
- еслиmanual Key
явно
задан, то он имеет приоритет
передConverterParameter
PreferConverterParameter
-
еслиConverterParameter
явно задан, то он имеет
приоритет перед manualKey
Стоит также отметить, что уSwitchConverter
помимо
обычныхCase
доступны такжеTypedCase
,
основное отличие которых в проверке на соответствие по типу
значения
<SwitchConverterByDefault="Undefined value"><TypedCaseKey="system:String"Value="String value" /><CaseKey="0"Value="Zero" /><CaseKey="1"Value="One" /><TypedCaseKey="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);
<SwitchConverterDiagnosticKey="UniqDiagnosticKey"x:Key="CodeToBackgroundConverter"ByDefault="White">...</SwitchConverter>
Dependency Value Converter
Также свойства Key
, Value
и
ByDefault
полезно объявлять в качестве свойств
зависимости
(Dependency Properties
), то есть
наследовать конвертеры и Case
s от класса
DependencyObject
. Хотя конвертеры значений обычно не
являются элементами визуального дерева, что отчасти ограничивает
работу механизма привязки данных, тем не менее остаётся возможность
производить привязку к статическим ресурсам или наследникам класса
Binding
, например
<KeyToValueConverterKey="AnyKey"Value="{Binding MatchedValue, Source={StaticResource AnyResource}}"ByDefault="{Binding DefaultValue, Source={StaticResource AnyResource}}" /><KeyToValueConverterKey="AnyKey"Value="{Localizing MatchedTitle}"ByDefault="{Localizing DefaultTitle}" />
Inline Converter
Встраиваемый конвертер позволят перенести логику по
преобразованию значений из отдельного класса, реализующего
интерфейс IValueConverter
, в code-behind
класс конкретного представления на основе событийной модели.
Это позволяет получить доступ к представлению и его отдельным визуальным элементам из логики конвертирования при реализации сложных сценариев, которые затруднительно реализовать при классическом подходе.
Для этого необходимо добавить декларацию конвертера в разметку,
а code-behind
классе определить обработчики для
соответствующих событий Converting
и
ConvertingBack
<Grid><Grid.Resources><InlineConverterx: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 viewe.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. Это позволяет переиспользовать конвертеры значений в различных представлениях без их повторного декларирования.
<Applicationxmlns="http://personeltest.ru/away/xamarin.com/schemas/2014/forms"xmlns:x="http://personeltest.ru/away/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
С благодарностью за внимание и интерес!