Moved to dfbaskin.com
June 15, 2015
July 26, 2010
MVVM – MEF – Reactive Data Grid – Part IV
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
There are a number of ways to retrieve data from a web service. Silverlight provides this functionality through:
- WebClient, which uses an eventing interface.
- HttpWebRequest, which uses asynchronous operations.
- Windows Communications Foundation (WCF), which uses a customizable framework.
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
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
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.
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:
June 19, 2010
WCF Client Functionality and Silverlight
I’ve been looking into Windows Communications Foundation (WCF) functionality, specifically focusing on client features for .NET and Silverlight clients, as a foundation for creating business applications.
So why wouldn’t I just use the WebClient or HttpWebRequest classes? These classes are simple to understand and use. And they don’t introduce the complexity of the WCF framework.
This is true, of course, but WCF offers flexibility that business applications require to implement additional management and interoperability features. Specifically, WCF offers a host of extensibility points that business applications can make use of, such as custom validation, service logging, and data manipulation.
This flexibility is also important where business applications have to rely on outside services. Third-party REST and SOAP services are more ubiquitous and integrating with these services is a common business requirement. These services are a "black box" and your application must manage change that is beyond your control.
Here are a few links to information related to WCF client functionality:
- WCF offers a host of extensibility points, but I am currently focusing on WCF client applications.
- Since Silverlight is a lightweight version of the .NET runtime, many of the WCF features available to .NET applications are not available to Silverlight applications, though Silverlight does provide some helpful features to access web services. There are some additional examples on MSDN in the Silverlight Web Services Samples project.
- The WebHttpBinding class, along with the WebGet and WebInvoke attributes, can be used to access REST services. Unfortunately, this binding doesn’t exist in Silverlight.
- Silverlight only supports asynchronous service calls, so service contracts must be transformed to use an asynchronous signature.
- Even though Silverlight doesn’t support it out of the box, WCF features can be used to access both REST/POX services and JSON services.
- You don’t have to automatically generate a proxy for your service in Silverlight. You can define (or share) your service contracts within Silverlight and use the Channel Model to access the service.
- There is a WCF REST Starter Kit, but as of this writing it is still in Preview 2 state and hasn’t been updated in over a year.
- In the MSDN Code Gallery, there is a Web Application Toolkit for REST Services that provides some REST functionality.
So I hope to post some code examples soon exploring some aspects of WCF client programming.
Sequential Asynchronous Workflows in Silverlight Using Rx Framework
Jeremy Likness has an excellent post called Sequential Asynchronous Workflows in Silverlight using Coroutines. It seems like this example would also work well using the Reactive (Rx) Extensions Framework.
Here’s the source for an Rx handler of the RandomNumberService:
/// <summary> /// Once we have the references, start the workflow (the Reactive Way!) /// </summary> public void OnImportsSatisfied() { int alpha = 0, red = 0, green = 0, blue = 0; int[] maxValuesRequired = new int[]{ 128, 255, 255, 255 }; var colorWorkflow = Observable .ToObservable( maxValuesRequired ) .SelectMany( m => Observable .FromAsyncPattern<int, int>( ( maxValue, callback, state ) => { GetRandomNumberResult asyncResult = new GetRandomNumberResult(); Service.GetRandomNumber( maxValue, v => { asyncResult.SetResult( v ); callback( asyncResult ); } ); return asyncResult; }, ( asyncResult ) => { return (int)asyncResult.AsyncState; } )( m ) ) .ObserveOnDispatcher(); int colorIndex = 0; colorWorkflow .Subscribe( result => { switch( colorIndex++ ) { case 0: alpha = result + 128; break; case 1: red = result; break; case 2: green = result; break; case 3: blue = result; break; } }, ex => { }, () => { RectangleFill = new SolidColorBrush( new Color { A = (byte)alpha, B = (byte)blue, G = (byte)green, R = (byte)red } ); } ); }
I am wrapping the service call so that it follows the asynchronous service pattern, but this is the more common method of acessing services in a Silverlight application anyway.
Updated July 24, 2010:
I corrected the code by moving the setting of the RectangleFill property to the subscription’s completed method rather than in the Finally method. The latter is more designed for cleaning up after the subscription is completed.
August 1, 2009
MissingMethodException and WaitOne
July 25, 2009
Security Exception on Silverlight Client when Calling WCF Service
I kept getting a SecurityException when calling from my Silverlight client to a WCF service. I had double-checked WCF settings (I used basicHttpBinding, which is what Silverlight supports). I had created clientaccesspolicy.xml and crossdomain.xml files on the root of my web site. The meta data came up correctly for my service. Via Fiddler, I saw that the clientaccesspolicy.xml file was being retrieved successfully. But the call to the actual service never happened (the SecurityException was generated first).
While researching this error, I found this post. The solution worked for me, but it also bugged me. I found it hard to believe that the clientaccesspolicy.xml file just simply didn’t work.
And I was right. I did manage to get the clientaccesspolicy.xml to work. According to this MSDN article, the contents of the clientaccesspolicy.xml file might look like this:
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy>
The problem turns out to be the encoding. When I removed the attribute, encoding=”utf-8″, the Silverlight client happily called the service with no problem.
Ok, scratch that. I tried to reproduce the original problem by adding the encoding back to the clientaccesspolicy.xml, and … it works. I tried resetting the IIS server and clearing the browser cache, and now I can’t get it to fail again.
So, I’m still scratching my head about this.
June 16, 2009
“Resource not found for the segment …” Azure Exception
Using table storage within the Azure platform, a simple query was throwing an exception if the table entry was not found.
MyDataContext dataCtx = new MyDataContext( acctInfo ); var q = from entity in dataCtx.EmailAddresses where entity.PartitionKey == organizationID && entity.RowKey == address select entity; EmailAddress emailAddr = q.FirstOrDefault(); return emailAddr;
I had assumed that an exception would not be thrown if there were no matching entries. The FirstOrDefault() method should have just returned null, right?
Well, not so. Here is a forum message gives a bit of a clue about how the query is actually processed and whether an exception is thrown or an empty data set is returned.
For right now, you’ll have to wrap the query in an exception block:
MyDataContext dataCtx = new MyDataContext( acctInfo ); var q = from entity in dataCtx.EmailAddresses where entity.PartitionKey == organizationID && entity.RowKey == address select entity; EmailAddress emailAddr = null; try { emailAddr = q.FirstOrDefault(); } catch( DataServiceQueryException ex ) { } return emailAddr;