Back to Devexpress

Advanced Binding Features

windowsforms-401698-cross-platform-app-development-winforms-mvvm-concepts-data-bindings-and-notifications-advanced-binding-features.md

latest9.2 KB
Original Source

Advanced Binding Features

  • Dec 09, 2024
  • 5 minutes to read

Converters

Converters allow you to dynamically convert bindable property values.

Default Converters

The DevExpress MVVM Framework automatically converts simple types. For instance, in the Binding via Default Converters demo, a string TextEdit.Text property is bound to the integer ViewModel Progress property. Here, the Framework converts property values from Int32 to String and back.

csharp
//View code
var fluent = mvvmContext.OfType<ViewModel>();
fluent.SetBinding(editor, e => e.Text, x => x.Progress);

//ViewModel code
public class ViewModel {
    public virtual int Progress { get; set; }
}
vb
'View code
Dim fluent = mvvmContext.OfType(Of ViewModel)()
fluent.SetBinding(editor, Function(e) e.Text, Function(x) x.Progress)

'ViewModel code
Public Class ViewModel
    Public Overridable Property Progress() As Integer
End Class

The MvvmContext component fires the BindingConvert event when the Framework converts values. You can handle this event to adjust the conversion logic. The Binding with Custom Conversion Handling demo illustrates a TextEdit editor. The editor’s EditValue property is bound to the integer ViewModel Value property. If a user leaves the TextEdit blank, the editor’s EditValue is null because automatic conversion cannot convert null to Int32. In this case, use the BindingConvert event handler to change null to 0.

csharp
//View code
var fluent = mvvmContext.OfType<ViewModel>();
mvvmContext.BindingConvert += (s, e) => {
    string strValue = e.Value as string;
    if(strValue != null) {
        int intValue;
        if(int.TryParse(strValue, out intValue))
            e.Value = intValue;
        else
            e.Value = null;
    }
    if(e.Value == null)
        e.Value = 0;
};
fluent.SetBinding(editor, e => e.EditValue, x => x.Value);
vb
'View code
Dim fluent = mvvmContext.OfType(Of ViewModel)()
AddHandler mvvmContext.BindingConvert, Sub(s, e)
    Dim strValue As String = TryCast(e.Value, String)
    If strValue IsNot Nothing Then
        Dim intValue As Integer = Nothing
        If Integer.TryParse(strValue, intValue) Then
            e.Value = intValue
        Else
            e.Value = Nothing
        End If
    End If
    If e.Value Is Nothing Then
        e.Value = 0
    End If
End Sub
fluent.SetBinding(editor, Function(e) e.EditValue, Function(x) x.Value)

Custom Converters

When you use complex property types that cannot be converted automatically, pass two converters as the last SetBinding method parameters. The first converter converts bindable property values to an acceptable type and the second converter does the opposite.

The Binding via Custom Converters demo illustrates a ViewModel with the ModelState property that accepts custom State enumeration values. This property is bound to the CheckBox.CheckState property of the System.Windows.Forms.CheckState type. Lambda expressions in the SetBinding methods are converters that transform property values.

csharp
//View code
var fluent = mvvmContext.OfType<ViewModel>();
fluent.SetBinding(check, e => e.CheckState, x => x.ModelState,
    modelState => {
        // Convert the ViewModel.State to CheckState
        switch(modelState) {
            case ViewModel.State.Active:
                return CheckState.Checked;
            case ViewModel.State.Inactive:
                return CheckState.Unchecked;
            default:
                return CheckState.Indeterminate;
        }
    },
    checkState => {
        // Convert back from CheckState to the ViewModel.State
        switch(checkState) {
            case CheckState.Checked:
                return ViewModel.State.Active;
            case CheckState.Unchecked:
                return ViewModel.State.Inactive;
            default:
                return ViewModel.State.Suspended;
    }
});

//ViewModel code
public class ViewModel {
    public virtual State ModelState {
        get;
        set;
    }
    public enum State {
        Suspended = 0,
        Inactive = 1,
        Active = 2
    }
}
vb
Dim fluent = mvvmContext.OfType(Of ViewModel)()
fluent.SetBinding(check, Function(e) e.CheckState, Function(x) x.ModelState, Function(modelState)
        Select Case modelState
            Case ViewModel.State.Active
                Return CheckState.Checked
            Case ViewModel.State.Inactive
                Return CheckState.Unchecked
            Case Else
                Return CheckState.Indeterminate
        End Select
    End Function,
    Function(checkState)
        Select Case checkState
            Case CheckState.Checked
                Return ViewModel.State.Active
            Case CheckState.Unchecked
                Return ViewModel.State.Inactive
            Case Else
                Return ViewModel.State.Suspended
        End Select
    End Function)

'ViewModel code
Public Class ViewModel
    Public Overridable Property ModelState() As State
    Public Enum State
        Suspended = 0
        Inactive = 1
        Active = 2
    End Enum
End Class

If users are not allowed to edit the View element’s property value, you can skip the reversed conversion.

Format Bound Values

To format bound property values, pass a string format expression to the SetBinding method. The {0} character sequence is a placeholder for the property value.

csharp
var fluent = mvvmContext.OfType<ViewModel>();
fluent.SetBinding(labelControl, l => l.Text, x => x.Value, "Bound property value is ({0})");
vb
Dim fluent = mvvmContext.OfType(Of ViewModel)()
fluent.SetBinding(labelControl, Function(l) l.Text, Function(x) x.Value, "Bound property value is ({0})")

You can add Format Specifiers to apply additional numeric, date-time, and time span formats. The MVVM Best Practices demo illustrates how to display integer values as currency.

csharp
var fluent = mvvmContext.OfType<ViewModel>();
fluent.SetBinding(label, l => l.Text, x => x.Price, "Price: {0:C2}");
vb
Dim fluent = mvvmContext.OfType(Of ViewModel)()
fluent.SetBinding(label, Function(l) l.Text, Function(x) x.Price, "Price: {0:C2}")

Bind Multiple Properties to the Same Control

To combine values of multiple properties within the same control, use the MvvmContext.SetMultiBinding method. This method accepts the following parameters:

  • a control name;
  • a control property that should be bound;
  • a string array populated with names of bindable ViewModel properties, which values should be combined;
  • a format string (for non-editable controls) or a pair of converters (if users are allowed edit a bound control).

The DevExpress Demo Center provides two modules that combine values of the FirstName and LastName properties into one TextEdit editor. The module that uses a format string binds properties to a disabled (non-editable) editor. In the module that uses converters, you can change the TextEdit value and pass updated strings back to ViewModel properties.

Format string demo

csharp
var fluent = mvvmContext.OfType<ViewModel>();
mvvmContext.SetMultiBinding(
    editForFullName,
    e => e.Text,
    new string[] { "FirstName", "LastName" },
    "{1}, {0}"
);
vb
Dim fluent = mvvmContext.OfType(Of ViewModel)()
mvvmContext.SetMultiBinding(editForFullName, Function(e) e.Text, New String() { "FirstName", "LastName" }, "{1}, {0}")

Converters demo

csharp
var fluent = mvvmContext.OfType<ViewModel>();
mvvmContext.SetMultiBinding(
    editForFullName,
    e => e.EditValue,
    new string[] { "FirstName", "LastName" },
    values => string.Join(",", values),
    value => ((string)value).Split(',')
);
vb
Dim fluent = mvvmContext.OfType(Of ViewModel)()
mvvmContext.SetMultiBinding(
    editForFullName,
    Function(e) e.EditValue,
    New String() { "FirstName", "LastName" },
    Function(values) String.Join(",", values),
    Function(value) CStr(value).Split(","c))