Back to Devexpress

Implementing Custom Strategy

wpf-113943-mvvm-framework-services-predefined-set-viewinjectionservice-implementing-custom-strategy.md

latest14.0 KB
Original Source

Implementing Custom Strategy

  • Sep 27, 2023
  • 7 minutes to read

Implementing Custom Strategy

Below is a list of controls supported by the ViewInjectionService out of the box.

  • ContentPresenter descendants;
  • ContentControl descendants;
  • Panel descendants;
  • ItemsControl descendants;
  • Selector descendants;
  • TabControl ;
  • DXTabControl ;
  • FlowLayoutControl ;
  • TileLayoutControl ;
  • NavBarControl
  • DocumentGroup

For each control from this list, the ViewInjectionService provides a separate View Injection Strategy. The Strategy implements the view injection mechanism for the specified control (and its descendants). You can create your own strategy or adapt an existing one. To customize the existing strategy, implement a wrapper for your specified control. Wrapper provides the base API for the interaction with your control.

Here is the default wrapper for the ItemsControl.

csharp
public interface IItemsControlWrapper<T> : ITargetWrapper<T> where T : DependencyObject {
    object ItemsSource { get; set; }
    DataTemplate ItemTemplate { get; set; }
    DataTemplateSelector ItemTemplateSelector { get; set; }
}
...
public class ItemsControlWrapper : IItemsControlWrapper<ItemsControl> {
    public ItemsControl Target { get; set; }
    public object ItemsSource {
        get { return Target.ItemsSource; }
        set { Target.ItemsSource = (IEnumerable)value; }
    }
    public virtual DataTemplate ItemTemplate {
        get { return Target.ItemTemplate; }
        set { Target.ItemTemplate = value; }
    }
    public virtual DataTemplateSelector ItemTemplateSelector {
        get { return Target.ItemTemplateSelector; }
        set { Target.ItemTemplateSelector = value; }
    }
}
vb
Public Interface IItemsControlWrapper(Of T As DependencyObject)
    Inherits ITargetWrapper(Of T)
    Property ItemsSource() As Object
    Property ItemTemplate() As DataTemplate
    Property ItemTemplateSelector() As DataTemplateSelector
End Interface
...
Public Class ItemsControlWrapper
    Implements IItemsControlWrapper(Of ItemsControl)
    Public Property Target() As ItemsControl
        Get
            Return m_Target
        End Get
        Set
            m_Target = Value
        End Set
    End Property
    Private m_Target As ItemsControl
    Public Property ItemsSource() As Object Implements IItemsControlWrapper(Of ItemsControl).ItemsSource
        Get
            Return Target.ItemsSource
        End Get
        Set
            Target.ItemsSource = DirectCast(value, IEnumerable)
        End Set
    End Property
    Public Overridable Property ItemTemplate() As DataTemplate Implements IItemsControlWrapper(Of ItemsControl).ItemTemplate
        Get
            Return Target.ItemTemplate
        End Get
        Set
            Target.ItemTemplate = value
        End Set
    End Property
    Public Overridable Property ItemTemplateSelector() As DataTemplateSelector Implements IItemsControlWrapper(Of ItemsControl).ItemTemplateSelector
        Get
            Return Target.ItemTemplateSelector
        End Get
        Set
            Target.ItemTemplateSelector = value
        End Set
    End Property
End Class

Below is an illustration that implements a custom strategy by using an example of injecting an element (command buttons) in the RibbonControl.

To adapt the ViewInjectionService for RibbonControl so the service injects BarButtonItems into the specified RibbonControl’s Group (taking into account the Categories and Pages), create a custom descendant of the IItemsControlWrapper interface.

csharp
public class RibbonControlWrapper : IItemsControlWrapper<RibbonControl> {
    ...
}
vb
Public Class RibbonControlWrapper
        Implements IItemsControlWrapper(Of RibbonControl)

Handle the changes of the ItemsSource that is the collection of command-button ViewModels ( RibbonItemViewModel instances). To subscribe the CollectionChanged event, use the ItemsSource property’s setter.

csharp
public class RibbonControlWrapper : IItemsControlWrapper<RibbonControl> {
    public RibbonControl Target { get; set; }
    private object _ItemsSource;
    public object ItemsSource {
        get {
            return _ItemsSource;
        }
        set {
            if (_ItemsSource != value) {
                if (_ItemsSource != null)
                    ((INotifyCollectionChanged)_ItemsSource).CollectionChanged -= RibbonControlWrapper_CollectionChanged;
                _ItemsSource = value;
                ((INotifyCollectionChanged)_ItemsSource).CollectionChanged += RibbonControlWrapper_CollectionChanged;
            }
        }
    }

    void RibbonControlWrapper_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
        ...
    }
}
vb
Public Class RibbonControlWrapper
    Implements IItemsControlWrapper(Of RibbonControl)
    Private _Target As RibbonControl
    Public WriteOnly Property Target() As RibbonControl Implements DevExpress.Mvvm.UI.ViewInjection.ITargetWrapper(Of RibbonControl).Target
        Set
            _Target = value
        End Set
    End Property
    Private _ItemsSource As Object
    Public Property ItemsSource() As Object Implements IItemsControlWrapper(Of RibbonControl).ItemsSource
        Get
            Return _ItemsSource
        End Get
        Set(ByVal value As Object)
            If _ItemsSource IsNot value Then
                If _ItemsSource IsNot Nothing Then
                    RemoveHandler DirectCast(_ItemsSource, INotifyCollectionChanged).CollectionChanged, AddressOf RibbonControlWrapper_CollectionChanged
                End If
                _ItemsSource = value
                AddHandler DirectCast(_ItemsSource, INotifyCollectionChanged).CollectionChanged, AddressOf RibbonControlWrapper_CollectionChanged
            End If
        End Set
    End Property

    Private Sub RibbonControlWrapper_CollectionChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs)
        ...
    End Sub

Implement a logic for adding BarButtonItems in the RibbonControlWrapper_CollectionChanged event hander. For instance:

csharp
void RibbonControlWrapper_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
    if (e.Action == NotifyCollectionChangedAction.Add) {
        foreach (RibbonItemViewModel vm in e.NewItems) {
            var category = Target.Categories.FirstOrDefault(c => String.Equals(c.Name, vm.Category) || String.Equals(c.Caption, vm.Category));
            if (category == null) {
                category = new RibbonPageCategory() { Name = vm.Category.Replace(" ", ""), Caption = vm.Category };
                Target.Categories.Add(category);
            }
            var page = category.Pages.FirstOrDefault(p => String.Equals(p.Name, vm.Page) || String.Equals(p.Caption, vm.Page));
            if (page == null) {
                page = new RibbonPage() { Name = vm.Page.Replace(" ", ""), Caption = vm.Page };
                category.Pages.Add(page);
            }
            var group = page.Groups.FirstOrDefault(g => String.Equals(g.Name, vm.Group) || String.Equals(g.Caption, vm.Group));
            if (group == null) {
                group = new RibbonPageGroup() { Name = vm.Group.Replace(" ", ""), Caption = vm.Group };
                page.Groups.Add(group);

            }
            group.Items.Add(new BarButtonItem() { Content = vm.Content, Glyph = vm.Glyph });
        }
    }
}
vb
Private Sub RibbonControlWrapper_CollectionChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs)
    If e.Action = NotifyCollectionChangedAction.Add Then
        For Each vm As RibbonItemViewModel In e.NewItems
            Dim category = _Target.Categories.FirstOrDefault(Function(c) String.Equals(c.Name, vm.Category) OrElse String.Equals(c.Caption, vm.Category))
            If category Is Nothing Then
                category = New RibbonPageCategory() With {.Name = vm.Category.Replace(" ", ""), .Caption = vm.Category}
                _Target.Categories.Add(category)
            End If
            Dim page = category.Pages.FirstOrDefault(Function(p) String.Equals(p.Name, vm.Page) OrElse String.Equals(p.Caption, vm.Page))
            If page Is Nothing Then
                page = New RibbonPage() With {.Name = vm.Page.Replace(" ", ""), .Caption = vm.Page}
                category.Pages.Add(page)
            End If
            Dim group = page.Groups.FirstOrDefault(Function(g) String.Equals(g.Name, vm.Group) OrElse String.Equals(g.Caption, vm.Group))
            If group Is Nothing Then
                group = New RibbonPageGroup() With {.Name = vm.Group.Replace(" ", ""), .Caption = vm.Group}
                page.Groups.Add(group)

            End If
            group.Items.Add(New BarButtonItem() With {.Content = vm.Content, .Glyph = vm.Glyph})
        Next vm
    End If
End Sub

The RibbonItemViewModel is a class that represents the command-button ViewModel.

csharp
public class RibbonItemViewModel {
    public virtual string Category { get; protected set; }
    public virtual string Page { get; protected set; }
    public virtual string Group { get; protected set; }
    public virtual string Content { get; protected set; }
    public virtual ImageSource Glyph { get; protected set; }
    ...
}
vb
Public Class RibbonItemViewModel
    Private privateCategory As String
    Public Overridable Property Category() As String
        Get
            Return privateCategory
        End Get
        Protected Set(ByVal value As String)
            privateCategory = value
        End Set
    End Property
    Private privatePage As String
    Public Overridable Property Page() As String
        Get
            Return privatePage
        End Get
        Protected Set(ByVal value As String)
            privatePage = value
        End Set
    End Property
    Private privateGroup As String
    Public Overridable Property Group() As String
        Get
            Return privateGroup
        End Get
        Protected Set(ByVal value As String)
            privateGroup = value
        End Set
    End Property
    Private privateContent As String
    Public Overridable Property Content() As String
        Get
            Return privateContent
        End Get
        Protected Set(ByVal value As String)
            privateContent = value
        End Set
    End Property
    Private privateGlyph As ImageSource
    Public Overridable Property Glyph() As ImageSource
        Get
            Return privateGlyph
        End Get
        Protected Set(ByVal value As ImageSource)
            privateGlyph = value
        End Set
    End Property

    Protected Sub New()
    End Sub
    ...
End Class

To register the newly implemented strategy, use the StrategyManager and its StrategyManager.RegisterStrategy<TTarget, TStrategy> method. Since this manager is similar to the ViewInjectionManager , you can also access it from any section of your code: for instance, from the StartUp event handler as shown in the code snippet below.

csharp
public partial class App : Application {
    private void Application_Startup(object sender, StartupEventArgs e) {            
        StrategyManager.Default.RegisterStrategy<RibbonControl, ItemsControlStrategy<RibbonControl, RibbonControlWrapper>>();
        ...
    }
    ...
}
vb
Partial Public Class App
    Inherits Application

    Private Sub Application_Startup(ByVal sender As Object, ByVal e As StartupEventArgs)
        StrategyManager.Default.RegisterStrategy(Of RibbonControl, ItemsControlStrategy(Of RibbonControl, RibbonControlWrapper))()
        ...
    End Sub
    ...
End Class

Use the Inject method to integrate the RibbonItemViewModel.

xaml
<dxr:RibbonControl>
    <dxr:RibbonDefaultPageCategory Caption="Default PageCategory" Name="defaultPageCategory">
        <dxr:RibbonPage Caption="Default Page" Name="defaultPage">
            <dxr:RibbonPageGroup Caption="Default PageGroup" Name="defaultPageGroup"/>
        </dxr:RibbonPage>
    </dxr:RibbonDefaultPageCategory>
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:ViewInjectionService RegionName="{x:Static c:Regions.Main}"/>
    </dxmvvm:Interaction.Behaviors>
</dxr:RibbonControl>
csharp
ViewInjectionManager.Default.Inject(
    Regions.Main,
    null,
    () => RibbonItemViewModel.Create(
        "Default PageCategory", 
        "Default Page", 
        "Default PageGroup", 
        "New BarButtonItem #1",
        new BitmapImage(new Uri("pack://application:,,,/DevExpress.Images.v14.2;component/Images/Actions/New_16x16.png"))
    ),
    String.Empty
);
vb
ViewInjectionManager.Default.Inject(
    Regions.Main, 
    Nothing, 
    Function() RibbonItemViewModel.Create(
        "Default PageCategory", 
        "Default Page", 
        "Default PageGroup", 
        "New BarButtonItem #1", 
        New BitmapImage(New Uri("pack://application:,,,/DevExpress.Images.v14.2;component/Images/Actions/New_16x16.png"))
    ), 
    String.Empty
)

See Also

View Injection Service Concept

View Injection Manager Concept