Blogs

The Progress Bar

MVVM Enhancements in WPF and Silverlight versions of Toolbar, Ribbon and Gallery Controls

In my previous blog post, I introduced the enhanced MVVM support for our WPF & Silverlight Data Grid Control to enable binding for column and summary creation. In this post, I’ll introduce new improvements in three of our other Silverlight and WPF components - DXBars, DXRibbon and GalleryControl. These features make it easy to use the controls when building applications following the MVVM pattern.

The main scenario addressed by this new feature is the automatic generation of visual objects in DXBars, DXRibbon and GalleryControl based on data provided by underlying objects (i.e. by View Model classes).

For the BarManager, the ideal MVVM support case would be as follows: The developer provides 1) a collection of objects each of which describes a toolbar and 2) a collection describing bar commands. As a result the BarManager automatically generates bars and populates them with bar items based on the provided data.

As an example, let's see how one could write an application using the BarManager and the MVVM pattern prior to the introduction of the enhanced features. Here is the markup that creates bars from an underlying collection:

  1. <UserControl.Resources>
  2.     <local:MyBarModelToBarValueConverter x:Key="barValueConverter"/>
  3. </UserControl.Resources>
  4. <Grid>
  5.     <dxb:BarManager x:Name="barManager1">
  6.         <Grid>
  7.             <dxb:BarContainerControl ItemsSource="{Binding Path=BarsCollecton, Converter={StaticResource barValueConverter}}"/>
  8.         </Grid>
  9.     </dxb:BarManager>
  10. </Grid>

In the code, a bar container is populated with bars from a BarsCollection, which is a View Model class. At first glance, this code looks quite simple. However, if you go deeper you will see that it requires additional code to be written:

  • A custom ValueConverter that will convert collection elements into bar objects
  • Code that initializes a bar's properties via data binding
  • Code that synchronizes the bar collection and underlying View Model collection

That’s not all though as the Bar’s own bar items will also need to be populated from another View Model collection. Therefore an additional ValueConverter must be implemented to convert collection elements into bar items. The code complexity increases with increasing the hierarchy level of children in a control. For instance, in DXRibbon, a four level hierarchy will need to be dealt with – requiring more complex code in your project.

So, we looked at these scenarios and made some changes to our controls to make it easier to work with them using the MVVM pattern.

Let’s start with the new properties we’ve added to our DXBars, DXRibbon and GalleryControl controls:

  • ItemsSource
  • ItemStyle
  • ItemTemplate
  • ItemTemplateSelector

All controls will follow the same naming convention for these properties.

Using these properties you can easily generate a control's items (including nested items) from collections by writing only XAML code, with one exception - code for TemplateSelectors should be written in code-behind files.

So let’s take a look at a View Model that contains a Bars collection:

  1. public class BarManagerViewModel : DependencyObject
  2. {
  3.     public static readonly DependencyProperty BarsProperty =
  4.         DependencyProperty.Register("Bars", typeof(ObservableCollection<BarViewModel>), typeof(BarManagerViewModel), new PropertyMetadata(null));
  5.  
  6.     public ObservableCollection<BarViewModel> Bars
  7.     {
  8.         get { return (ObservableCollection<BarViewModel>)GetValue(BarsProperty); }
  9.         set { SetValue(BarsProperty, value); }
  10.     }
  11.     //...
  12. }

Then suppose we also have another View Model that provides items for bars:

  1. public class BarViewModel : BarViewModelBase
  2. {
  3.     public static readonly DependencyProperty CommandsProperty;
  4.  
  5.     static BarViewModel()
  6.     {
  7.         CommandsProperty = DependencyProperty.Register("Commands", typeof(ObservableCollection<BarCommand>), typeof(BarViewModel), new PropertyMetadata(null));
  8.     }
  9.     public ObservableCollection<BarCommand> Commands
  10.     {
  11.         get { return ((ObservableCollection<BarCommand>)GetValue(CommandsProperty)); }
  12.         set { SetValue(CommandsProperty, value); }
  13.     }
  14.     //...
  15. }

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

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

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

  1. <local:BarsDemoModule.Resources>
  2.     <DataTemplate x:Key="barTemplate">
  3.         <ContentControl>
  4.             <dxb:Bar Caption="{Binding Name}"
  5.                      ItemLinksSource="{Binding Commands}"/>
  6.         </ContentControl>
  7.     </DataTemplate>
  8. </local:BarsDemoModule.Resources>
  9. <dxdb:DemoModuleControl>
  10.     <Grid>
  11.         <dxb:BarManager
  12.             BarsSource="{Binding Bars}"
  13.             BarTemplate="{StaticResource barTemplate}"
  14.         />
  15.     </Grid>
  16. </dxdb:DemoModuleControl>

In this example, a DataTemplate is used to generate each bar from an underlying View Model (BarViewModel). You’ll notice that BarManagerViewModel.Bars is a collection of BarViewModel objects – this collection's objects are automatically assigned to the DataTemplate's DataContext, allowing us to initialize a bar's settings with properties on BarViewModel.

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

  1. <local:BarCommandTemplateSelector x:Key="itemTemplateSelector"/>
  2.  
  3. <Style x:Key="barStyle" TargetType="dxb:Bar">
  4.     <Setter Property="ItemTemplateSelector" Value="{StaticResource itemTemplateSelector}"/>
  5. </Style>
  6. <dxb:BarManager
  7.     BarsSource="{Binding Bars}"
  8.     BarTemplate="{StaticResource barTemplate}"
  9.     BarStyle="{StaticResource barStyle}"
  10. />

As seen above, all bindings between View Models and View 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:

  1. public class BarCommandTemplateSelector : DataTemplateSelector
  2. {
  3.     public override DataTemplate SelectTemplate(object item, DependencyObject container)
  4.     {
  5.         if (item is BarSubMenuCommand) return (DataTemplate)MVVMBar.SharedResources["subItemTemplate"];
  6.         return (DataTemplate)MVVMBar.SharedResources["itemTemplate"];
  7.     }
  8. }

The complete code of this sample can be found in the "MVVM Bars" demos for WPF and Silverlight shipped with the installation.

The described approach is extremely efficient and can also be applied when writing MVVM applications with the DXRibbon and GalleryControl. These controls provide corresponding properties (ItemsSource, ItemStyle, ItemTemplate and ItemTemplateSelector) that allow the controls to be populated with items from underlying collections.

For implementation details, you may refer to the "MVVM Ribbon" demos for WPF and Silverlight included in the installation. The online demos can be launched from our Demo Center at:

http://demos.devexpress.com/DemoCenter/

Finally, below you will find a list of DevExpress classes and properties in DXBars, DXRibbon and GalleryControl that support the MVVM pattern:

 

Class

Properties

BarManager

BarsSource

BarTemplate

BarStyle

BarTemplateSelector

Bar, BarLinkContainerItem, BarSubItem, PopupMenu, PopupMenuInfo, RibbonPageGroup, ApplicationMenu

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

Published Aug 03 2011, 01:42 PM by
Bookmark and Share

Comments

Brian Cook

I love this.  It will make my development easier.  Thanks!

August 15, 2011 10:52 AM
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, Silverlight, ASP.NET, WinForms, HTML5 or Windows 8, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2014 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners