Nejprve začněme shrnutím tříd triggerů a akcí triggeru, se kterými jsme se setkali v minulé a předminulé časti tohoto seriálu. Dále k nim ale ještě přidáme poslední typ objektu z namespace System.Windows.Interactivity, kterým je Behavior, o kterém bude řeč dnes.
Následující tabulka ukazuje srovnání těchto tří typů objektů:
A nyní k samotnému objektu Behavior.
Objekty typu Behavior se také přidávají k ovládacím prvkům ve View podobně jako triggery, konkrétně se přidávají do XAML elementu i:Interaction.Behaviors. Behavior ale obsahuje v jedné třídě jak logiku pro vyvolávání akcí, tak i vlastní implementaci manipulující s daným prvkem (akce je resp. jsou inicializovány samotným ovládacím prvkem). Zjednodušeně tedy můžeme říci, že aplikováním Behavioru měníme chování daného controlu (odtud i název “Behavior”).
Na rozdíl od triggerů mechanizmus behavioru již neslouží k interakci mezi View s ViewModelem či naopak. Přesto je stále v MVVM využitelný, i když není samozřejmě vázaný pouze na použití v MVVM. Behavior umožňuje totiž implementovat a zapouzdřit obecnou funkcionalitu, kterou lze ve View opakovaně a pouze deklarativně používat. To opět snižuje nutnost codebehindu a těsné vazby mezi implementací funkcionality a konkrétním ovládacím prvkem, což je přesně v souladu s ostatními vlastnostmi MVVM.
Jak se behavior implementuje a používá si ukážeme na poměrně jednoduchém příkladu. Předpokládejme, že na formuláři s přehledem firem máme ComboBox pro výběr filtru s výčtem hodnot “Vše”, “Pouze plátci DPH”, “Pouze neplátci DPH”. V XAML to vypadá takto:
<ComboBox Margin="0,0,8,0" Width="155" Height="24" SelectedIndex="{Binding PlatceDPHSelectedIndex, Mode=TwoWay}">
<ComboBoxItem>Vše</ComboBoxItem>
<ComboBoxItem>Pouze plátci DPH</ComboBoxItem>
<ComboBoxItem>Pouze neplátci DPH</ComboBoxItem>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<interactivity:EventToCommand Command="{Binding RefreshCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
To co chceme udělat je doplnit funkcionalitu, že při stisku klávesy DELETE nebo BACKSPACE se tento prvek nastaví na první hodnotu tj. hodnotu “Vše”. Jak by se to udělalo v codebehind je asi jasné, jako behavior to ale nebude o moc těžší a navíc bez nutnosti pojmenování prvku.
Třídu našeho behavioru pojmenujeme ComboBoxDeleteBehavior.
/// <summary>
/// Set first item in ComboBox on delete
/// </summary>
public class ComboBoxDeleteBehavior : Behavior<ComboBox>
{
#region action methods
/// <summary>
/// Overrides OnAttached
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.KeyDown += new KeyEventHandler(ComboBox_KeyDown);
}
/// <summary>
/// Overrides OnDetaching
/// </summary>
protected override void OnDetaching()
{
this.AssociatedObject.KeyDown -= new KeyEventHandler(ComboBox_KeyDown);
base.OnDetaching();
}
#endregion
#region private member functions
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete || e.Key == Key.Back)
{
this.AssociatedObject.SelectedIndex = 0;
}
}
#endregion
}
Třída dědí ze základní generické třídy Behavior<T>, kde T je typ ovládacího prvku, pro který je behavior určený, tedy ComboBox v našem případě. Tohoto typu je pak dostupná vlastnost AssociatedObject, která bude vracet instanci controlu, pro konkrétní použití třídy.
Ve vlastní implementaci je třeba přepsat metody OnAttached a OnDetaching pro registraci a odregistraci příslušných událostí, v našem případě události KeyDown. V obsluze této události pak pro klávesy DELETE/BACKSPACE již jen provedeme, opět pomoci vlastnost AssociatedObject, vlastní manipulaci s naším prvkem tj. nastavení SelectedIndex na hodnotu 0.
Použití behavioru provedeme přímo v XAML našeho prvku:
<ComboBox Margin="0,0,8,0" Width="155" Height="24" SelectedIndex="{Binding PlatceDPHSelectedIndex, Mode=TwoWay}">
<ComboBoxItem>Vše</ComboBoxItem>
<ComboBoxItem>Pouze plátci DPH</ComboBoxItem>
<ComboBoxItem>Pouze neplátci DPH</ComboBoxItem>
<i:Interaction.Behaviors>
<interactivity:ComboBoxDeleteBehavior/>
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<interactivity:EventToCommand Command="{Binding RefreshCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Obdobně vypadá i implementace behavioru, který na stejné klávesy aktuálně vybranou položku ComboBoxu odstraní, což se může hodit pro výběr nepovinné položky.
/// <summary>
/// Clear ComboBox on delete
/// </summary>
public class ComboBoxNullDeleteBehavior : Behavior<ComboBox>
{
#region action methods
/// <summary>
/// Overrides OnAttached
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.KeyDown += new KeyEventHandler(ComboBox_KeyDown);
}
/// <summary>
/// Overrides OnDetaching
/// </summary>
protected override void OnDetaching()
{
this.AssociatedObject.KeyDown -= new KeyEventHandler(ComboBox_KeyDown);
base.OnDetaching();
}
#endregion
#region private member functions
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete || e.Key == Key.Back)
{
this.AssociatedObject.SelectedIndex = -1;
}
}
#endregion
}
A příklad použití tohoto behavioru u výběru měny z kurzovního lístku, pro případ kdy je vstupní měna jiná než měna výchozí:
<ComboBox x:Name="cboMena" Margin="0,0,6,0" Height="24" Width="60" ItemsSource="{Binding Kurzy}" ToolTipService.ToolTip="{Binding ElementName=cboMena, Path=SelectedItem.mn_Nazev}"
IsEnabled="{Binding ReadOnly, Converter={StaticResource NegateConverter}}"
DisplayMemberPath="mn_Kod" SelectedItem="{Binding MenaZ, Mode=TwoWay}" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<interactivity:ComboBoxNullDeleteBehavior/>
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding NotifyHasChangesCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Dalším příkladem behavioru může být třída SetLanguageBehavior pro nastavení správné kultury u prvků tooltip nebo popup. Jeho implementace i použití bylo popsáno v tomto dřívějším článku.
A poslední příklad je zablokování pravého tlačítka myši u prvku typu Popup, které bylo popsáno zde.
Tím tuto sérii prozatím přerušíme, v budoucnu se ale k tématu MVVM určitě zase někdy vrátíme.