wpf-17442-mvvm-framework-behaviors.md
Behaviors can attach to visual controls and customize them (or extend their capabilities). Use this customization technique if you want your application to follow the MVVM pattern (no code-behind in Views). Our WPF product line ships with a wide variety of pre-defined Behaviors. You can find the list later in this topic. If no-built in Behavior meets your requirements, you can implement a custom class as described in the last section.
Run Demo: Behaviors Module in the WPF MVVM Demo
The Interactivity.Interaction class implements the Behaviors attached property. To use a Behavior, place it in the following collection:
<TextBox Text="This control is focused on startup">
<dxmvvm:Interaction.Behaviors>
<dxmvvm:FocusBehavior/>
<!--Other Behaviors-->
</dxmvvm:Interaction.Behaviors>
</TextBox>
Behaviors may contain dependency properties:
<ListBox ...>
<dxmvvm:Interaction.Behaviors>
<dxmvvm:EventToCommand EventName="MouseDoubleClick" Command="{Binding EditCommand}">
<dxmvvm:EventToCommand.EventArgsConverter>
<Common:ListBoxEventArgsConverter/>
</dxmvvm:EventToCommand.EventArgsConverter>
</dxmvvm:EventToCommand>
</dxmvvm:Interaction.Behaviors>
...
</ListBox>
The following code sample attaches a BarSubItemThemeSelectorBehavior to a BarSubItem in code-behind. This example hides touch themes from the theme selector (sets the ShowTouchThemes property to false):
<Window ...
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon">
<StackPanel>
<dxr:RibbonControl>
<dxr:RibbonDefaultPageCategory>
<dxr:RibbonPage Caption="Home">
<dxr:RibbonPageGroup Caption="File">
<dxb:BarSubItem x:Name="barsubitem1"/>
</dxr:RibbonPageGroup>
</dxr:RibbonPage>
</dxr:RibbonDefaultPageCategory>
</dxr:RibbonControl>
</StackPanel>
</Window>
using DevExpress.Mvvm.UI.Interactivity;
using DevExpress.Xpf.Bars;
// ...
public MainWindow() {
InitializeComponent();
Interaction.GetBehaviors(barsubitem1).Add(new BarSubItemThemeSelectorBehavior {ShowTouchThemes=false });
}
Imports DevExpress.Mvvm.UI.Interactivity
Imports DevExpress.Xpf.Bars
Public Sub New()
InitializeComponent()
Interaction.GetBehaviors(barsubitem1).Add(New BarSubItemThemeSelectorBehavior With {
.ShowTouchThemes = False
})
End Sub
You can attach a behavior to all objects of a specific type (and descendants) on a Window or a UserControl. To do that, place your declaration in Style. The following code sample uses the BehaviorsTemplate attached property to add the KeyToCommand behavior to each element of the GridControl type:
<Style TargetType="dxg:GridControl">
<Setter Property="dxmvvm:Interaction.BehaviorsTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl>
<dxmvvm:KeyToCommand KeyGesture="CTRL+U" Command="{Binding UpdateCommand}"/>
</ContentControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Place multiple behaviors to the ItemsControl element. The following code sample adds KeyToCommand and EventToCommand behaviors to each element of the GridControl type and its descendants:
<Style TargetType="dxg:GridControl">
<Setter Property="dxmvvm:Interaction.BehaviorsTemplate">
<Setter.Value>
<DataTemplate>
<ItemsControl>
<dxmvvm:KeyToCommand KeyGesture="CTRL+U" Command="{Binding UpdateCommand}"/>
<dxmvvm:EventToCommand EventName="MouseDoubleClick" Command="{Binding EditCommand}">
<dxmvvm:EventToCommand.EventArgsConverter>
<Common:ListBoxEventArgsConverter/>
</dxmvvm:EventToCommand.EventArgsConverter>
</dxmvvm:EventToCommand>
</ItemsControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
DevExpress WPF Product Line includes the following predefined behaviors:
EventToCommand Executes a command in response to a raised event.KeyToCommand Allows you to bind a KeyGesture to a command.FocusBehavior Focuses a UI control without code-behind.ValidationErrorsHostBehavior Tracks validation errors within a UI container.ConfirmationBehavior Shows a confirmation message box before an application executes the specified command. Users can confirm or cancel the operation.DependencyPropertyBehavior Allows you to bind a ViewModel property to a control’s non-dependency property.EnumItemsSourceBehavior Binds a control’s ItemsSource property to an enumeration.CompositeCommandBehavior Allows you to aggregate multiple commands and execute them in a specific order.FunctionBindingBehavior Binds a ViewModel function’s result to a control’s property.MethodToCommandBehavior Binds a method of a ViewModel or control to a Command property of a control on a View.ReadOnlyDependencyPropertyBindingBehavior Allows you to bind read-only dependency and attached properties to a ViewModel’s properties.CurrentWindowSerializationBehavior Allows you to serialize/deserialize settings (size and state) of the associated view (or window).BarSubItemThemeSelectorBehavior Populates the associated BarSubItem with available themes and allows you to choose the application’s theme.BarSplitItemThemeSelectorBehavior Populates the associated BarSplitButtonItem with available themes and allows you to choose the application’s theme.RibbonGalleryItemThemeSelectorBehavior Populates the associated RibbonGalleryBarItem with available themes and allows you to choose the application’s theme.GalleryThemeSelectorBehavior Populates the associated GalleryControl with available themes and allows you to choose the application’s theme.HamburgerSubMenuThemeSelectorBehavior Populates the associated HamburgerSubMenu with available themes and allows you to choose the application’s theme.RibbonGalleryItemThemePaletteSelectorBehavior Populates the associated RibbonGalleryBarItem with the available palettes and allows you to choose the application theme’s palette.
Each Behavior is a Behavior<T> class descendant. The T parameter defines the associated control type. The following code sample specifies a Behavior that you can apply only to a TextBox control or its descendants:
public class ValidationBehavior : Behavior<TextBox> // TextBox is the AssociatedObject type
{
protected override void OnAttached() {
base.OnAttached();
// subscribe to the AssociatedObject events
}
protected override void OnDetaching() {
// unsubscribe from the AssociatedObject events
base.OnDetaching();
}
}
Class SurroundingClass
Public Class ValidationBehavior
' TextBox is the AssociatedObject type
Inherits Behavior(Of TextBox)
End Class
Protected Overrides Sub OnAttached()
' subscribe to the AssociatedObject events
MyBase.OnAttached()
End Sub
Protected Overrides Sub OnDetaching()
'unsubscribe from the AssociatedObject events
MyBase.OnDetaching()
End Sub
End Class
The Behavior<T> class contains the AssociatedObject property. DevExpress MVVM Framework specifies this property when you add a Behavior to the Behaviors collection.
After the AssociatedObject is specified, DevExpress MVVM Framework invokes the virtual OnAttached method. You can override this method to subscribe to AssociatedObject’s events and initialize its properties.
To unsubscribe from events, you can use the virtual OnDetaching method. DevExpress MVVM Framework invokes when the Behavior is destroyed.
public class ValidationBehavior : Behavior<TextBox> {
public static readonly DependencyProperty ValidForegroundProperty =
DependencyProperty.Register("ValidForeground", typeof(Brush), typeof(ValidationBehavior),
new PropertyMetadata(new SolidColorBrush(Colors.Black), (d, e) => ((ValidationBehavior)d).Update()));
public static readonly DependencyProperty InvalidForegroundProperty =
DependencyProperty.Register("InvalidForeground", typeof(Brush), typeof(ValidationBehavior),
new PropertyMetadata(new SolidColorBrush(Colors.Red), (d,e) => ((ValidationBehavior)d).Update()));
public static readonly DependencyProperty InvalidValueProperty =
DependencyProperty.Register("InvalidValue", typeof(string), typeof(ValidationBehavior),
new PropertyMetadata(string.Empty, (d, e) => ((ValidationBehavior)d).Update()));
public Brush ValidForeground {
get { return (Brush)GetValue(ValidForegroundProperty); }
set { SetValue(ValidForegroundProperty, value); }
}
public Brush InvalidForeground {
get { return (Brush)GetValue(InvalidForegroundProperty); }
set { SetValue(InvalidForegroundProperty, value); }
}
public string InvalidValue {
get { return (string)GetValue(InvalidValueProperty); }
set { SetValue(InvalidValueProperty, value); }
}
protected override void OnAttached() {
base.OnAttached();
AssociatedObject.TextChanged += OnAssociatedObjectTextChanged;
Update();
}
protected override void OnDetaching() {
AssociatedObject.TextChanged -= OnAssociatedObjectTextChanged;
base.OnDetaching();
}
void OnAssociatedObjectTextChanged(object sender, TextChangedEventArgs e) {
Update();
}
void Update() {
if(AssociatedObject == null) return;
if(AssociatedObject.Text == InvalidValue)
AssociatedObject.Foreground = InvalidForeground;
else AssociatedObject.Foreground = ValidForeground;
}
}
Public Class ValidationBehavior
Inherits Behavior(Of TextBox)
Public Shared ReadOnly ValidForegroundProperty As DependencyProperty = DependencyProperty.Register("ValidForeground", GetType(Brush), GetType(ValidationBehavior), New PropertyMetadata(New SolidColorBrush(Colors.Black), Sub(d, e) CType(d, ValidationBehavior).Update()))
Public Shared ReadOnly InvalidForegroundProperty As DependencyProperty = DependencyProperty.Register("InvalidForeground", GetType(Brush), GetType(ValidationBehavior), New PropertyMetadata(New SolidColorBrush(Colors.Red), Sub(d,e) CType(d, ValidationBehavior).Update()))
Public Shared ReadOnly InvalidValueProperty As DependencyProperty = DependencyProperty.Register("InvalidValue", GetType(String), GetType(ValidationBehavior), New PropertyMetadata(String.Empty, Sub(d, e) CType(d, ValidationBehavior).Update()))
Public Property ValidForeground() As Brush
Get
Return CType(GetValue(ValidForegroundProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(ValidForegroundProperty, value)
End Set
End Property
Public Property InvalidForeground() As Brush
Get
Return CType(GetValue(InvalidForegroundProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(InvalidForegroundProperty, value)
End Set
End Property
Public Property InvalidValue() As String
Get
Return CStr(GetValue(InvalidValueProperty))
End Get
Set(ByVal value As String)
SetValue(InvalidValueProperty, value)
End Set
End Property
Protected Overrides Sub OnAttached()
MyBase.OnAttached()
AddHandler AssociatedObject.TextChanged, AddressOf OnAssociatedObjectTextChanged
Update()
End Sub
Protected Overrides Sub OnDetaching()
RemoveHandler AssociatedObject.TextChanged, AddressOf OnAssociatedObjectTextChanged
MyBase.OnDetaching()
End Sub
Private Sub OnAssociatedObjectTextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
Update()
End Sub
Private Sub Update()
If AssociatedObject Is Nothing Then
Return
End If
If AssociatedObject.Text = InvalidValue Then
AssociatedObject.Foreground = InvalidForeground
Else
AssociatedObject.Foreground = ValidForeground
End If
End Sub
End Class
<TextBox Text="Text">
<dxmvvm:Interaction.Behaviors>
<Behaviors:ValidationBehavior ValidForeground="{Binding ValidBrush}"
InvalidForeground="{Binding InvalidBrush}"
InvalidValue="{Binding InvalidValue}"/>
</dxmvvm:Interaction.Behaviors>
</TextBox>
View Example: WPF MVVM Behaviors - Create a Custom Attached Behavior