Konvertor - nejlepší přítel člověka 1
Součástí WPF je propracovaný DataBinding (více ve článku DataBinding a DataTemplate), kterým je možné přiřazovat hodnoty z různých zdrojů dat (vlastnosti objektů, kolekce dat, apod.) v uživatelském rozhraní. Určitě vás napadne, že ne všechny zdroje a cíle jsou stejného typu. A máte samozřejmě pravdu. Také vás může napadnout, že je občas potřeba data nějakým způsobem upravit, než se dostanou ke svému cíli. Naštěstí existuje řešení: Ať na úpravu typu dat nebo na určitou datovou transformaci můžeme použít konvertor (Converter).
Co to tedy je? Konvertor (Converter) je třída zděděná z interface IValueConverter pro jednoduchý konvertor nebo z interface IMultiValueConverter pro transformace vyžadující více zdrojů dat. V dnešním dílu se budeme zabývat pouze jednoduchým konvertorem implementujícím interface IValueConverter. Tento interface obsahuje pouze dvě metody: Convert(...) pro převod ze zdroje do cíle a ConvertBack(...) pro převod z cíle do zdroje. Pro názornost je činnost konvertoru vysvětlena následujícím obrázkem:

Ve zkratce:
- Jako cíl DataBindingu můžeme použít pouze Dependency Property (a tu může obsahovat pouze Dependency object). Stručně tento základní mechanismus popisuje článek Dependency Properties (EN).
- Zdroj dat pro DataBinding může být libovolná vlastnost (Property) jakéhokoli objektu.
- Mezi zdroj a cíl DataBindingu se vloží konvertor, který může data upravit.
- Pro směr dat ze zdroje do cíle se používá metoda
Convert(...) - Pro opačný směr se používá
ConvertBack(...)
Příklad
Pro názornou ukázku jsem zvolil velmi jednoduchý příklad. Budeme chtít, aby se nějaký TextBox zneplatnil (IsEnabled = false), pokud je určitý CheckBox zaškrtnutý (IsChecked = true). Je zde vidět, proč pouze DataBinding nepomůže – je potřeba převést logiku zaškrtávání na opačnou hodnotu bool. Tedy něco jako: “value = !value”. Logika konvertoru je tedy velmi jednoduchá:
[System.Windows.Data.ValueConversion(typeof(bool), typeof(bool))]
public class InverseBoolConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("Target is not a boolean.");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
// pouze převod ze zdroje do cíle
throw new NotSupportedException();
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Ke kódu jenom pár poznámek:
- ValueConversionAttribute nemá na implementaci žádný vliv a slouží pouze pro vývojové nástroje, které mohou konvertor najít podle daných zdrojových a cílových typů. Konvertor bude fungovat i bez tohoto atributu.
- Protože při psaní XAMLu neprobíhá silně typová kontrola, je dobré ošetřit typy dat na vstupu konvertoru.
Po vytvoření konvertoru jej musíme nadefinovat v sekci Resources, abychom ho mohli dále použít. Pokud bychom chtěli konvertor používat v rámci celého elementu(objektu) Window, bude definice vypadat takto:
<Window.Resources>
<local:InverseBoolConverter x:Key="InverseConverter" />
</Window.Resources>
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Pokud bychom chtěli nyní použít vytvořenou instanci naší třídy InverseBoolConverter stačí se na ni odkázat pomocí jména a markup extension StaticResource - {StaticResource InverseConverter}.
Celý kód v XAMLu:
<Window x:Class="ConverterTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ConverterTest"
Title="Converter Test - Xaml.cz" Height="300" Width="300">
<Window.Resources>
<local:InverseBoolConverter x:Key="InverseConverter" />
</Window.Resources>
<StackPanel Margin="10">
<CheckBox x:Name="check1" Content="Zaškrtni mě :)" Padding="10,0,0,10" />
<TextBox MinLines="3" IsEnabled="{Binding ElementName=check1,Path=IsChecked,Converter={StaticResource InverseConverter}}">Textbox...</TextBox>
</StackPanel>
</Window>
Zde je názorně vidět použití konvertoru: Vlastnost IsEnabled TextBoxu je provázána DataBindingem s vlastností IsChecked [CheckBox](http://msdn.microsoft.com/en-us/library/system.windows.controls.checkbox.aspx)u, který je pojmenovaný “check1”. Mezi tuto vazbu se použil konvertor z přikladu pro otočení hodnoty bool, který je v sekci Resources nadefinován pod názvem “InverseConverter”.
Závěr
Možná z tohoto článku na první pohled nebyla zřejmá krása použití konvertoru pro tak jednoduchý příklad. Ale stačí, když tento InverseBoolConverter už jednou budeme mít v projektu a uvidíte, že se jeho použití najde vícekrát. A o elegantní znovupoužitelné řešení jde přece vždy. :)