ASP.NET MVC: A Framework done right

Lately I've been doing a lot of miscellaneous work in ASP.NET MVC, Silverlight and WPF and some of the differences in approach by their respective development teams is infuriating. The MVC libraries have some of the best examples of how to build a solid library while still allowing developers to override granular pieces of behavior. The amount of extensiblity currently available is amazing (with more coming for validation in the next release).

Compared to this WPF has way too much stuff marked as internal, private or sealed, a good example of how this inhibits some great innovation is "Forget about extending WPF data binding support".

I'm not a huge fan of the IDataErrorInfo interface, it being a kind of hold over from .NET 1.0 DataSet days, so wanted to create similar interface to work with a validation system seperate from the model. WPF has built in support for IDataErrorInfo through the DataErrorValidationRule and my initial approach was to build something similar for my own interface.

Sadly 90% of the code required for DataErrorValidationRule depends on the internals of BindingExpression so no dice. I'm finding it very frustating that something as simple as DataErrorValidationRule couldn't be implemented by a developer outside of Microsoft, what hope do the rest of us have at making meaningful extensions to the way WPF / Silverlight works?

The best example I've found at doing this is "Automatically validating business entities in WPF using custom binding and attributes" which requires a good chunk of wiring code thanks to the BindingDecoratorBase class.

A bit of rant I'm afraid, but had to get some frustration at the lack of extensiblty in WPF / Silverlight validation.

Shout It Kick It submit to reddit

Building a Visual Studio Style Tabbed Interface with Caliburn

In the application I'm currently working I'm using an interface patten similar to Visual Studio with multiple tabs (work items) open at any one time. Caliburn has some great utiltity baked in for this style of application with their IPresenter Component Model.  Ultimately we'll be building something that looks like:

Visual Studio Style Tabs

Note: There was an issue in the v1 RTW that caused the following example to function incorrectly. The 1.1 branch in the Codeplex Repository correct this.

While this post is mainly targeting WPF a lot of it (minus the ContextMenu) can be used in Silverlight as well.

I'm going to assume you have some experience with setting up Caliburn and won't dig too much into that if not I'd start at the Caliburn documentation. By default Caliburn wants to use IServiceLocator to create the child presenters, I've created a simple IViewModelFactory to allow me to plug extra functionality into this process, for this article the implementation is as follows.

public class DefaultViewModelFactory : IViewModelFactory

{

    private readonly IServiceLocator serviceLocator;

 

    public DefaultViewModelFactory(IServiceLocator serviceLocator)

    {

        if(serviceLocator == null)

            throw new ArgumentNullException("serviceLocator");

 

        this.serviceLocator = serviceLocator;

    }

 

    public T Create<T>() where T : IPresenter

    {

        return serviceLocator.GetInstance<T>();

    }

}

We'll start with building the ViewModel for our application, we'll name it WorkspaceViewModel and because we'll be dealing with multiple "child" presenters and the idea of a current presenter we'll inherit this class from MultiPresenterManager. After that we'll add some methods to manage the child presenters, specially CloseThis, CloseAll and CloseAllButThis. We'll also create a dummy method to open a sample "child view", more like the WorkspaceViewModel will be responding to events from an event aggregator to open child views.

public class WorkspaceViewModel : MultiPresenterManager

{

    private readonly IViewModelFactory viewModelFactory;

 

    public WorkspaceViewModel(IViewModelFactory viewModelFactory)

    {

        this.viewModelFactory = viewModelFactory;

    }

 

    public void OpenSimpleViewModel()

    {

        var viewModel = viewModelFactory.Create<SimpleViewModel>();

 

        this.Open(viewModel);

    }

 

    public void CloseAll()

    {

        foreach(Presenter presenter in Presenters.ToList())

        {

            presenter.Close();

        }

    }

 

    public void ClosePresenter(Presenter selectedPresenter)

    {

        selectedPresenter.Close();

    }

 

    public void CloseAllButThis(Presenter selectedPresenter)

    {

        foreach(Presenter presenter in Presenters.ToList())

        {

            if(presenter == selectedPresenter)

                continue;

 

            presenter.Close();

        }

    }

}

Now we have the ViewModel we need to build the corresponding view. To we'll need a couple of things to begin with, obviously a TabControl to display our child presenters and little button to open new presenters.

<TabControl Grid.Row="1" x:Name="Presenters" ItemsSource="{Binding Presenters}" SelectedItem="{Binding CurrentPresenter}">

    <TabControl.ItemTemplate>

        <DataTemplate>

            <Grid>

                <Grid.ColumnDefinitions>

                    <ColumnDefinition Width="*" />

                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding DisplayName}" />

            </Grid>

        </DataTemplate>

    </TabControl.ItemTemplate>

    <TabControl.ContentTemplate>

        <DataTemplate>

            <ContentControl caliburn:View.Model="{Binding}" />

        </DataTemplate>

    </TabControl.ContentTemplate>

</TabControl>

This should be enough to get started, the button is attached to the OpenPresenter and when clicked the Presenters collection is changed which opens another tab in the TabControl.

<Button Margin="4" Content="Open" caliburn:Message.Attach="OpenSimpleViewModel"/>

Now lets hook up the familar context menu on the tabs, we'll need to do a bit of wrangling with the Message Target in Caliburn as we want to target the actual presenter and not the current DataContext (in the TabItem it's the child presenter and not the workspace presenter). The simplest way to do this is to bind to something outside the tab control. We can then attach messages for CloseThis, CloseAll and CloseAllButThis to each menu item.

<UserControl.Resources>

    <FrameworkElement x:Key="DataContextReference"/>

</UserControl.Resources>

<UserControl.DataContext>

    <Binding Mode="OneWayToSource" Path="DataContext" Source="{StaticResource DataContextReference}"/>

</UserControl.DataContext>

<ContextMenu caliburn:Action.TargetWithoutContext="{Binding DataContext, Source={StaticResource DataContextReference}}">

    <MenuItem Header="Close" caliburn:Message.Attach="ClosePresenter($dataContext)" />

    <MenuItem Header="Close All But This" caliburn:Message.Attach="CloseAllButThis($dataContext)" />

    <MenuItem Header="Close All" caliburn:Message.Attach="CloseAll" />

</ContextMenu>

Now the only thing we've left to do is recreate the the drop down and close box in the top right corner. The combobox is bound to the same values as the tab control and the button is attached to the CloseThis method using the currently selected tab as the parameter.

<Button Margin="4" Content="x" caliburn:Message.Attach="ClosePresenter(Presenters.SelectedItem)"/>

<ComboBox ItemsSource="{Binding Presenters}" SelectedItem="{Binding CurrentPresenter}" Margin="4" DisplayMemberPath="DisplayName"/>

The full xaml for the view is as follows.

<UserControl

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:caliburn="http://www.caliburnproject.org"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    x:Class="CompiledExperience.Azure.Explorer.Client.Shell.Views.WorkspaceView"

   mc:Ignorable="d" d:DesignWidth="503.507" d:DesignHeight="384.96"

   >

    <UserControl.Resources>

        <FrameworkElement x:Key="DataContextReference"/>

    </UserControl.Resources>

    <UserControl.DataContext>

        <Binding Mode="OneWayToSource" Path="DataContext" Source="{StaticResource DataContextReference}"/>

    </UserControl.DataContext>

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto" />

            <RowDefinition/>

        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">

            <Button Margin="4" Content="Open" caliburn:Message.Attach="OpenSimpleViewModel"/>

            <Button Margin="4" Content="x" caliburn:Message.Attach="ClosePresenter(Presenters.SelectedItem)"/>

            <ComboBox ItemsSource="{Binding Presenters}" SelectedItem="{Binding CurrentPresenter}" Margin="4" DisplayMemberPath="DisplayName"/>

        </StackPanel>

        <TabControl Grid.Row="1" x:Name="Presenters" ItemsSource="{Binding Presenters}" SelectedItem="{Binding CurrentPresenter}">

            <TabControl.ItemTemplate>

                <DataTemplate>

                    <Grid>

                        <Grid.ContextMenu>

                            <ContextMenu caliburn:Action.TargetWithoutContext="{Binding DataContext, Source={StaticResource DataContextReference}}">

                                <MenuItem Header="Close" caliburn:Message.Attach="ClosePresenter($dataContext)" />

                                <MenuItem Header="Close All But This" caliburn:Message.Attach="CloseAllButThis($dataContext)" />

                                <MenuItem Header="Close All" caliburn:Message.Attach="CloseAll" />

                            </ContextMenu>

                        </Grid.ContextMenu>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="*" />

                        </Grid.ColumnDefinitions>

                        <TextBlock Text="{Binding DisplayName}" />

                    </Grid>

                </DataTemplate>

            </TabControl.ItemTemplate>

            <TabControl.ContentTemplate>

                <DataTemplate>

                    <ContentControl caliburn:View.Model="{Binding}" />

                </DataTemplate>

            </TabControl.ContentTemplate>

        </TabControl>

    </Grid>

</UserControl>

There's a good Stack Overflow question about styling the tab control like Visual Studio so I'll leave to that to someone with actual design skills but from here you're pretty much complete. Some stuff you'll most likely want to extend it with is dealing with duplicate tabs, you may not want to have two options tabs open and so on.

Shout it kick it on DotNetKicks.com

Shout It Kick It submit to reddit

Convention Based Event Aggregation in WPF and Silverlight Applications

One thing that can get pretty tedious very quickly in building composite UI's in Silverlight and WPF is wiring up an event aggregator. Typically if a service or view model wants to subscribe to an event in the application you would declare a dependency on IEventAggregator and call Subscribe on it passing the method to invoke when the event is published.

The Event Aggregator we'll be using for this example is based off something like the "Suck Less Event Aggregator" and the introduction of non generic methods required for this example. The interface for this example looks as follows.

public interface IEventAggregator

{

    void Subscribe<T>(Action<T> action);

    void Subscribe<T>(Action<T> action, bool keepSubscriberAlive);

 

    void Subscribe(Type eventType, Delegate action);

    void Subscribe(Type eventType, Delegate action, bool keepSubscriberAlive);

 

    void Unsubscribe<T>(Action<T> action);

    void Unsubscribe(Type eventType, Delegate action);

 

    void Publish<T>(T eventToPublish);

}

This seems pretty standard interface the problem is that over a whole applications worth of services and view models it's a lot of repetitious code to subscribe to all the events we're interested in. Lots of the view models will have dependencies on the event aggregator which could end mean a lot of repetitious unit tests.

What I'm going to try and show today is how to use some conventions to simplify your code. We'll adopt the convention that any public method on a View Model named "OnX" on a and has one parameter should be subscribed to an event (the type of the event will be based on the parameter type).

I'm currently using Caliburn to build my WPF applications but Prism is the same in this regard. Both don't have an explicit way to construct a new View Model, they simply want you to depend on the IServiceLocator or IUnityContainer and use it to Resolve your new ViewModel. Since we'll be wanting to insert some functionality into this process we'll create our own IViewModelFactory it'll be pretty simple and just shift the dependency on IServiceLocator into a specific place. The interface and implementation are.

public interface IViewModelFactory

{

    T Create<T>() where T : IPresenter;

}

 

public class DefaultViewModelFactory : IViewModelFactory

{

    private readonly IServiceLocator serviceLocator;

 

    public DefaultViewModelFactory(IServiceLocator serviceLocator)

    {

        if(serviceLocator == null)

            throw new ArgumentNullException("serviceLocator");

 

        this.serviceLocator = serviceLocator;

    }

 

    public T Create<T>() where T : IPresenter

    {

        var viewModel = serviceLocator.GetInstance<T>();

 

        return viewModel;

    }

}

We'll use the Decorator Pattern to add extra functionality to our View Model factory, our EventAggregatorViewModelFactory takes a dependency on another IViewModelFactory and uses that to actually create the viewModel, we'll simply do some extra work on the result before passing back to the caller. The Decorator pattern is great in this regard in that I can plug even more functionality into the pipeline of view model creation without requiring each piece of functionality to know about any of the others.

public class EventAggregationViewModelFactory : IViewModelFactory

{

    private readonly IViewModelFactory viewModelFactory;

    private readonly IEventAggregator eventAggregator;

 

    public EventAggregationViewModelFactory(IViewModelFactory viewModelFactory, IEventAggregator eventAggregator)

    {

        this.viewModelFactory = viewModelFactory;

        this.eventAggregator = eventAggregator;

    }

 

    public T Create<T>() where T : IPresenter

    {

        var viewModel = viewModelFactory.Create<T>();

 

        AttachSubscriptions(viewModel);

 

        return viewModel;

    }

 

    protected void AttachSubscriptions<T>(T viewModel) where T : IPresenter

    {

        var methods = from m in viewModel.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)

                      where IsEventSubscriptionMethod(m)

                      select m;

 

        foreach(var method in methods)

        {

            var eventType = method.GetParameters()[0].ParameterType;

            var delegateType = Expression.GetActionType(eventType);

            var @delegate = Delegate.CreateDelegate(delegateType, viewModel, method);

 

            eventAggregator.Subscribe(eventType, @delegate);

        }

    }

 

    protected virtual bool IsEventSubscriptionMethod(MethodInfo method)

    {

        return method.Name.StartsWith("On") && method.GetParameters().Length == 1;

    }

}

AttachSubscriptions is where the extra functionality is added, we first find all methods on the ViewModel that meet the convention (note the logic is in a method that can be overridden to change the convention). Once we have the methods we're interested in we create a delegate  (Action<T>) based on the event type and the method, we then call subscribe on the event aggreator using the event type and our delegate. Pretty simple really, the only complicated part is that most event aggregators don't come with a non generic interface, this could be avoided if you're ok with using reflection to invoke one of the generic methods instead.

I'm wiring the entire thing together using Ninject and the following code.

kernel.Bind<IViewModelFactory>().To<EventAggregationViewModelFactory>().InSingletonScope();

kernel.Bind<IViewModelFactory>().To<DefaultViewModelFactory>().WhenInjectedInto<EventAggregationViewModelFactory>().InSingletonScope();

With this in place we can now simplify an example view model from the following

public class DependentCustomerViewModel : Presenter

{

    private readonly IEventAggregator eventAggregator;

 

    public DependentCustomerViewModel(IEventAggregator eventAggregator)

    {

        this.eventAggregator = eventAggregator;

    }

 

    protected override void OnInitialize()

    {

        base.OnInitialize();

 

        eventAggregator.Subscribe<CustomerCreatedEvent>(OnCustomerCreated);

    }

 

    protected void OnCustomerCreated(CustomerCreatedEvent customerCreatedEvent)

    {

        // Business logic for customer creation

    }

}

to

public class ConventionCustomerViewModel : Presenter

{

    public void OnCustomerCreated(CustomerCreatedEvent customerCreatedEvent)

    {

        // Business logic for customer creation

    }

}

Shout it kick it on DotNetKicks.com

Shout It Kick It submit to reddit

Caliburn: Binding Conventions

Lately I've been playing around with Caliburn in WPF (and a little in Silverlight). One of the things I've really enjoyed is the convention based binding between the View and the ViewModel (Presenter). The default binding (DefaultBinder) helps with the more complex screen activiation by binding the Presenters collection and exposed Presenter objects on the ViewModel. It does all this binding based on the name of the control and the exposed property.

One thing it doesn't do is bind any simple exposed properties to the view, such has simple strings, collections and so forth. To implement this we'll create a new IBinder, to ensure we keep all the existing conventions we'll inherit our new ConventionBinder from DefaultBinder and override ApplyBindingConventions.

The logic for this will be fairly simple, we'll loop through all public instance variables on the model and look for a control of the same name. If we find one we'll look up which property to bind to and ensure the types are comparable, after that we create the Binding object based on the type of property we're binding to.

public class ConventionBinder : DefaultBinder

{

    private readonly Dictionary<Type, DependencyProperty> defaultProperties = new Dictionary<Type, DependencyProperty>

    {

        {typeof(TextBox), TextBox.TextProperty},

        {typeof(TextBlock), TextBlock.TextProperty},

        {typeof(ItemsControl), ItemsControl.ItemsSourceProperty}

    };

 

    public ConventionBinder(IServiceLocator serviceLocator, IActionFactory actionFactory, IMessageBinder messageBinder)

        : base(serviceLocator, actionFactory, messageBinder)

    {

    }

 

    protected override void ApplyBindingConventions(DependencyObject element, object model)

    {

        base.ApplyBindingConventions(element, model);

 

        if(!(element is FrameworkElement))

            return;

 

        foreach(var property in model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))

        {

            var bindingElement = FindControl(element, property.Name);

 

            if(bindingElement == null)

                continue;

 

            var defaultProperty = GetDefaultProperty(bindingElement.GetType());

 

            if(defaultProperty == null)

                continue;

 

            if(!defaultProperty.PropertyType.IsAssignableFrom(property.PropertyType))

                continue;

 

            BindingOperations.SetBinding(bindingElement, defaultProperty, new Binding(property.Name)

            {

                Mode = GetDirectionForProperty(property)

            });

        }

    }

 

    private DependencyProperty GetDefaultProperty(Type elementType)

    {

        do

        {

            if(defaultProperties.ContainsKey(elementType))

                return defaultProperties[elementType];

 

            elementType = elementType.BaseType;

 

        } while(elementType != null);

 

        return null;

    }

 

    protected virtual BindingMode GetDirectionForProperty(PropertyInfo property)

    {

        if(property.CanRead && property.CanWrite)

            return BindingMode.TwoWay;

 

        if(property.CanRead)

            return BindingMode.OneWay;

 

        if(property.CanWrite)

            return BindingMode.OneWayToSource;

 

        return BindingMode.Default;

    }

}

Now we just need to configure Caliburn to use the new binder. Now all properties will be bound to the controls with the appropriate name.

protected override void ConfigurePresentationFramework(PresentationFrameworkModule module)

{

    module.UsingBinder<ConventionBinder>();

}

The list of default properties is in no way exhaustive, just add to it when you want a new convention.

Shout it kick it on DotNetKicks.com

Shout It Kick It submit to reddit