The last Visibility Converter

Posted Monday, April 08, 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

Common Problem with Notifications from a View Model

Posted Monday, March 28, 2011 by

In the MVVM (Model, View, ViewModel) pattern in xaml based applications (Windows Phone 7, Silverlight and WPF) there are two different ways a view model will notify the view of changes. If you're unaware of the differences you can end up wondering why your changes aren't being reflected in your view.

The most common is implementing the INotifyPropertyChanged interface, the other is through the INotifyCollectionChanged interface. Most developers really won't use second interface but its useful to know about and how it affects your view model.

When you bind to a property on your view model the binding infrastructure will listen for INotifyPropertyChanged events from your view model for that property.

If the type of the property implements INotifyCollectionChanged (such as ObservableCollection) it will also listen for events off that property.

The distinction is important because a common pattern with developers is implement collection properties as automatic properties. This however limits the way you can interact with the collection such that changes will be reflected in the view.

public class CorrectViewModel : ViewModelBase

{

    public CorrectViewModel()

    {

        Items = new ObservableCollection<Item>();??

    }

 

    public ObservableCollection<Item> Items

    {

        get; set;

    }

 

    public void Add()

    {

        var items = GetItems();

 

        Items.Clear(); // Collection changed raised

 

        foreach(var item in items)

        {

            Items.Add(item); // Collection changed raised

        }

    }

 

    private IEnumerable<Item> GetItems()

    {

        return from i in Enumerable.Range(0, 10)

                select new Item

                {

                    Name = "Item: " + i

                };

    }

}

 

public class IncorrectViewModel : ViewModelBase

{

    public IncorrectViewModel()

    {

        Items = new ObservableCollection<Item>();

    }

 

    public ObservableCollection<Item> Items

    {

        get;

        set;

    }

 

    public void Add()

    {

        var items = GetItems();

 

        Items = new ObservableCollection<Item>(); // No notification raised

 

        foreach(var item in items)

        {

            // Collection changed raised but view unaware of new collection

            Items.Add(item);

        }

    }

 

    private IEnumerable<Item> GetItems()

    {

        return from i in Enumerable.Range(0, 10)

                select new Item

                {

                    Name = "Item: " + i

                };

    }

}

Notice the difference between the correct view model and the incorrect one is that the correct one alters the already existing collection and the view is notified with INotifyCollectionChanged events. The incorrect view model replaces the collection with an entirely new one, but because the automatic property doesn't fire an INotifyPropertyChanged event the changes will not be reflected in the view.

The incorrect view model can be fixed by either modifying the Add method to not replace the collection but modify it in place, or to modify the Items collection to fire property changed events.

Useful Value Converters

Posted Saturday, January 29, 2011 by

Value converters are a really useful part of the xaml binding infrastructure, they work in Windows Phone 7, Silverlight and WPF. As we work on more and more projects in this space we build up a library of useful value converters. I'd like to illustrate some of the ones I use here.

Bitmap Image Converter

public class BitmapImageConverter : IValueConverter

{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    {

        if(value is string)

            return new BitmapImage(new Uri((string)value, UriKind.RelativeOrAbsolute));

 

        if(value is Uri)

            return new BitmapImage((Uri)value);

 

        throw new NotSupportedException();

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    {

        throw new NotSupportedException();

    }

}

Boolean To Visibility Converter

public class BooleanToVisibilityConverter : IValueConverter

{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    {

        if(value == null)

            return Visibility.Collapsed;

 

        var isVisible = (bool)value;

 

        return isVisible ? Visibility.Visible : Visibility.Collapsed;

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    {

        var visiblity = (Visibility)value;

 

        return visiblity == Visibility.Visible;

    }

}

Colour To Brush Converter

public class ColorToBrushConverter : IValueConverter

{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    {

        if(value == null)

            return new SolidColorBrush(Color.FromArgb(0, 0, 0, 0));

 

        if(value is Color)

            return new SolidColorBrush((Color)value);

 

        if(value is string)

            return new SolidColorBrush(Parse((string)value));

 

        throw new NotSupportedException("ColorToBurshConverter only supports converting from Color and String");

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    {

        throw new NotSupportedException();

    }

 

    private static Color Parse(string color)

    {

        var offset = color.StartsWith("#") ? 1 : 0;

 

        var a = Byte.Parse(color.Substring(0 + offset, 2), NumberStyles.HexNumber);

        var r = Byte.Parse(color.Substring(2 + offset, 2), NumberStyles.HexNumber);

        var g = Byte.Parse(color.Substring(4 + offset, 2), NumberStyles.HexNumber);

        var b = Byte.Parse(color.Substring(6 + offset, 2), NumberStyles.HexNumber);

 

        return Color.FromArgb(a, r, g, b);

    }

}

Simple Type Converter

public class SimpleTypeConverter : IValueConverter

{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    {

        return System.Convert.ChangeType(value, targetType, CultureInfo.CurrentCulture);

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    {

        return System.Convert.ChangeType(value, targetType, CultureInfo.CurrentCulture);

    }

}

String Format Converter

public class StringFormatConverter : IValueConverter

{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    {

        if(parameter == null && value == null)

            return String.Empty;

 

        if(parameter == null)

            return value.ToString();

 

        return String.Format(CultureInfo.CurrentCulture, parameter.ToString(), value);

    }

 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    {

        throw new NotSupportedException();

    }

}

One nice thing about value converters is because they're simple pluggable pieces of code they've easy to unit test. The unit tests for our Boolean To Visibility Converter may look like.

[TestClass]

public class BooleanToVisibilityConverterFixture

{

    [TestMethod]

    public void ConvertWithNullValueReturnsCollapsed()

    {

        var converter = new BooleanToVisibilityConverter();

        var value = converter.Convert(null, typeof(Visibility), null, CultureInfo.CurrentCulture);

 

        Assert.AreEqual(Visibility.Collapsed, value);

    }

 

    [TestMethod]

    public void ConvertWithFalseReturnsCollapsed()

    {

        var converter = new BooleanToVisibilityConverter();

        var value = converter.Convert(false, typeof(Visibility), null, CultureInfo.CurrentCulture);

 

        Assert.AreEqual(Visibility.Collapsed, value);

    }

 

    [TestMethod]

    public void ConvertWithTrueReturnsVisible()

    {

        var converter = new BooleanToVisibilityConverter();

        var value = converter.Convert(true, typeof(Visibility), null, CultureInfo.CurrentCulture);

 

        Assert.AreEqual(Visibility.Visible, value);

    }

 

    [TestMethod]

    public void ConvertBackWithVisibleReturnsTrue()

    {

        var converter = new BooleanToVisibilityConverter();

        var value = converter.ConvertBack(Visibility.Visible, typeof(bool), null, CultureInfo.CurrentCulture);

 

        Assert.AreEqual(true, value);

    }

 

    [TestMethod]

    public void ConvertBackWithCollapsedReturnsFalse()

    {

        var converter = new BooleanToVisibilityConverter();

        var value = converter.ConvertBack(Visibility.Collapsed, typeof(bool), null, CultureInfo.CurrentCulture);

 

        Assert.AreEqual(false, value);

    }

I tend to create a resource dictionary named Converters.xaml to hold references to all the converters I need.

<ResourceDictionary

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

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

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

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

   xmlns:c="clr-namespace:CompiledExperience.Kore.Client.Converters"

   mc:Ignorable="d">

 

    <c:BitmapImageConverter x:Key="BitmapImage"/>

    <c:BooleanToVisibilityConverter x:Key="BooleanToVisiblity"/>

    <c:ColorToBrushConverter x:Key="ColorToBrush"/>

    <c:SimpleTypeConverter x:Key="SimpleType"/>

    <c:StringFormatConverter x:Key="StringFormat"/>

 

</ResourceDictionary>

Once that's created then you modify your Binding statements to use the Converter.

<Rectangle Fill="{Binding Colour, Converter={StaticResource ColourToBrushConverter}}" Width="12" Height="72" />

If you're using Expression Blend to wire up your bindings then you can select the Converter from the provided drop down.

Page 1 of 212>

Professional Windows App Development