DevExpress.Mvvm.Free Framework

repository·main·Indexed 42 days ago

https://github.com/devexpress/devexpress.mvvm.free

A lightweight, standalone version of the DevExpress MVVM Framework for WPF. Designed for use with standard or third-party WPF controls, it provides tools for implementing the Model-View-ViewModel pattern, including DelegateCommand, AsyncCommand, BindableBase, ViewModelBase, and the Module Injection Framework (MIF).

Tokens
3.6K
Snippets
12
Records
17
Agent score
54%

What's inside DevExpress MVVM Free

  1. Understand the difference between DevExpress.Mvvm and DevExpress.Mvvm.Free

    main

    There are two distinct versions of the DevExpress MVVM Framework:

    1. DevExpress.Mvvm: Included in the DevExpress WPF component suite. It is optimized for use with DevExpress WPF controls.
    2. DevExpress.Mvvm.Free: A standalone version designed for use with standard WPF controls or third-party control suites. It is a mirror of the full framework, meaning you can use the same official DevExpress documentation for both.

    Note: The DevExpress.Mvvm.Free version is not compatible with the DevExpress WPF suite.

  2. Use Module Injection Framework (MIF) for modular applications

    main

    The Module Injection Framework (MIF) facilitates the development of modular applications with loosely coupled modules. Key features include:

    • Automatic persistence of logical state and layout.
    • Code separation into logical modules with straightforward navigation.
    • Support for Unit Testing.
    • Use ModuleInjection.GetRegions to retrieve all regions from a specific ViewModel.
  3. Install DevExpress.Mvvm.Free via NuGet

    main

    The Free DevExpress MVVM Framework can be installed as a NuGet package. This version is specifically designed for use with standard or third-party WPF controls and is not compatible with the full DevExpress WPF component suite.

    # Install via NuGet package manager
    Install-Package DevExpressMvvm
  4. How DelegateCommand and AsyncCommand work together

    main

    Both DelegateCommand and AsyncCommand inherit from CommandBase, which manages the relationship between the command's logic and the UI's ability to execute it.

    1. CommandBase: Provides the foundation for CanExecute logic and the CanExecuteChanged event. It handles the logic of whether to use the WPF CommandManager or a local event handler.
    2. DelegateCommandBase: A middle layer for synchronous commands that allows initializing the executeMethod and canExecuteMethod via a constructor.
    3. AsyncCommandBase: A middle layer for asynchronous commands that adds INotifyPropertyChanged support (specifically for IsExecuting) and manages CancellationTokenSource for task cancellation.
    4. Concrete Implementations: DelegateCommand and AsyncCommand are the final classes you instantiate to bind to your View or ViewModel.
  5. Use Async Commands in ViewModels

    main

    POCO ViewModels and ViewModelBase descendants can automatically generate AsyncCommand instances for methods marked with the async keyword. You can also manually invalidate an auto-generated command using RaiseCanExecuteChanged by referencing the async method.

    [AsyncCommand(UseCommandManager = false)]
    public async Task Calculate() {
        for(int i = 0; i <= 100; i++) {
            Progress = i;
            await Task.Delay(20);
        }
    }
    
    void UpdateCalculateCommand() {
        // Invalidate the auto-generated command for the Calculate method
        this.RaiseCanExecuteChanged(x => x.Calculate());
    }
  6. Simplified property syntax in BindableBase and ViewModelBase

    main

    The BindableBase and ViewModelBase classes provide simplified syntax for implementing properties with change notification. You can use GetValue<T> and SetValue for a cleaner implementation.

    // Option 1: Using GetValue/SetValue
    public string FullName {
        get { return GetValue<string>(); }
        set { SetValue(value, OnFullNameChanged); }
    }
    
    // Option 2: Using ref with SetValue
    string fullName;
    public string FullName {
        get { return fullName; }
        set { SetValue(ref fullName, value, OnFullNameChanged); }
    }
  7. Configure CommandManager behavior globally

    main

    The CommandBase class provides a static property to control whether commands globally use the WPF CommandManager to handle CanExecuteChanged events.

    • Setting CommandBase.DefaultUseCommandManager = true (default) allows commands to automatically re-evaluate their CanExecute state when the WPF CommandManager.RequerySuggested event fires.
    • Setting it to false requires you to manually call RaiseCanExecuteChanged() on your command instances to update the UI.
    // Disable automatic CommandManager updates globally
    DevExpress.Mvvm.CommandBase.DefaultUseCommandManager = false;
  8. Generate UICommands from MessageButtons

    main

    The UICommand class provides static factory methods to automatically generate a list of UICommand objects based on standard message box button sets. This is useful for creating consistent dialog button layouts.

    Available methods:

    • GenerateFromMessageButton(MessageButton dialogButtons, IMessageButtonLocalizer buttonLocalizer, MessageResult? defaultButton = null, MessageResult? cancelButton = null)
    • GenerateFromMessageBoxButton(...) (Helper for MessageBoxButton types)

    Supported MessageButton types include:

    • OK
    • OKCancel
    • YesNo
    • YesNoCancel
  9. Use DelegateCommand for synchronous command logic

    main

    A DelegateCommand is used to wrap synchronous methods into an ICommand implementation. You can create a command that takes no parameters, or a generic DelegateCommand<T> that accepts a parameter of type T during execution.

    Key features:

    • Parameter Support: Use DelegateCommand<T> to pass typed data to your command logic.
    • Execution Control: Provide a Func<T, bool> to define when the command is allowed to run (CanExecute).
    • Command Manager Integration: By default, commands use the WPF CommandManager to automatically handle CanExecuteChanged events. You can disable this by passing false to the constructor to improve performance if you prefer manual updates via RaiseCanExecuteChanged().
    // Simple command with no parameters
    var simpleCommand = new DelegateCommand(() => DoSomething());
    
    // Command with a parameter and validation logic
    var parameterizedCommand = new DelegateCommand<int>(
        executeMethod: (id) => ProcessItem(id),
        canExecuteMethod: (id) => id > 0
    );
    
    // Manually trigger a re-evaluation of CanExecute
    parameterizedCommand.RaiseCanExecuteChanged();
  10. Use AsyncCommand for asynchronous command logic

    main

    An AsyncCommand is designed for long-running or asynchronous operations. It implements IAsyncCommand and provides built-in support for tracking execution state.

    Key features:

    • Execution State: Monitor IsExecuting (boolean) to show loading indicators in your UI.
    • Concurrency Control: Use AllowMultipleExecution to decide if the command can be triggered again while a previous execution is still running. If false (default), CanExecute will return false while IsExecuting is true.
    • Cancellation: The command provides a CancelCommand and a CancellationTokenSource. You can use these to cancel the underlying task.
    • Async/Await: Use ExecuteAsync(parameter) to return a Task that you can await.

    Note: AsyncCommand implements INotifyPropertyChanged for the IsExecuting property.

    // Define an async command
    var asyncCommand = new AsyncCommand<string>(
        executeMethod: async (name) => await Task.Delay(2000),
        canExecuteMethod: (name) => !string.IsNullOrEmpty(name),
        allowMultipleExecution: false
    );
    
    // In your UI or logic:
    if (asyncCommand.IsExecuting) {
        // Show spinner
    }
    
    await asyncCommand.ExecuteAsync("User123");
  11. Use IDelegateCommand to manually trigger command re-evaluation

    main

    If you are implementing or using an IDelegateCommand, you can call RaiseCanExecuteChanged() to notify the command target (e.g., a UI button) that the result of CanExecute may have changed. This is useful when the state determining whether a command can run is updated outside of the command's own logic.

    // Assuming 'myCommand' implements IDelegateCommand
    myCommand.RaiseCanExecuteChanged();