Windows Phone 7 Notification Control

One of the controls I felt was missing out of the box for the Windows Phone 7 was a Notification control similar to how Toast notifications are displayed. As part of an application I'm working on I need a control like this with some of the animation effects the toast has.

On a side note the Coding4Fun Toolkit has a more fully featured control but lacks good view model integration (to be fair 90% of controls do).

It has three properties, Title and Text (I'll most likely be adding an icon at a later date) are fairly self explanatory, the third OnDimiss is a callback when the notification is dismissed by a tap.

<controls:Notification x:Name="Notification" />

From the code behind it displayed with the following calls.

private void OnDisplay(object sender, RoutedEventArgs e)

{

    Notification.Display("Title", "Text", () =>

    {

        MessageBox.Show("Dismissed", "The notification control has been dimissed", MessageBoxButton.OK);

    });

}

 

private void OnDismiss(object sender, RoutedEventArgs e)

{

?Notification.Dismiss();

}

From a view model we can expose a unit testable notification source and bind that to the control.

public class NotificationViewModel

{

    public NotificationViewModel()

    {

        Notifications = new NotificationSource();

    }

 

    public INotificationSource Notifications

    {

        get;

        set;

    }

 

    public void Display()

    {

        Notifications.Display("Security", "Tap to authenticate the application", () =>

            {

                MessageBox.Show("Authenticated", "The application has been authenticated against the server", MessageBoxButton.OK);

            });

    }

}

The source code here contains the code for this notification control as well as the Status Indicator control.

Shout It Kick It submit to reddit

Windows Phone 7 Status Indicator Control

A lot of the built in applications have really subtle transitions and effects that really add polish to an existing application. In the process of building a new application I've been working on mimicking those controls. The first I'm releasing is the Status Indicator most commonly seen in the email application.

It's a fairly simple control that has two properties Text and InProgress controlling both the displayed status and whether the progress bar should be displayed. The control exposes two methods Display and Clear to easily set the properties of the control as well as manage state for nice animated transitions between the two.

<controls:StatusIndicator x:Name="Status" />

private void OnDisplay(object sender, RoutedEventArgs e)

{

    Status.Display("Loading...", true);

}

 

private void OnClear(object sender, RoutedEventArgs e)

{

    Status.Clear();

}

One thing that frustrates me when dealing some out of the box controls is that there's often no nice way to interact with the control when using some sort of UI separation pattern such as MVVM. To deal with this the control exposes a Source property, you can then have a property on your view model that implements the IStatusSource interface and will allow you to determine the properties and state of the control from your view model. It's all very unit testable as well!

<controls:StatusIndicator Source="{Binding Status}" />

public class StatusViewModel

{

    public StatusViewModel()

    {

        Status = new StatusSource();

    }

 

    public IStatusSource Status

    {

        get; set;

    }

 

    public void Display()

    {

        Status.Display("Updating", true);

    }

 

    public void Finish()

    {

        Status.Display(String.Format("Updated {0:d}", DateTime.Now), false);

    }

}

Of course here's the source code.

Shout It Kick It submit to reddit

Useful Value Converters

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.

Shout It Kick It submit to reddit

Using styles and resources to simplify your xaml

The Windows Phone 7 SDK comes with a lot of great templates, styles and resources to get you up and running quickly. But they can be a little repetitive, notice that every page set's a lot of it's own properties such as Font that will the same over all your pages. When you have a dozen or more pages (which isn't that hard to do) you're going to be repeating a lot of xaml.

Also if you're taking advantage of the TransitionFrame from the Silverlight Toolkit then you also be needing to specify transitions for each page, wouldn't it be better to just define these once and reuse them?

A much better solution is to create a set of styles and resources that build on the existing styles in order simply the xaml in any given page.

In order to simply keep this nice and simple we'll create a separate resource dictionary for our new styles to avoid cluttering App.xaml. The Phone SDK doesn't have an item template for Resource Dictionary, so I end up just selecting new text file and giving it the extension of .xaml. Once created I paste the following code in it to set it up as an empty resource file.

<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"

   mc:Ignorable="d">

 

</ResourceDictionary>

We then modify App.xaml to include the merged dictionary.

<Application.Resources>

    <ResourceDictionary>

        <ResourceDictionary.MergedDictionaries>

            <ResourceDictionary Source="Resources/Styles.xaml"/>

        </ResourceDictionary.MergedDictionaries>

    </ResourceDictionary>

</Application.Resources>

Here's the default Style I use for my pages, these can be overridden in the page itself so you can set a few things that may have exceptions later on.

<Style x:Key="DefaultPage" TargetType="phone:PhoneApplicationPage">

    <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>

    <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeNormal}"/>

    <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>

    <Setter Property="SupportedOrientations" Value="PortraitOrLandscape"/>

    <Setter Property="Orientation" Value="Portrait"/>

    <Setter Property="shell:SystemTray.IsVisible" Value="True"/>

    <Setter Property="toolkit:TransitionService.NavigationInTransition">

        <Setter.Value>

            <toolkit:NavigationInTransition>

                <toolkit:NavigationInTransition.Backward>

                    <toolkit:TurnstileTransition Mode="BackwardIn"/>

                </toolkit:NavigationInTransition.Backward>

                <toolkit:NavigationInTransition.Forward>

                    <toolkit:TurnstileTransition Mode="ForwardIn"/>

                </toolkit:NavigationInTransition.Forward>

            </toolkit:NavigationInTransition>

        </Setter.Value>

    </Setter>

    <Setter Property="toolkit:TransitionService.NavigationOutTransition">

        <Setter.Value>

            <toolkit:NavigationOutTransition>

                <toolkit:NavigationOutTransition.Backward>

                    <toolkit:TurnstileTransition Mode="BackwardOut"/>

                </toolkit:NavigationOutTransition.Backward>

                <toolkit:NavigationOutTransition.Forward>

                    <toolkit:TurnstileTransition Mode="ForwardOut"/>

                </toolkit:NavigationOutTransition.Forward>

            </toolkit:NavigationOutTransition>

        </Setter.Value>

    </Setter>

</Style>

We can apply that any page we create now and strip out a lot of the repetitive styling code.

Style="{StaticResource DefaultPage}"

So what other things can we push into styles and resources? Pretty much anything we want. A few examples from To Do Today that are resources include.

  • Application name, resources can be strings.
  • Data templates for List Pickers
  • Value converters
  • Style extensions for things such as form labels.

Shout It Kick It submit to reddit

Using Windows Phone Gestures as Triggers

The Silverlight Toolkit for Windows Phone 7 made it really useful for apps to deal with simple gestures such as tap, pinch and swipe. Wiring these gestures to event handlers is incredibly simple with the code below.

<toolkit:GestureService.GestureListener>

    <toolkit:GestureListener DoubleTap="OnDoubleTap" />

</toolkit:GestureService.GestureListener>

Sometimes however we'll want to use gestures to trigger actions in Blend, this could be for simple interactivity when tapped trigger a storyboard to wiring up an Model, View, ViewModel framework such as Caliburn Micro or MVVMLight. To do this we'll need to create some Triggers.

For gestures that don't provide extra information such as Tap, Hold and Double Tap a simple trigger will work for all three, we'll provide and enumeration to let the user select what gesture they require.

public enum Gesture

{

    Tap,

    Hold,

    DoubleTap

}

public class GestureServiceTrigger : TriggerBase<FrameworkElement>

{

    public Gesture Gesture

    {

        get; set;

    }

 

    protected override void OnAttached()

    {

        var listener = GestureService.GetGestureListener(AssociatedObject);

 

        switch(Gesture)

        {

            case Gesture.Tap:

                listener.Tap += OnGesture;

                break;

            case Gesture.Hold:

                listener.Hold += OnGesture;

                break;

            case Gesture.DoubleTap:

                listener.Flick += OnGesture;

                break;

        }

    }

 

    protected override void OnDetaching()

    {

        var listener = GestureService.GetGestureListener(AssociatedObject);

 

        switch(Gesture)

        {

            case Gesture.Tap:

                listener.Tap -= OnGesture;

                break;

            case Gesture.Hold:

                listener.Hold -= OnGesture;

                break;

            case Gesture.DoubleTap:

                listener.Flick -= OnGesture;

                break;

        }

    }

 

    private void OnGesture(object sender, GestureEventArgs e)

    {

        e.Handled = true;

 

        InvokeActions(e);

    }

}

Windows Phone 7 Gesture as a Trigger

Once it's compiled you can use Blend to drag on any Action from the Assets Panel, then select the GestureTrigger for the Action.

For the Flick gesture we'll create a separate TriggerAction that also lets the user select the Using Windows Phones Gestures as Triggersdirection of the flick.

public class FlickGestureServiceTrigger : TriggerBase<FrameworkElement>

{

    public Orientation Direction

    {

        get; set;

    }

 

    protected override void OnAttached()

    {

        var listener = GestureService.GetGestureListener(AssociatedObject);

 

        listener.Flick += OnFlick;

    }

 

    protected override void OnDetaching()

    {

        var listener = GestureService.GetGestureListener(AssociatedObject);

 

        listener.Flick -= OnFlick;

    }

 

    private void OnFlick(object sender, FlickGestureEventArgs e)

    {

        if(e.Direction != Direction)

            return;

 

        e.Handled = true;

 

        InvokeActions(e);

    }

}

Shout It Kick It submit to reddit