Views for types that aren't View Models

Posted Thursday, February 20, 2014 by

In most MVVM style frameworks we're used of seeing pairs of views and view models, RepositoryView and RepositoryViewModel for instance. We may sometimes have view models without views (such as GroupViewModel) but how do we deal with instances when we want views for types that aren't view models?

I'm currently redesigning and rebuilding bits of Hub Bug and making use of the Octokit.NET library from GitHub. This awesome nuget package provides 90% of what I'd call my model. Often view models wrap these model types when bound to the view. Yet some of the smaller types such as Label and User may be bound directly to the view inside list boxes or form small parts of a large view. Creating view models just so Caliburn.Micro's ViewLocator can locate the correct view feels like a waste as the view models don't add any functionality and create unnecessary ceremony.

By default Caliburn.Micro won't know what to do with these types, they don't end in "ViewModel" so it won't find any views. Even if it did it would try to locate them in the Octokit assembly which definitely isn't something we want.

What we want to do is give the ViewLocator a hint that when it tries to locate views for certain types to look somewhere where it wouldn't normally look. In this case When it locates a view for Octokit.User I want to it to use HubBug.App.Views.Octokit.UserView. In application set up (either the Bootstrapper or the Application depending on platform) I have the following:

ViewLocator.NameTransformer.AddRule(

    @"^Octokit\.(\w*)",

    @"HubBug.App.Views.Octokit.${1}View");

This uses lovely old regular expressions to transform the view model type to the view type, that's all it takes.

What's very cool about this is that I now have a consistent way to display Users, whether I'm binding to a ComboBox or it's part of a Grid. Caliburn makes sure I'm using the correct view in every instance.

For instance any where I want to display a user I can drop in the following xaml, tweak the binding appropriate to the view and I'm done.

<ContentControl micro:View.Model="{Binding Repository.Owner}" FontSize="{StaticResource ControlContentThemeFontSize}"/>

ViewLocator has quite a few customisation options, take a look and see how you can streamline your app development.

The last Visibility Converter

Posted Monday, April 8, 2013 by

The Boolean to Visibility Converter is as close you’re going to get to bread and butter in the xaml frameworks. You need it in almost every app and almost every framework has one, even the Windows 8 project templates come with one.

The trouble is that a lot of these implementations are naïve and simplistic, and soon enough you’ll be writing “Inverse Boolean to Visibility Converter”, “Int32 to Visibility Converter” etc. Ultimately these all exhibit very similar behavior, convert the default value (false, 0, null etc.) to Collapsed and anything else to Visible or do the same but inversed. Having over a dozen converters for all the implementations becomes a pain to manage and awkward.

So let’s build the last Visibility Converter you’ll need, we’ve already defined what the behavior should be, “Convert the default value to Collapsed and everything else to Visible”. The first thing we need to do is determine the default value for a type. If the type is a value type (int, boolean etc.) then we create an instance of the type otherwise the type is a reference type so the default value is null. Below is an example of this as an extension method for WinRT.

public static object GetDefaultValue(this Type type)

{

    return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null;

}

Now we can create our converter, we’ll want to have two properties to customize the behavior, the first, Inverse is pretty simplistic, the second, SupportIsNullOrEmpty is to deal with the one exception to our rules above and that’s string. The default value for string is null but we’ll typically want to treat an empty string as null, so we’ll add a second property SupportIsNullOrEmpty to be able to turn on or off dealing empty strings as null (it’ll be on by default).

public class VisibilityConverter : IValueConverter

{

    public VisibilityConverter()

    {

        SupportIsNullOrEmpty = true;

    }

 

    public bool Inverse

    {

        get;

        set;

    }

 

    public bool SupportIsNullOrEmpty

    {

        get; set;

    }

 

    public object Convert(object value, Type targetType, object parameter, string language)

    {

        bool visible;

 

        if (value is string && SupportIsNullOrEmpty)

        {

            visible = !String.IsNullOrEmpty(value.ToString());

        }

        else

        {

            var defaultValue = value != null ? value.GetType().GetDefaultValue() : null;

 

            visible = !Equals(value, defaultValue);

        }

 

        if (Inverse)

            visible = !visible;

 

        return visible ? Visibility.Visible : Visibility.Collapsed;

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, string language)

    {

        throw new NotSupportedException();

    }

}

The only downside to this approach is that ConvertBack can’t be implemented sensibly but to be honest I’ve never found a reason to have a * to Visibility converter to need it (that’s not to say there aren’t some).

Storyboards in Caliburn Micro

Posted Monday, November 14, 2011 by

In my previous post I talked about the benefits of using co-routines in Caliburn Micro to ease any interactions with the View from the View Model. In that case it was the use of the Visual State Manager; in this post we’ll cover managing storyboards and animation.

We’ll use code from an older post around how to create one off event handlers. What I’ve done is encapsulate that logic into an extension method ToObservable.

public static IObservable<IEvent<EventArgs>> ToObservable(this Storyboard storyboard)

{

    if(storyboard == null)

        throw new ArgumentNullException("storyboard");

 

    return Observable.FromEvent((EventHandler<EventArgs> e) => new EventHandler(e),

                                e => storyboard.Completed += e,

                                e => storyboard.Completed -= e);

}

In the BeginStoryboardResult we verify the view is a FrameworkElement (and therefore can contain Resources). We then load the Storyboard from the Resources collection. Using the extension method we wire the Completed event of the Storyboard to the completion of the IResult.

public class BeginStoryboardResult : ResultBase

{

    private readonly string storyboardName;

 

    public BeginStoryboardResult(string storyboardName)

    {

        this.storyboardName = storyboardName;

    }

 

    public string StoryboardName

    {

        get { return storyboardName; }

    }

 

    public override void Execute(ActionExecutionContext context)

    {

        if(!(context.View is FrameworkElement))

            throw new InvalidOperationException("View must be a framework element to use BeginStoryboardResult");

 

        var view = (FrameworkElement)context.View;

 

        if(!view.Resources.Contains(StoryboardName) || !(view.Resources[StoryboardName] is Storyboard))

            throw new InvalidOperationException(String.Format("View doesn't the contain a Storyboard with the key {0} as a resource", StoryboardName));

 

        var storyboard = (Storyboard)view.Resources[StoryboardName];

 

        storyboard.ToObservable().Take(1)

            .Subscribe(e => OnCompleted());

 

        storyboard.Begin();

    }

}

After that it’s pretty much just starting the actual storyboard.

Again one of the main benefits of result an IResult like this is we still maintain separation between the view model and the view, we can now create unit tests that test how the view model plays storyboards without requiring the actual storyboard.

Useful WP7 App for Developers

Posted Wednesday, March 30, 2011 by

As a developer for Windows Phone 7 one app I've found incredibly useful is AppTracker from Very Software. It quickly brings together all reviews for an app from the different marketplaces.

It quickly gives me an idea of how new versions of To Do Today are performing and also when there are new reviews.

I highly recommend you check it out on the Marketplace

Page 1 of 3123>

Professional Windows App Development