Infragistics WPF controls

Implementing a re-sizable composite view user interface using the MVVM pattern, the GridSplitter, and WPF UserControls.

WPF Succinctly Download

 

A composite view user interface

In the world of WPF, the Model-View-ViewModel pattern is a great way to separate your UI from your business logic.  This decoupling allows you to easily unit test your code.  When implemented correctly, the MVVM pattern promotes a view agnostic structure which makes switching between different views extremely easy.  A composite view UI is basically a user interface that is made up of multiple independent views.  By independent, I mean that the views should be decoupled and unaware of each other.  In this tutorial, I will show you how to create the following UI.

 

 

Existing frameworks

There are several existing frameworks that facilitate the implementation of the MVVM pattern and composite view applications.  MVVM Toolkit, MvvMLight, SoPrism, and Prism are a few examples of some of the existing libraries out there.  Prism is probably the most complete and extensive but it also has a large learning curve.  I decided that before I dive into learning the details of Prism, I would benefit by trying to code a composite application by hand. 

The User Interface design

The previous screen shot shows an application made up of a Window that contains a grid, a listview, and a ContentControl.  The ContentControl and ListView are separated by a GridSplitter control.  This allows the two views to be re-sized.  The ContentControl will have a UserControl injected into the content property as the ListView's selected item is changed.

Here is the XAML markup

<Window x:Class="OutlookLayoutPresenter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:OutlookLayoutPresenter.Views"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="128*" />
            <ColumnDefinition Width="375*" />
        </Grid.ColumnDefinitions>
        <ListView SelectionChanged="lstMenu_SelectionChanged" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="MenuDisplayText" x:Name="lstMenu">
        </ListView>

        <ContentControl Grid.Column="1" Content="{Binding Path=Presenter.CurrentView}" x:Name="MainContent">
        </ContentControl>

        <GridSplitter Grid.Column="1"  Width="2" HorizontalAlignment="Left" />
        
    </Grid>
</Window>

As you can see, we have a Grid with two columns.  The column on the left holds a ListView that that is data bound in the code behind.  The ContentControl's content property is also data bound to the Presenter.CurrentView property of our ViewModel.  I chose to implement a presenter object to handle the injection of the UserControl into the ContentControl.  We will start with the codebehind of our view to show how we are setting the DataContext.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using OutlookLayoutPresenter.Views;

namespace OutlookLayoutPresenter
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private MainWindowViewModel _viewModel;

        public MainWindow()
        {
            InitializeComponent();

            _viewModel = new MainWindowViewModel();
            
            this.DataContext = _viewModel;
            lstMenu.ItemsSource = _viewModel;

        }

        void lstMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var menuItem = (ViewMenuItems)e.AddedItems[0];
            _viewModel.Presenter.DisplayView(menuItem);
        }
    }
}

Note that we have bound our ViewModel to the view's DataContext as well as the ListView's ItemsSource property.  The ViewModel is an ObservableCollection of ViewMenuItem objects.  I created the ViewMenuItem class to contain the region's name and the display text.  The ListView serves as a menu.  

Here is the code for the ViewMenuItem class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace OutlookLayoutPresenter
{
    public class ViewMenuItems : INotifyPropertyChanged
    {
        private string _regionName;
        private string _menuDisplayText;

        public string RegionName 
        { 
            get
            {
                return _regionName;
            }
            set
            {
                if (_regionName == value)
                    return;

                _regionName = value;
                OnPropertyChanged("RegionName");
            }
        }

        public string MenuDisplayText 
        { 
            get 
            {
                return _menuDisplayText;
            }
            set
            {
                if (_menuDisplayText == value)
                    return;

                _menuDisplayText = value;
                OnPropertyChanged("MenuDisplayText");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

When looking at the ViewModel, please be sure to note the public Presenter property that is used in the data binding of the ContentControl.

The ObservableCollection of ViewMenuItem objects builds the ListView menu.

Here's the ViewModel's code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace OutlookLayoutPresenter
{
    public class MainWindowViewModel : ObservableCollection<ViewMenuItems> , INotifyPropertyChanged
    {
        private IPresenter _presenter;
        public IPresenter Presenter
        {
          get { return _presenter; }
          set 
          { 
              _presenter = value; 
              OnPropertyChanged("Presenter");
          }
        }

        public MainWindowViewModel ()
	    {
            this.Add(new ViewMenuItems{RegionName = "RegionOne", MenuDisplayText = "Content One"});
            this.Add(new ViewMenuItems{RegionName = "RegionTwo", MenuDisplayText = "Content Two"});
            this.Add(new ViewMenuItems{RegionName = "RegionThree", MenuDisplayText = "Content Three"});

            this.Presenter = new MainWindowPresenter();
	    }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

All that's left now is the MainWindowPresenter class which will dynamically load one of three user controls based on the ListView's selected item.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Controls;
using OutlookLayoutPresenter.Views;

namespace OutlookLayoutPresenter
{
    /// <summary>
    /// This is the presenter class for the main Shell.  the purpose of this
    /// class is to contain all of the UI event handlers for the main shell view.
    /// </summary>
    public class MainWindowPresenter : INotifyPropertyChanged, IPresenter
    {
        ContentControl _currentView = new ContentControl();
        public ContentControl CurrentView
        {
            get 
            { 
                return _currentView; 
            }
            set 
            { 
                _currentView = value;
                OnPropertyChanged("CurrentView");
            }
        }

        public void DisplayView(ViewMenuItems item)
        {
            switch (item.RegionName.ToUpper())
            {
                case "REGIONONE":
                    this.CurrentView.Content = new ContentOne();
                    break;

                case"REGIONTWO":
                    this.CurrentView.Content = new ContentTwo();
                    break;

                case "REGIONTHREE":
                    this.CurrentView.Content = new ContentThree();
                    break;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

There are three user controls that have a different background color and text.  I will show the markup for one of the controls for brevity.

<UserControl x:Class="OutlookLayoutPresenter.Views.ContentOne"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             Background="Blue">
    <Grid>
        <TextBlock FontWeight="Bold" Foreground="White" Text="Content One" />
    </Grid>
</UserControl>
 

As you click on each menu item, the content view will change colors and text.  I've included the source code for your reference.

OutlookLayoutPresenterMVVM.zip (93.27 kb)  

This concludes the tutorial.  Thanks for reading!

Until next time...

Buddy James

kick it on DotNetKicks.com

 

 

 



Comments (3) -

Arla Welding
Arla Welding
10/25/2012 7:53:22 PM #

excellent blog very nice layout and understanding

best virtual server
best virtual server
12/1/2012 12:00:37 AM #

I want to say that this post is astonishing, nice written and comprise about all important info’s. I would like to see more posts like this.

Buddy James
Buddy James
12/1/2012 1:32:38 AM #

Thank you very much for the kind words!

Pingbacks and trackbacks (1)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About the author

My name is Buddy James.  I'm a Microsoft Certified Solutions Developer from the Nashville, TN area.  I'm a Software Engineer, an author, a blogger (http://www.refactorthis.net), a mentor, a thought leader, a technologist, a data scientist, and a husband.  I enjoy working with design patterns, data mining, c#, WPF, Silverlight, WinRT, XAML, ASP.NET, python, CouchDB, RavenDB, Hadoop, Android(MonoDroid), iOS (MonoTouch), and Machine Learning. I love technology and I love to develop software, collect data, analyze the data, and learn from the data.  When I'm not coding,  I'm determined to make a difference in the world by using data and machine learning techniques. (follow me at @budbjames).  

Related links

Month List