Second Stanza

July 26, 2010

MVVM – MEF – Reactive Data Grid – Part IV

Filed under: MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 7:01 am

In MVVM, the View is meant to be isolated from other parts of the model. The goal for the View is that It contains user interface and not code. This is a good goal, obviously, since it allows designers to work separately from developers and allows for unit testing other parts of the model separate from the design.

In MVVM, events that originate in the view are usually handled in the view model. The idea of event delegation through a command interface has become popular for addressing this issue within MVVM. For example, the Prism framework provides a DelegateCommand that can be bound from the View which in turn executes a method implemented within the View Model. The MVVM Light Toolkit expands event delegation more by supporting a wider array of events. These solutions allow the user to declaratively express how events should be handled.

And anybody who has used these knows the difficulties in addressing more complex eventing scenarios, beyond just doing something when the user clicks a button.

One of the benefits of the Reactive Extensions for .NET (Rx) is the ability to do interesting things with events. You can compose events from multiple sources.  You can filter and transform events. You can orchestrate different UI elements. Rx gives you a great tool for managing the asynchronous nature of event handling.I think the Rx extensions provides a better mechanism for dealing with UI events that the current MVVM solutions. Yes, a developer is putting code into the code behind for the View, but the benefits of Rx outweigh this in my mind.

In the MVVMGrid application, the columns to be displayed in the grid are stored in settings files on the web server, but these settings, in fact, map to a very specific configuration of the controls contained within the View. The View knows about these controls (the DataGridColumn controls) so it makes sense that the View should be the one to manipulate these controls.

Let’s examine how this is used in the MVVMGrid application.

First, the GridViewModel exposes a ViewSettings property that contains the settings for the view the user has currently selected. The GridView needs to know when this property changes so that it can update the view. The bindings expressed in the GridView use the INotifyPropertyChanged interface to determine when the value of a property has changed. The GridView itself can use this same interface to determine when the view needs updating.

        [Import]
        public GridViewModel ViewModel
        {
            get { return DataContext as GridViewModel; }
            set { DataContext = value; OnViewModelChanged(); }
        }

        private IDisposable notifyChangedSubscription;

        private void OnViewModelChanged()
        {
            if( notifyChangedSubscription != null )
            {
                notifyChangedSubscription.Dispose();
                notifyChangedSubscription = null;
            }
            if( ViewModel != null )
            {
                notifyChangedSubscription =
                    Observable
                        .FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
                            h => new PropertyChangedEventHandler( h ),
                            h => ViewModel.PropertyChanged += h,
                            h => ViewModel.PropertyChanged -= h )
                        .Where( evt => evt.EventArgs.PropertyName == "ViewSettings" )
                        .Subscribe( evt =>
                        {
                            UpdateColumns();
                        } );
            }
        }

When the ViewModel property is initially set, the OnViewModelChanged method subscribes (using the Rx extensions) to the PropertyChanged event. It filters out the other property changed notifications except for the one we need.  Finally, when the event occurs, the UpdateColumns method is called to update the view.

The UpdateColumns method uses a LINQ query to map from the column definitions in the view settings to the DataGridColumn controls contained within the data grid, showing or hiding the columns and setting the prescribed width.

        private static string GetColumnID( DataGridColumn gridColumn )
        {
            return (string)gridColumn.GetValue( FrameworkElement.NameProperty );
        }

        private void UpdateColumns()
        {
            int displayIndex = 0;
            ViewModel.ViewSettings.Columns
                .GroupJoin(
                    dataGrid.Columns,
                    vc => vc.ColumnID,
                    gc => GetColumnID( gc ),
                    ( vc, gc ) => Tuple.Create( vc, gc.FirstOrDefault() ) )
                .Concat(
                    dataGrid.Columns
                        .Where( gc => !ViewModel.ViewSettings.Columns
                                            .Where( vc => GetColumnID( gc ) == vc.ColumnID )
                                            .Any() )
                        .Select( gc => Tuple.Create( ViewColumnSettings.Null, gc ) ) )
                .ToList()
                .ForEach( t =>
                {
                    ViewColumnSettings viewCol = t.Item1;
                    DataGridColumn gridCol = t.Item2;

                    if( viewCol == null )
                        gridCol.Visibility = System.Windows.Visibility.Collapsed;
                    else
                    {
                        gridCol.Visibility = System.Windows.Visibility.Visible;
                        gridCol.Width = new DataGridLength( viewCol.ColumnWidth, DataGridLengthUnitType.Pixel );
                        gridCol.DisplayIndex = displayIndex++;
                    }
                } );
        }

I grant that this may not be the best example of the benefit of using the Rx extensions within the View, but it shows the basic idea. The bottom line is that I think that there are some great benefits of using Rx in this way and perhaps we should take a second look our View implementations.

Agree/Disagree with me? Please add your comments!

July 25, 2010

MVVM – MEF – Reactive Data Grid – Part III

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 12:18 am

There are a number of ways to retrieve data from a web service. Silverlight provides this functionality through:

The Reactive Extensions for .NET (Rx) builds upon this by providing functionality that allows for composing asynchronous and event-based applications. The MVVMGrid application uses the Rx extensions for the grid services and in certain event handling. I’ll focus on the grid services first.

An application service makes a call out to a web service and provides a response to the application. It must also provide some mechanism to notify the application that a service has failed for some reason. In the MVVMGrid application, the GridService class exposes its methods using IObservable collections. So instead of using an event or an asynchronous interface, a client to the service simply calls the Subscribe method to obtain the response from the service or a failure status.

Let’s examine the grid data service. The grid’s data is generated randomly by the application, but could have easily been retrieved from a web service.

        public IObservable<GridDataCollection> GetGridItems()
        {
            return
                Observable
                    .Repeat( 0, 1 )
                    .Select( _ =>
                    {
                        GridDataCollection gridDataCollection = new GridDataCollection();
                        gridDataCollection.AddRandomData( 100 );
                        return gridDataCollection;
                    } )
                    .SubscribeOn( Scheduler.ThreadPool );
        }

Here

  • The integer 0 is repeated once in order to get an IObservable collection started.
  • A GridDataCollection object is created, 100 randomly-generated GridData items are inserted into it, and then it is selected.
  • The subscription is moved to a worker thread so that the generation of these elements does not occur on the UI thread (which could make the Silverlight application less responsive).

In the GridViewModel, the OnImportsSatisfied method is used to initiate the process of obtaining data from the services.

        private void SetExceptionMessage( Exception ex )
        {
            ErrorMessageOne = String.Concat( "Error: ", ex.GetType().FullName );
            ErrorMessageTwo = ex.Message;
        }

        public void OnImportsSatisfied()
        {
            ...

            Service
                .GetGridItems()
                .ObserveOnDispatcher()
                .Subscribe( d =>
                {
                    DataItems = new ObservableCollection<GridData>( d );
                },
                SetExceptionMessage );
        }

The GetGridItems method of the service object returns an IObservable collection.  Before subscribing, I ensure that the subscription handlers will be invoked on the UI thread (since I will be changing user interface elements). Finally, I subscribe to the IObservable interface and provide it two of the three possible handlers for handling the response: the OnNext handler and the OnError handler (the OnCompleted handler is not required in this case). On a successful response, the DataItems property on the GridViewModel is populated with the items to display. On an error response, I simply set the error message properties which will, in turn, be displayed below the grid.

Now let’s look at a more complicated example.

The “views” for the data grid are defined in XML files located on the web server. The GridService class is used to fetch both the list of all views as well as the individual views.

        private const string ListFileName = "/Data/Settings.xml";
        private readonly Uri WebSiteUri = HtmlPage.Document.DocumentUri;

        public IObservable<ViewSettingsCollection> GetViewSettingsCollection()
        {
            Uri listFileUri = new Uri( WebSiteUri, ListFileName );
            WebRequest httpReq = HttpWebRequest.Create( listFileUri );
            return
                Observable
                    .FromAsyncPattern<WebResponse>( httpReq.BeginGetResponse, httpReq.EndGetResponse )()
                    .Select( r =>
                    {

                        XDocument rspDoc;
                        using( Stream rspStream = r.GetResponseStream() )
                            rspDoc = XDocument.Load( rspStream );

                        return
                            rspDoc
                                .Element( "settings" )
                                .Elements( "items" )
                                .Select( items =>
                                    new ViewSettingsCollection {
                                        DefaultViewSettingsID = (int?)items.Attribute( "default" ),
                                        Items = new List<ViewSettingsCollectionItem>(
                                            items
                                                .Elements( "item" )
                                                .Select( item => new ViewSettingsCollectionItem {
                                                    ID = (int)item.Attribute( "id" ),
                                                    Description = (string)item.Attribute( "desc" ),
                                                    Link = (string)item.Attribute( "link" )
                                                })
                                        )
                                })
                                .Single();

                    } );
        }

An HttpWebRequest object is used to fetch the XML file from the Data folder on the web server. The FromAsyncPattern method is used to wrap the BeginGetResponse and EndGetResponse methods of the request object. When the response is received, LINQ queries are used to create a new ViewSettingsCollection object. The new instance of the ViewSettingsCollection object is then selected for the observable collection.

Back in the GridViewModel, the service is called to fetch the ViewSettingsCollection object.

            Service
                .GetViewSettingsCollection()
                .ObserveOnDispatcher()
                .Subscribe( vsc =>
                {
                    SettingsCollection = vsc;
                    SelectedSettings = SettingsCollection.GetDefaultSettings();
                },
                SetExceptionMessage );

Again, the subscription handlers will be invoked on the UI thread and the OnNext handler will simply set properties on the GridViewModel. Notice that the SelectedSettings property is also set. When this property is changed, either here or when the user changes the selected view in the drop-down box, the setter for this property calls the grid service to get the details of the view.

        public ViewSettingsCollectionItem SelectedSettings
        {
            get { return selectedSettings; }
            set
            {
                selectedSettings = value;
                OnSelectedSettingsChanged();
                OnPropertyChanged( "SelectedSettings" );
            }
        }

        ...

        private void OnSelectedSettingsChanged()
        {
            if( SelectedSettings != null )
            {
                Service
                    .GetViewSettings( SelectedSettings )
                    .ObserveOnDispatcher()
                    .Subscribe( vs =>
                    {
                        ViewSettings = vs;
                    },
                    SetExceptionMessage );
            }
        }

This allows us to set up the columns to display in the data grid.  I’ll examine this issue next.

July 24, 2010

MVVM – MEF – Reactive Data Grid – Part II

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 11:12 pm

The MVVMGrid application is architected using the Model-View-ViewModel (MVVM) pattern. So here are the parts:

  • GridView.xaml – this is the view containing the data grid. Visual elements are bound to data contained within the GridViewModel. The GridView knows about the properties that the GridViewModel exposes.
  • GridViewModel – this is the view model which is used as the DataContext of the GridView. The GridViewModel knows nothing about the GridView class.
  • GridData, ViewSettings, etc. – these are classes that define the data model that the application uses.
  • GridService – this is the class that provides the services to fetch the grid data and the view information.

We use the Managed Extensibility Framework (MEF) to stitch these pieces together. First, we need to create a CompositionContainer to handle the imports and exports of the various components. In this application, we only need to look in the one assembly for components, so only a single AssemblyCatalog is needed.

    public partial class App : Application
    {
        private CompositionContainer partsContainer;

        [Import]
        public UserControl MainView { get; set; }

        public App()
        {
            this.Startup += this.Application_Startup;
            this.Exit += this.Application_Exit;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();
        }

        private void Application_Startup( object sender, StartupEventArgs e )
        {
            partsContainer = new CompositionContainer( new AssemblyCatalog( typeof( App ).Assembly ) );
            partsContainer.SatisfyImportsOnce( this );

            RootVisual = MainView;
        }

Normally in Silverlight, we would set our RootVisual to a new UserControl we create in the application startup. Here, we allow MEF to find our root visual for us. How does it know which UserControl to create? The App class defines an Import attribute with a type of UserControl. The SatisfyImportsOnce method goes into the catalog we’ve supplied to it and finds a UserControl that is marked for export. Our GridView control is marked with the Export attribute and thus MEF is able to find it.

    [Export(typeof(UserControl))]
    public partial class GridView : UserControl
    {
        [Import]
        public GridViewModel ViewModel
        {
            get { return DataContext as GridViewModel; }
            set { DataContext = value; OnViewModelChanged(); }
        }


Similarly, our GridViewModel is imported into our GridView, setting it as the DataContext for the view and thus enabling the data binding to work. The GridViewModel then imports the GridService.

    [Export]
    public class GridViewModel : INotifyPropertyChanged, IPartImportsSatisfiedNotification
    {
        [Import]
        public GridService Service { get; set; }

Notice that the GridViewModel implements a couple of important interfaces, INotifyPropertyChanged and IPartImportsSatisfiedNotification. The first interface is used in data binding.  When properties on the GridViewModel change, the PropertyChanged event is fired. This allows data bindings to know that the data that is bound has changed and the user interface element needs to be updated.

The second interface is related to MEF. If an exported component implements this interface, then MEF will call the OnImportsSatisfied method on the object when all of the imports for the object have been satisified and the object is safe to use. In our application, we use this method to begin retrieving the data from our services that will be used in the data grid.

The source code for this project can be downloaded from CodePlex.

MVVM – MEF – Reactive Data Grid – Part I

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 10:27 pm

I want to explore some of the issues with MVVM, MEF, and Reactive Extensions in developing .NET applications. I’ve created an example Silverlight application that employs these technologies in some interesting ways. I’ll use this example as the basis for our discussion.

First, an overview.

The application displays a simple data grid. The grid itself has a defined set of columns that it can display (bound to the data elements that it knows the data source contains). The actual columns that are displayed and the order of these columns can be modified by selecting a different “view” for the data grid. The user selects the view through a drop-down box above the grid.

MVVMGrid

For this example application, the data is built using random values for few data types: strings, boolean, and DateTime. The views are requested from the server and update the visibility, display order, and width of the grid columns. Even though actual web services are not used, this application provides a simulated service interface to obtain these data elements.

This project was built using Visual Studio 2010. The source code can be downloaded from CodePlex.

Here are the links to the posts in this series:

Create a free website or blog at WordPress.com.