Implementing Custom Strategy

  • 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.

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; }
    }
}

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.

public class RibbonControlWrapper : IItemsControlWrapper<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.

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) {
        ...
    }
}

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

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 });
        }
    }
}

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

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; }
    ...
}

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.

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

Use the Inject method to integrate the RibbonItemViewModel.

<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>
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
);

The full example that illustrates this approach is available here.

See Also