Back to Devexpress

MVVM Behaviors

windowsforms-113975-cross-platform-app-development-winforms-mvvm-concepts-behaviors.md

latest16.9 KB
Original Source

MVVM Behaviors

  • Dec 09, 2024
  • 8 minutes to read

Behaviors add extra functionality to an object without modifying it. For instance, a close button closes a tab or a form and additionally displays a confirmation dialog. You can use behaviors in MVVM applications to accomplish this.

Confirmation Behavior

Note

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Behaviors

Example: Simple Behaviors

25.2 Demo Center: Launch the demo

A simple confirmation behavior can only be attached to a cancelable event (for example, form closing or edit value changing). To implement custom behavior, define a class that derives from the ConfirmationBehavior class. It contains two virtual string properties for the confirmation message box’s title and text. Override these properties to assign your text strings. The class constructor should inherit the base class constructor with a parameter equal to the name of the event that triggers this behavior.

csharp
public class FormCloseBehavior : ConfirmationBehavior<FormClosingEventArgs> {
    public FormCloseBehavior() : base("FormClosing") { }

    protected override string GetConfirmationCaption() {
        return "Confirm exit";
    }

    protected override string GetConfirmationText() {
        return "Do you really want to exit the application?";
    }
}
vb
Public Class FormCloseBehavior
    Inherits ConfirmationBehavior(Of FormClosingEventArgs)

    Public Sub New()
        MyBase.New("FormClosing")
    End Sub

    Protected Overrides Function GetConfirmationCaption() As String
        Return "Confirm exit"
    End Function

    Protected Overrides Function GetConfirmationText() As String
        Return "Do you want to exit the application?"
    End Function
End Class

Tip

The ConfirmationBehavior class’s last virtual property is a Boolean Confirm property. You can also override it to specify the related event’s cancel conditions.

csharp
//Inverted confirmation logic
protected override bool Confirm() {
    if(MessageBox.Show(GetConfirmationText(), GetConfirmationCaption(), MessageBoxButtons.YesNo) == DialogResult.Yes) return false;
    else return true;
}
vb
'Inverted confirmation logic
Protected Overrides Function Confirm() As Boolean
    If MessageBox.Show(GetConfirmationText(), GetConfirmationCaption(), MessageBoxButtons.YesNo) = DialogResult.Yes Then
        Return False
    Else
        Return True
    End If
End Function

Use the MvvmContext component’s API to attach this behavior to the target UI element.

csharp
//View
mvvmContext.AttachBehavior<FormCloseBehavior>(this);
vb
'View
mvvmContext.AttachBehavior(Of FormCloseBehavior)(Me)

You can simplify this process by moving the behavior declaration from a separate class to a generic one.

csharp
mvvmContext1.AttachBehavior<ConfirmationBehavior<ChangingEventArgs>>(checkEdit1, behavior => {
    behavior.Caption = "CheckEdit State Changing";
    behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?";
    behavior.Buttons = ConfirmationButtons.YesNo;
    behavior.ShowQuestionIcon = true;
}, "EditValueChanging");
vb
mvvmContext1.AttachBehavior(Of ConfirmationBehavior(Of ChangingEventArgs))(checkEdit1, Sub(behavior)
    behavior.Caption = "CheckEdit State Changing"
    behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?"
    behavior.Buttons = ConfirmationButtons.YesNo
    behavior.ShowQuestionIcon = True
End Sub, "EditValueChanging")

Fluent API is also supported.

csharp
mvvmContext1.WithEvent<ChangingEventArgs>(checkEdit1, "EditValueChanging").Confirmation(behavior => {
    behavior.Caption = "CheckEdit State changing";
    behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?";
});
vb
mvvmContext1.WithEvent(Of ChangingEventArgs)(checkEdit1, "EditValueChanging").Confirmation(Sub(behavior)
    behavior.Caption = "CheckEdit State changing"
    behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?"
End Sub)

Event-To-Command Behaviors

Note

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Behaviors

Example: Event-To-Command Behavior

25.2 Demo Center: Launch the demo

Event-to-command behaviors fit all the remaining events if a confirmation behavior requires an event that receives CancelEventArgs type arguments. This behavior binds a command to a target UI element, and when this element fires the required event, the command executes. This can be used for:

  • Third-party UI elements that do not implement the ISupportCommandBinding interface and thus cannot be bound using the MvvmContext component’s API (the mvvmContext.BindCommand method);
  • DevExpress controls that require extra functionality. For instance, if you need a SimpleButton to do something on the MouseHover event.

Event-to-command behaviors are implemented similarly to confirmation behaviors. Define a separate class that derives from the DevExpress EventToCommandBehavior class. In the class constructor, specify the target event name and the command that should be executed on this event.

csharp
public class ClickToSayHello : DevExpress.Utils.MVVM.EventToCommandBehavior<ViewModel, EventArgs> {
    public ClickToSayHello()
        : base("Click", x => x.SayHello()) {
    }
}
vb
Public Class ClickToSayHello
    Inherits DevExpress.Utils.MVVM.EventToCommandBehavior(Of ViewModel, EventArgs)
    Public Sub New()
        MyBase.New("Click", Function(x) x.SayHello())
    End Sub
End Class

Attaching event-to-command behaviors is identical to confirmation behaviors.

csharp
mvvmContext.AttachBehavior<ClickToSayHello>(thirdPartyButton);
vb
mvvmContext.AttachBehavior(Of ClickToSayHello)(thirdPartyButton)

Fluent API allows you to implement event-to-command behaviors without creating separate classes.

csharp
mvvmContext.WithEvent<ViewModel, EventArgs>(thirdPartyButton, "Click").EventToCommand(x => x.SayHello());
vb
mvvmContext.WithEvent(Of ViewModel, EventArgs)(thirdPartyButton, "Click").EventToCommand(Function(x) x.SayHello())

Key-To-Command and Keys-To-Command Behaviors

Note

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Behaviors

Example: Key-To-Command and Keys-To-Command Behaviors

25.2 Demo Center: Launch the demo

These behaviors allow you to execute commands when end-users press specific keyboard keys.

Binding a single keyboard shortcut

The sample ViewModel below defines the “OnAKey” and “OnAltKey” commands that display service-based notifications.

csharp
public class KeyAwareViewModel {
    protected IMessageBoxService MessageBoxService {
        get { return this.GetService<IMessageBoxService>(); }
    }
    public void OnAKey() {
        MessageBoxService.ShowMessage("Key Command: A");
    }
    public void OnAltAKey() {
        MessageBoxService.ShowMessage("Key Command: Alt+A");
    }
}
vb
Public Class KeyAwareViewModel
    Protected ReadOnly Property MessageBoxService() As IMessageBoxService
        Get
            Return Me.GetService(Of IMessageBoxService)()
        End Get
    End Property
    Public Sub OnAKey()
        MessageBoxService.ShowMessage("Key Command: A")
    End Sub
    Public Sub OnAltAKey()
        MessageBoxService.ShowMessage("Key Command: Alt+A")
    End Sub
End Class

Use the following fluent API methods of the MvvmContext component to bind these commands to related keys:

|

WithKey

|

The first method parameter is a UI element that must be focused when end-users press certain keys. The second parameter is a key combination.

| |

KeyToCommand

|

Refers to a command that should be executed.

|

The code below binds the “OnAKey” and “OnAltKey” commands to “A” and “Alt+A” keys respectively. Pressing both key combinations fires related commands only if the memo edit control currently has focus.

csharp
mvvmContext.ViewModelType = typeof(KeyAwareViewModel);
    // Binding the "A" key
    mvvmContext.OfType<KeyAwareViewModel>()
        .WithKey(memo, Keys.A)
        .KeyToCommand(x => x.OnAKey());
    // Binding the "Alt+A" shortcut
    mvvmContext.OfType<KeyAwareViewModel>()
        .WithKey(memo, Keys.A | Keys.Alt)
        .KeyToCommand(x => x.OnAltAKey());
vb
mvvmContext.ViewModelType = GetType(KeyAwareViewModel)
    ' Binding the "A" key
    mvvmContext.OfType(Of KeyAwareViewModel)().WithKey(memo, Keys.A).KeyToCommand(Function(x) x.OnAKey())
    ' Binding the "Alt+A" shortcut
    mvvmContext.OfType(Of KeyAwareViewModel)().WithKey(memo, Keys.A Or Keys.Alt).KeyToCommand(Function(x) x.OnAltAKey())

Binding multiple keys to the same command

You want to bind multiple keys to the same command and pass these keys as parameters, use the WithKeys and KeysToCommands methods. Below is the sample KeyAwareViewModel View Model that contains parameterized “OnKey” and “OnKeyArgs” commands. These commands notify users what keys have been pressed.

csharp
public class KeyAwareViewModel {
    protected IMessageBoxService MessageBoxService {
        get { return this.GetService<IMessageBoxService>(); }
    public void OnKey(Keys keys) {
        MessageBoxService.ShowMessage("Key Command:" + keys.ToString());
    }
    public void OnKeyArgs(KeyEventArgs args) {
        string message = string.Join(", ",
            "KeyValue: " + args.KeyValue.ToString(),
            "KeyData: " + args.KeyData.ToString(),
            "KeyCode: " + args.KeyCode.ToString(),
            "Modifiers: " + args.Modifiers.ToString());
        MessageBoxService.ShowMessage("Args = {" + message + "}");
    }
}
vb
Public Class KeyAwareViewModel
    Protected ReadOnly Property MessageBoxService() As IMessageBoxService
        Get
            Return Me.GetService(Of IMessageBoxService)()
        End Get
    public void OnKey(Keys keys)
        MessageBoxService.ShowMessage("Key Command:" & keys.ToString())
    public void OnKeyArgs(KeyEventArgs args)
        Dim message As String = String.Join(", ", "KeyValue: " & args.KeyValue.ToString(), "KeyData: " & args.KeyData.ToString(), "KeyCode: " & args.KeyCode.ToString(), "Modifiers: " & args.Modifiers.ToString())
        MessageBoxService.ShowMessage("Args = {" & message & "}")
    End Property

The code below binds these commands to multiple hotkeys simultaneously.

csharp
mvvmContext.ViewModelType = typeof(KeyAwareViewModel);
    // Binding to the OnKey command
    mvvmContext.OfType<KeyAwareViewModel>()
        .WithKeys(memo, new Keys[] { Keys.A, Keys.B, Keys.C })
        .KeysToCommand(x => x.OnKey(Keys.None), args => args.KeyCode);
    // Binding to the OnKeyArgs command
    mvvmContext.OfType<KeyAwareViewModel>()
        .WithKeys(memo, new Keys[] { Keys.Shift | Keys.A, Keys.Shift | Keys.B, Keys.Shift | Keys.C})
        .KeysToCommand(x => x.OnKeyArgs((KeyEventArgs)null), args => (KeyEventArgs)args);
vb
mvvmContext.ViewModelType = GetType(KeyAwareViewModel)
    ' Binding to the OnKey command
    mvvmContext.OfType(Of KeyAwareViewModel)().WithKeys(memo, New Keys() { Keys.A, Keys.B, Keys.C }).KeysToCommand(Function(x) x.OnKey(Keys.None), Function(args) args.KeyCode)
    ' Binding to the OnKeyArgs command
    mvvmContext.OfType(Of KeyAwareViewModel)().WithKeys(memo, New Keys() { Keys.Shift Or Keys.A, Keys.Shift Or Keys.B, Keys.Shift Or Keys.C}).KeysToCommand(Function(x) x.OnKeyArgs(DirectCast(Nothing, KeyEventArgs)), Function(args) CType(args, KeyEventArgs))

Custom Behaviors

You can implement a custom behavior if there is no ready DevExpress behavior for your needs. For instance, the Ctrl+C keyboard combination copies the entire GridView row by default. You can attach your own behavior to copy the value of the selected row cell only.

csharp
public class ControlCBehavior : EventTriggerBase {
     public ControlCBehavior()
         : base("KeyDown") {
     }
     protected override void OnEvent() {
         if (Args.Control && Args.KeyCode == System.Windows.Forms.Keys.C) {
             var cbService = this.GetService<Services.IClipboardService>();
             GridView View = Source as GridView;
             if (View.GetRowCellValue(View.FocusedRowHandle, View.FocusedColumn) != null && View.GetRowCellValue(View.FocusedRowHandle, View.FocusedColumn).ToString() != String.Empty)
                 cbService.SetText(View.GetRowCellValue(View.FocusedRowHandle, View.FocusedColumn).ToString());
             else
                 XtraMessageBox.Show("The value in the selected cell is null or empty!");
             Args.Handled = true;
         }
     }
 }
vb
Public Class ControlCBehavior
     Inherits EventTriggerBase(Of System.Windows.Forms.KeyEventArgs)

     Public Sub New()
         MyBase.New("KeyDown")
     End Sub
     Protected Overrides Sub OnEvent()
         If Args.Control AndAlso Args.KeyCode = System.Windows.Forms.Keys.C Then
             Dim cbService = Me.GetService(Of Services.IClipboardService)()
             Dim View As GridView = TryCast(Source, GridView)
             If View.GetRowCellValue(View.FocusedRowHandle, View.FocusedColumn) IsNot Nothing AndAlso View.GetRowCellValue(View.FocusedRowHandle, View.FocusedColumn).ToString() <> String.Empty Then
                 cbService.SetText(View.GetRowCellValue(View.FocusedRowHandle, View.FocusedColumn).ToString())
             Else
                 XtraMessageBox.Show("The value in the selected cell is null or empty!")
             End If
             Args.Handled = True
         End If
     End Sub
 End Class

This behavior uses the custom IClipboardService service that copies text to a clipboard.

csharp
public class ClipboardService : IClipboardService {
    public void SetText(string text) {
        Clipboard.SetText(text);
    }
}

public interface IClipboardService {
    void SetText(string text);
}
vb
Public Class ClipboardService
    Implements IClipboardService

    Public Sub SetText(ByVal text As String) Implements IClipboardService.SetText
        Clipboard.SetText(text)
    End Sub
End Class

Public Interface IClipboardService
    Sub SetText(ByVal text As String)
End Interface

After the custom behavior is ready, register the service and call the AttachBehavior method to attach the behavior to the grid view.

csharp
mvvmContext1.RegisterService(new Services.ClipboardService());
mvvmContext1.AttachBehavior<Behaviors.ControlCBehavior>(gridView1);
vb
mvvmContext1.RegisterService(New Services.ClipboardService())
mvvmContext1.AttachBehavior(Of Behaviors.ControlCBehavior)(gridView1)

The custom behavior now suppresses the default shortcut handler and copies only the selected cell value.