Skip to main content

MVVM Support in DXBars, DXRibbon and GalleryControl

  • 4 minutes to read

According to the MVVM pattern, a visual component needs to be bound to a View Model(s) that contains information required to render or populate the visual component with data. As a result, the bound component should automatically generate visual objects based on data stored in an underlying View Model. This scenario is supported by DXBars, DXRibbon and GalleryControl, which provide dedicated properties.

  • ...(Item)Source - This property is used to bind a component/class to a data collection.
  • ...(Item)Style - This property allows you to customize styles of visual objects generated via templates. It also allows you to implement custom template selecting logic.
  • ...(Item)Template - Templates are used to render underlying objects in a specific form.
  • ...(Item)TemplateSelector - Template selectors allow you to choose templates based on your logic.

Below is the list of DevExpress classes and properties in DXBars, DXRibbon and GalleryControl that support the MVVM pattern.

Class

Property

BarManager

BarsSource

BarTemplate

BarTemplateSelector

BarStyle

Bar, BarLinkContainerItem, BarSubItem, PopupMenu

ItemLinksSource

ItemTemplate

ItemStyle

ItemTemplateSelector

ApplicationMenu, RibbonPageGroup

ItemLinksSource

ItemTemplate

ItemStyle

ItemTemplateSelector

RibbonControl, RibbonStatusBarControl

CategoriesSource

CategoryTemplate

CategoryStyle

CategoryTemplateSelector

RibbonPageCategory

PagesSource

PageTemplate

PageTemplateSelector

PageStyle

RibbonPage

GroupsSource

GroupTemplate

GroupStyle

GroupTemplateSelector

GalleryControl

GroupsSource

GroupTemplate

GroupStyle

GroupTemplateSelector

GalleryItemGroup

ItemsSource

ItemTemplate

ItemStyle

ItemTemplateSelector

#Example

Below, we will consider basic steps to implement MVVM support in an application using DXBars. This very same approach is applicable when designing applications with DXRibbon and GalleryControl. See the following demos that ship with the installation to learn the details.

  • "MVVM Bars"
  • "MVVM Ribbon"

Let's examine how to populate bars in a BarManager and bar items from underlying collections. Suppose we have two classes (View Models) that describe bars and bar items. The first class, BarManagerViewModel, provides a Bars collection, whose elements (BarViewModel objects) describe individual bars.


public class BarManagerViewModel : DependencyObject
{
    public static readonly DependencyProperty BarsProperty =
        DependencyProperty.Register("Bars", typeof(ObservableCollection<BarViewModel>), typeof(BarManagerViewModel), new PropertyMetadata(null));

    public ObservableCollection<BarViewModel> Bars
    {
        get { return (ObservableCollection<BarViewModel>)GetValue(BarsProperty); }
        set { SetValue(BarsProperty, value); }
    }
    //...
}

The second class, BarViewModel, contains a Commands collection, where elements (BarCommand) contain information to initialize bar items.


public class BarViewModel : BarViewModelBase
{
    public static readonly DependencyProperty CommandsProperty;

    static BarViewModel()
    {
        CommandsProperty = DependencyProperty.Register("Commands", typeof(ObservableCollection<BarCommand>), typeof(BarViewModel), new PropertyMetadata(null));
    }
    public ObservableCollection<BarCommand> Commands
    {
        get { return ((ObservableCollection<BarCommand>)GetValue(CommandsProperty)); }
        set { SetValue(CommandsProperty, value); }
    }
    //...
}

In the code, DataContext must be set for the main window to a BarManagerViewModel object. This DataContext will be propagated to the window's children, including a BarManager component.


BarManagerViewModel viewModel = new BarManagerViewModel();
this.DataContext = viewModel;

Once we ensure that the BarManager gets the correct DataContext, we can populate a BarManager with bars from the BarManagerViewModel.Bars collection using data binding.


<local:BarsDemoModule.Resources>
    <DataTemplate x:Key="barTemplate">
        <ContentControl>
            <dxb:Bar Caption="{Binding Name}"
                     ItemLinksSource="{Binding Commands}"/>
        </ContentControl>
    </DataTemplate>
</local:BarsDemoModule.Resources>
<dxdb:DemoModuleControl>
    <Grid>
        <dxb:BarManager
            BarsSource="{Binding Bars}"
            BarTemplate="{StaticResource barTemplate}"
        />
    </Grid>
</dxdb:DemoModuleControl>

Here, the BarManager.BarsSource property is bound to the BarManagerViewModel.Bars collection. The BarManager.BarTemplate property is set to a template that will visualize elements in the BarManager.BarsSource collection. The collection's elements (BarViewModel objects) are automatically assigned to the DataTemplate's DataContext, allowing us to initialize bar settings with properties on BarViewModel. So, you see that the Bar.Caption property is bound to the BarViewModel.Name property and the Bar.ItemLinkSource property is bound to the BarViewModel.Commands property.

Generally speaking, a DataTemplate's DataContext is automatically set to an object being visualized by this template.

When defining a DataTemplate for a Bar, the DataTemplate's root element must be ContentControl with a Bar object as the content.

It is also possible to define a style that will be automatically applied to each bar created via a template. For instance, in the markup below, a style defines an item template selector (an object that selects templates for bar items based on your logic).


<local:BarCommandTemplateSelector x:Key="itemTemplateSelector"/>

<Style x:Key="barStyle" TargetType="dxb:Bar">
    <Setter Property="ItemTemplateSelector" Value="{StaticResource itemTemplateSelector}"/>
</Style>
<dxb:BarManager 
    BarsSource="{Binding Bars}" 
    BarTemplate="{StaticResource barTemplate}"
    BarStyle="{StaticResource barStyle}"
/>

As seen above, all bindings between View Models and View classes are set up in XAML, without using code-behind files. However, there is one exception: template selectors must be written in code-behind files. The BarCommandTemplateSelector below chooses between two DataTemplates (subItemTemplate or itemTemplate) based on our model.


public class BarCommandTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is BarSubMenuCommand) return (DataTemplate)MVVMBar.SharedResources["subItemTemplate"];
        return (DataTemplate)MVVMBar.SharedResources["itemTemplate"];
    }
}

The approach used to initialize bar items within bars is identical to the one used to initialize bars. You can find the complete source code of this example in the "MVVM Bars" demo shipped with the installation. See it to learn all implementation details.