« Attached behavior » : différence entre les versions
(19 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 18 : | Ligne 18 : | ||
= InvokeCommandAction = | = InvokeCommandAction = | ||
Lie un événement UI à une commande du VM.<br/> | |||
Ici on execute la commande {{boxx|SelectionChangedCmd}} lorsque l’évènement {{boxx|SelectionChanged}} est lancé. | Ici on execute la commande {{boxx|SelectionChangedCmd}} lorsque l’évènement {{boxx|SelectionChanged}} est lancé. | ||
Ligne 38 : | Ligne 38 : | ||
= CallMethodAction = | = CallMethodAction = | ||
Lie un événement UI à une méthode du VM.<br/> | |||
La méthode doit être publique et ne pas avoir d'arguments. | La méthode du VM doit être publique et ne pas avoir d'arguments. | ||
<filebox fn='MainWindow.xaml'> | <filebox fn='MainWindow.xaml'> | ||
<Window xmlns: | <Window xmlns:b="http://schemas.microsoft.com/xaml/behaviors"> | ||
< | <b:Interaction.Triggers> | ||
< | <b:EventTrigger EventName="Loaded"> | ||
< | <b:CallMethodAction TargetObject="{Binding Mode=OneTime}" | ||
MethodName="OnViewLoaded" /> | |||
</ | </b:EventTrigger> | ||
</ | </b:Interaction.Triggers> | ||
</filebox> | </filebox> | ||
Ligne 55 : | Ligne 55 : | ||
public void OnViewLoaded() | public void OnViewLoaded() | ||
{ } | { } | ||
</filebox> | |||
= TriggerAction = | |||
Lie un événement UI à du code, équivalent du code behind. | |||
<filebox fn='MainWindow.xaml'> | |||
<Window xmlns:b="http://schemas.microsoft.com/xaml/behaviors"> | |||
<b:Interaction.Triggers> | |||
<b:EventTrigger EventName="Click"> | |||
<triggers:MyTriggerAction /> | |||
</b:EventTrigger> | |||
</b:Interaction.Triggers> | |||
</filebox> | |||
<filebox fn='MyTriggerAction.cs'> | |||
public sealed class MyTriggerAction : TriggerAction<Button> | |||
{ | |||
protected override void Invoke(object parameter) | |||
{ | |||
var args = parameter as RoutedEventArgs; | |||
if (args == null) | |||
return; | |||
var tabControl = VisualTreeHelperPlus.GetParent<TabControl>(args.OriginalSource as DependencyObject); | |||
} | |||
} | |||
</filebox> | </filebox> | ||
Ligne 110 : | Ligne 137 : | ||
= Custom Behavior = | = Custom Behavior = | ||
Ajoute un comportement à un composant UI, comme lier un événement UI à l’exécution de code. | |||
<filebox fn='NumericInputBehavior.cs'> | <filebox fn='NumericInputBehavior.cs'> | ||
// un behavior pour les TextBox | // un behavior pour les TextBox | ||
public class NumericInputBehavior : Behavior<TextBox> | public class NumericInputBehavior : Behavior<TextBox> | ||
Ligne 127 : | Ligne 152 : | ||
set => SetValue(EnableDecimalInputProperty, value); | set => SetValue(EnableDecimalInputProperty, value); | ||
} | } | ||
public static readonly DependencyProperty EnableDecimalInputProperty = | public static readonly DependencyProperty EnableDecimalInputProperty = | ||
DependencyProperty.Register( | DependencyProperty.Register( | ||
typeof(NumericInputBehavior), new PropertyMetadata(false)); | nameof(EnableDecimalInput), | ||
typeof(bool), | |||
typeof(NumericInputBehavior), | |||
new PropertyMetadata(false)); | |||
// called when XAML is parsed and behavior is added | // called when XAML is parsed and behavior is added | ||
Ligne 161 : | Ligne 189 : | ||
<filebox fn='MyWindow.xaml'> | <filebox fn='MyWindow.xaml'> | ||
<Window xmlns: | <Window xmlns:b="http://schemas.microsoft.com/xaml/behaviors"> | ||
<TextBox> | <TextBox> | ||
< | <b:Interaction.Behaviors> | ||
<local:MyTextBoxBehavior EnableDecimalInput="True" /> | <local:MyTextBoxBehavior EnableDecimalInput="True" /> | ||
</ | </b:Interaction.Behaviors> | ||
</TextBox> | </TextBox> | ||
</filebox> | </filebox> | ||
* [[ItemsControl,_ListBox,_ComboBox,_TreeView#ScrollToSelectedItemBehavior|ScrollToSelectedItemBehavior]] | |||
* [[Prism_navigation_with_tabcontrol#RequestNavigateOnLoadedBehavior|RequestNavigateOnLoadedBehavior]] | |||
== Attacher le behavior via un style == | == Attacher le behavior via un style == |
Dernière version du 20 octobre 2021 à 19:59
Définition
Un behavior est une surcouche d'attached property.
L'AB est définie dans une classe non-statique pour pouvoir ensuite être attachée à un DependencyObject.
L'AB correspond a une fonctionnalité autonome qui peut s'ajouter à des DependencyObjects pour étendre leurs fonctionnalités.
Paquet NuGet à installer
Microsoft.Xaml.Behaviors.Wpf
Le plus récent: Exemples
- Microsoft.Xaml.Behaviors
- Behavior
System.Windows.Interactivity.WPF / Expression.Blend.Sdk.WPF
- Microsoft.Expression.Interactions
- MouseDragElementBehavior
- System.Windows.Interactivity
- Behavior
InvokeCommandAction
Lie un événement UI à une commande du VM.
Ici on execute la commande SelectionChangedCmd lorsque l’évènement SelectionChanged est lancé.
MainWindow.xaml |
<Window xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"> <Grid> <ComboBox x:Name="cb"> <ComboBoxItem>Item #1</ComboBoxItem> <ComboBoxItem IsSelected="True">Item #2</ComboBoxItem> <Behaviors:Interaction.Triggers> <Behaviors:EventTrigger EventName="SelectionChanged"> <Behaviors:InvokeCommandAction Command="{Binding ChangeColorCmd}" CommandParameter="{Binding ElementName=cb}" /> </Behaviors:EventTrigger> </Behaviors:Interaction.Triggers> </ComboBox> |
CallMethodAction
Lie un événement UI à une méthode du VM.
La méthode du VM doit être publique et ne pas avoir d'arguments.
MainWindow.xaml |
<Window xmlns:b="http://schemas.microsoft.com/xaml/behaviors"> <b:Interaction.Triggers> <b:EventTrigger EventName="Loaded"> <b:CallMethodAction TargetObject="{Binding Mode=OneTime}" MethodName="OnViewLoaded" /> </b:EventTrigger> </b:Interaction.Triggers> |
MainViewModel.cs |
public void OnViewLoaded() { } |
TriggerAction
Lie un événement UI à du code, équivalent du code behind.
MainWindow.xaml |
<Window xmlns:b="http://schemas.microsoft.com/xaml/behaviors"> <b:Interaction.Triggers> <b:EventTrigger EventName="Click"> <triggers:MyTriggerAction /> </b:EventTrigger> </b:Interaction.Triggers> |
MyTriggerAction.cs |
public sealed class MyTriggerAction : TriggerAction<Button> { protected override void Invoke(object parameter) { var args = parameter as RoutedEventArgs; if (args == null) return; var tabControl = VisualTreeHelperPlus.GetParent<TabControl>(args.OriginalSource as DependencyObject); } } |
ChangePropertyAction
Permet de modifier un propriété suite à un évènement ou au changement de valeur d'une propriété.
MainWindow.xaml |
<Window xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"> <RadioButton Content="Good" VerticalAlignment="Stretch" IsChecked="True" x:Name="rb1"> <Behaviors:Interaction.Triggers> <Behaviors:DataTrigger Binding="{Binding IsChecked, ElementName=rb1}" Value="True"> <Behaviors:ChangePropertyAction TargetObject="{Binding}" PropertyName="CustomerStatus"> <Behaviors:ChangePropertyAction.Value> <local:CustomerStatus>Good</local:CustomerStatus> </Behaviors:ChangePropertyAction.Value> </Behaviors:ChangePropertyAction> </Behaviors:DataTrigger> </Behaviors:Interaction.Triggers> </RadioButton> <RadioButton Content="Bad" VerticalAlignment="Stretch" x:Name="rb2"> <Behaviors:Interaction.Triggers> <Behaviors:DataTrigger Binding="{Binding IsChecked, ElementName=rb2}" Value="True"> <Behaviors:ChangePropertyAction TargetObject="{Binding}" PropertyName="CustomerStatus"> <Behaviors:ChangePropertyAction.Value> <local:CustomerStatus>Bad</local:CustomerStatus> </Behaviors:ChangePropertyAction.Value> </Behaviors:ChangePropertyAction> </Behaviors:DataTrigger> </Behaviors:Interaction.Triggers> </RadioButton> |
MainViewModel.cs |
public class MainViewModel : BindableBase { public CustomerStatus CustomerStatus { get; set; } } public enum CustomerStatus { Good, Bad } |
Custom Behavior
Ajoute un comportement à un composant UI, comme lier un événement UI à l’exécution de code.
NumericInputBehavior.cs |
// un behavior pour les TextBox public class NumericInputBehavior : Behavior<TextBox> { private static readonly Regex decimalRegex = new Regex($@"^\d+{CultureInfo.CurrentCulture.NumberFormat.PercentDecimalSeparator}?\d*$", RegexOptions.Compiled); // dependency property public bool EnableDecimalInput { get => (bool) GetValue(EnableDecimalInputProperty); set => SetValue(EnableDecimalInputProperty, value); } public static readonly DependencyProperty EnableDecimalInputProperty = DependencyProperty.Register( nameof(EnableDecimalInput), typeof(bool), typeof(NumericInputBehavior), new PropertyMetadata(false)); // called when XAML is parsed and behavior is added protected override void OnAttached() { // AssociatedObject correspond au control attachant le behavior this.AssociatedObject.PreviewTextInput += OnPreviewTextInput; } // never called unless programmatic code removes behavior from parent collection or calls Detach protected override void OnDetaching() { this.AssociatedObject.PreviewTextInput -= OnPreviewTextInput; } private void OnPreviewTextInput(object sender, TextCompositionEventArgs e) { if (EnableDecimalInput) { if (!decimalRegex.IsMatch(AssociatedObject.Text + e.Text)) e.Handled = true; } else { if (!Char.IsDigit(e.Text[0])) e.Handled = true; } } } |
MyWindow.xaml |
<Window xmlns:b="http://schemas.microsoft.com/xaml/behaviors"> <TextBox> <b:Interaction.Behaviors> <local:MyTextBoxBehavior EnableDecimalInput="True" /> </b:Interaction.Behaviors> </TextBox> |
Attacher le behavior via un style
MyWindow.xaml |
<telerik:RadGridView ItemsSource="{Binding Items}" ValidatesOnDataErrors="InViewMode"> <telerik:RadGridView.RowStyle> <Style TargetType="telerik:GridViewRow" BasedOn="{StaticResource {x:Type telerik:GridViewRow}}"> <Setter Property="local:MyBehavior.IsEnabled" Value="True" /> </Style> </telerik:RadGridView.RowStyle> </telerik:RadGridView> |
MyBehavior.cs |
class MyBehavior : Behavior<GridViewRow> { protected override void OnAttached() { // AssociatedObject correspond au control attachant le behavior base.OnAttached(); } protected override void OnDetaching() { base.OnDetaching(); } public static bool GetIsEnabled(DependencyObject element) { return (bool) element.GetValue(IsEnabledProperty); } public static void SetIsEnabled(DependencyObject element, bool value) { element.SetValue(IsEnabledProperty, value); } public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached( "IsEnabled", typeof(bool), typeof(MyBehavior), new PropertyMetadata(default(bool), OnIsEnabledChanged)); private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behaviors = Interaction.GetBehaviors(d); if ((bool)e.NewValue) { if (!behaviors.OfType<MyBehavior>().Any()) { behaviors.Add(new MyBehavior()); } } else { behaviors.RemoveAll(b => b is MyBehavior); } } } |
Custom behavior vs custom control
Utilisé de préférence un custom behavior, sauf si:
- il faut accéder aux membres protected
- il faut modifier le control template
Custom Trigger
Comme le custom behavior mais définit seulement les conditions de déclenchement pas le behavior. Celui-ci est définit dans le xaml à chaque déclaration du trigger.
EmployeeTrigger.cs |
public class EmployeeTrigger : TriggerBase<TextBlock> { protected override void OnAttached() { AssociatedObject.Loaded += OnLoaded; AssociatedObject.Unloaded += OnUnloaded; } protected override void OnDetaching() { AssociatedObject.Loaded -= OnLoaded; AssociatedObject.Unloaded -= OnUnloaded; } private void OnLoaded(object sender, RoutedEventArgs e) { AssociatedObject.DataContextChanged += OnDataContextChanged; Refresh(); } private void OnUnloaded(object sender, RoutedEventArgs e) { AssociatedObject.DataContextChanged -= OnDataContextChanged; } private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { Refresh(); } private void Refresh() { if (AssociatedObject.DataContext is Employee employee && employee.IsNew) { base.InvokeActions(null); } } } |
MyWindow.xaml |
<Window xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"> <ItemsControl ItemsSource="{Binding Employees}"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}"> <Behaviors:Interaction.Triggers> <local:EmployeeTrigger> <Behaviors:ChangePropertyAction PropertyName="Foreground"> <Behaviors:ChangePropertyAction.Value> <SolidColorBrush Color="Red" /> </Behaviors:ChangePropertyAction.Value> </Behaviors:ChangePropertyAction> </local:EmployeeTrigger> </Behaviors:Interaction.Triggers> |