Monthly Archives: September 2012

Glyphs that can be Outlined in WPF

Most modern day applications use glyphs these days. A glyph in developer lingo is a monochromatic icon. In technologies like WPF that use render engines that can easily work with vectors, these glyphs tend to be in the form of vectors, so that UI designers and developers can use them in any form of scale without destroying any of the looks of the glyph.

While using glyphs, one of the things I wanted to do was bind some of them to boolean properties and have the glyph in it’s full colour when it was true, and revert back to an outlined form when it was false.

The example application has 2 glyphs and a CheckBox, both glyphs Outlined property are bound to the CheckBox’s IsChecked property, so that we can go from this state

blog5

To this state

blog6

The glyphs

The glyphs in this example were made in Adobe Illustrator, then imported into blend and the corresponding Path’s generated. They were then inserted into a Viewbox to allow scaling while retaining Blend’s absolute margin values for alignment. In the end what we have is a WPF vector glyph.

bell

<l:BaseGlyph x:Class="GlyphOutline.Bell"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:l="clr-namespace:GlyphOutline"
            mc:Ignorable="d" 
            d:DesignHeight="200" d:DesignWidth="200">

    <Viewbox Stretch="Fill">
        <Canvas x:Name="Icon" Height="64" Width="64">
            <Path Data="F1M32,6C34.205,6,36,7.797,36,10C36,12.203,34.205,14,32,14C29.795,14,28,12.203,28,10C28,7.797,29.795,6,32,6 M53,32C53,21.5,47.217,15.563,41.719,12.266C41.889,11.535,42,10.781,42,10C42,4.477,37.523,0,32,0C26.477,0,22,4.477,22,10C22,10.781,22.111,11.535,22.281,12.266C16.783,15.563,11,21.5,11,32C11,52,4,40,4,56L60,56C60,40,53,52,53,32" Height="56" Canvas.Left="0" Stretch="Fill" Canvas.Top="0" Width="64"/>
            <Path Data="F1M32,64C35.723,64,38.824,61.445,39.717,58L24.283,58C25.176,61.445,28.277,64,32,64" Height="6" Canvas.Left="21.783" Stretch="Fill" Canvas.Top="58" Width="18.434"/>
        </Canvas>
    </Viewbox>

</l:BaseGlyph>

calculator

<l:BaseGlyph x:Class="GlyphOutline.Calculator"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:l="clr-namespace:GlyphOutline"
            mc:Ignorable="d" 
            d:DesignHeight="200" d:DesignWidth="200">

    <Viewbox Stretch="Fill">
        <Canvas x:Name="Icon" Height="64" Width="64">
            <Path Data="F1M48,16C48,16.547,47.555,17,47,17L17,17C16.453,17,16,16.547,16,16L16,9C16,8.445,16.453,8,17,8L47,8C47.555,8,48,8.445,48,9z M44,31.5C42.07,31.5,40.5,29.938,40.5,28C40.5,26.07,42.07,24.5,44,24.5C45.937,24.5,47.5,26.07,47.5,28C47.5,29.938,45.937,31.5,44,31.5 M44,43.5C42.07,43.5,40.5,41.934,40.5,40C40.5,38.066,42.07,36.5,44,36.5C45.937,36.5,47.5,38.066,47.5,40C47.5,41.934,45.937,43.5,44,43.5 M44,55.5C42.07,55.5,40.5,53.934,40.5,52C40.5,50.066,42.07,48.5,44,48.5C45.937,48.5,47.5,50.066,47.5,52C47.5,53.934,45.937,55.5,44,55.5 M32,31.5C30.07,31.5,28.5,29.938,28.5,28C28.5,26.07,30.07,24.5,32,24.5C33.937,24.5,35.5,26.07,35.5,28C35.5,29.938,33.937,31.5,32,31.5 M32,43.5C30.07,43.5,28.5,41.934,28.5,40C28.5,38.066,30.07,36.5,32,36.5C33.937,36.5,35.5,38.066,35.5,40C35.5,41.934,33.937,43.5,32,43.5 M32,55.5C30.07,55.5,28.5,53.934,28.5,52C28.5,50.066,30.07,48.5,32,48.5C33.937,48.5,35.5,50.066,35.5,52C35.5,53.934,33.937,55.5,32,55.5 M20,31.5C18.07,31.5,16.5,29.938,16.5,28C16.5,26.07,18.07,24.5,20,24.5C21.937,24.5,23.5,26.07,23.5,28C23.5,29.938,21.937,31.5,20,31.5 M20,43.5C18.07,43.5,16.5,41.934,16.5,40C16.5,38.066,18.07,36.5,20,36.5C21.937,36.5,23.5,38.066,23.5,40C23.5,41.934,21.937,43.5,20,43.5 M20,55.5C18.07,55.5,16.5,53.934,16.5,52C16.5,50.066,18.07,48.5,20,48.5C21.937,48.5,23.5,50.066,23.5,52C23.5,53.934,21.937,55.5,20,55.5 M49,0L15,0C11.148,0,8,3.148,8,7L8,57C8,60.852,11.148,64,15,64L49,64C52.852,64,56,60.852,56,57L56,7C56,3.148,52.852,0,49,0" Height="64" Canvas.Left="9" Stretch="Fill" Canvas.Top="0" Width="48"/>

        </Canvas>
    </Viewbox>

</l:BaseGlyph>

The MainWindow

The layout of the main window is simple, it’s a stackpanel nested inside a Grid for aligning the glyphs and the CheckBox and a Viewbox to scale the CheckBox to a nice size.

<Window x:Class="GlyphOutline.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:GlyphOutline="clr-namespace:GlyphOutline"
        Title="MainWindow" Height="140" Width="150">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        
        <StackPanel Grid.Row="0"
                    Orientation="Horizontal"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center">

            <GlyphOutline:Bell Height="40"
                               Width="40"
                               Outlined="{Binding ElementName=OutlineCheck, Path=IsChecked}" />
            <GlyphOutline:Calculator Height="40"
                                     Width="40"
                                     Margin="20 0 0 0"
                                     Outlined="{Binding ElementName=OutlineCheck, Path=IsChecked}" />
        </StackPanel>
        
        <Viewbox Grid.Row="1"
                 Height="40"
                 Width="40"
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center">

            <CheckBox Name="OutlineCheck" />
        </Viewbox>
    </Grid>

</Window>

The Outlining

To implement the Outline feature I created a custom ContentControl that I called the “BaseGlyph”. This control hosts the Outlined property and it’s implementation.

First there’s the Outline property implementation with a callback defined to handle the conversion of styles on the paths when the property is either true or false.

public static readonly DependencyProperty OutlinedProperty = DependencyProperty.Register(
    "Outlined",
    typeof(Boolean),
    typeof(BaseGlyph),
    new UIPropertyMetadata(
        false, // default value for this DP
        OnOutlinedChanged)); // callback on change
                

private static void OnOutlinedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var caller = (BaseGlyph)d;

    if ((bool)e.NewValue)
    {
        foreach (var item in d.GetVisualDescendents().OfType<Path>())
            item.Style = (Style)caller.FindResource("OutlinePath");
    }
    else
    {
        foreach (var item in d.GetVisualDescendents().OfType<Path>())
            item.Style = (Style)caller.FindResource(typeof(Path));
    }
}

public Boolean Outlined
{
    get { return (Boolean)GetValue(OutlinedProperty); }
    set { SetValue(OutlinedProperty, value); }
}

There’s a bit of code on the constructor to create the ResourceDictionary that needs to be present to transform the Paths in the glyph in Shapes with thickness.

public BaseGlyph()
{
    // Build the Resource Dictionary
    Resources = new ResourceDictionary();

    var binding = new Binding();
    var style = new Style();

    // Ancestor Foreground Binding,
    binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(BaseGlyph), 1);
    binding.Path = new PropertyPath(ForegroundProperty);

    style.TargetType = typeof(Path);
    style.Setters.Add(new Setter(Shape.FillProperty, binding));

    var transparentBrush = new SolidColorBrush(Colors.White) {Opacity = 0};
    var outline = new Style {TargetType = typeof (Path)};

    outline.Setters.Add(new Setter(Shape.StrokeThicknessProperty, 2d));
    outline.Setters.Add(new Setter(Shape.StrokeProperty, binding));
    outline.Setters.Add(new Setter(Shape.FillProperty, transparentBrush));

    Resources.Add(typeof(Path), style);
    Resources.Add("OutlinePath", outline);

    // Register event handlers
    Loaded += BaseIconLoaded;
}

To support the callback method in the Outlined property I used some Bag-of-Tricks visual helpers and isolated them in the static class VisualHelper

namespace GlyphOutline
{
    #region Using Declarations

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Media;

    #endregion

    public static class VisualHelper
    {
        public static IEnumerable<DependencyObject> GetVisualDescendents(this DependencyObject source)
        {
            return source
                .GetVisualChildren()
                .SelectRecursive(element => element.GetVisualChildren());
        }

        public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject source)
        {
            var count = VisualTreeHelper.GetChildrenCount(source);

            for (var i = 0; i < count; i++)
                yield return VisualTreeHelper.GetChild(source, i);
        }

        public static IEnumerable<TSource> SelectRecursive<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> recursiveSelector)
        {
            var stack = new Stack<IEnumerator<TSource>>();
            stack.Push(source.GetEnumerator());

            try
            {
                while (stack.Any())
                {
                    if (stack.Peek().MoveNext())
                    {
                        var current = stack.Peek().Current;
                        yield return current;
                        stack.Push(recursiveSelector(current).GetEnumerator());
                    }
                    else
                    {
                        stack.Pop().Dispose();
                    }
                }
            }
            finally
            {
                while (stack.Any())
                {
                    stack.Pop().Dispose();
                }
            }
        }

    }
}

The full source of BaseGlyph is listed below

namespace GlyphOutline
{
    #region Using Declarations

    using System;
    using System.Linq;
    using System.Windows.Controls;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using System.Windows.Data;

    #endregion

    public class BaseGlyph : ContentControl
    {
        
        #region Dependency Properties

        public static readonly DependencyProperty OutlinedProperty = DependencyProperty.Register(
            "Outlined",
            typeof(Boolean),
            typeof(BaseGlyph),
            new UIPropertyMetadata(
                false, // default value for this DP
                OnOutlinedChanged)); // callback on change
                

        private static void OnOutlinedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var caller = (BaseGlyph)d;

            if ((bool)e.NewValue)
            {
                foreach (var item in d.GetVisualDescendents().OfType<Path>())
                    item.Style = (Style)caller.FindResource("OutlinePath");
            }
            else
            {
                foreach (var item in d.GetVisualDescendents().OfType<Path>())
                    item.Style = (Style)caller.FindResource(typeof(Path));
            }
        }

        public Boolean Outlined
        {
            get { return (Boolean)GetValue(OutlinedProperty); }
            set { SetValue(OutlinedProperty, value); }
        }

        #endregion

        #region Constructors

        public BaseGlyph()
        {
            // Build the Resource Dictionary
            Resources = new ResourceDictionary();

            var binding = new Binding();
            var style = new Style();

            // Ancestor Foreground Binding,
            binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(BaseGlyph), 1);
            binding.Path = new PropertyPath(ForegroundProperty);

            style.TargetType = typeof(Path);
            style.Setters.Add(new Setter(Shape.FillProperty, binding));

            var transparentBrush = new SolidColorBrush(Colors.White) {Opacity = 0};
            var outline = new Style {TargetType = typeof (Path)};

            outline.Setters.Add(new Setter(Shape.StrokeThicknessProperty, 2d));
            outline.Setters.Add(new Setter(Shape.StrokeProperty, binding));
            outline.Setters.Add(new Setter(Shape.FillProperty, transparentBrush));

            Resources.Add(typeof(Path), style);
            Resources.Add("OutlinePath", outline);

            // Register event handlers
            Loaded += BaseIconLoaded;
        }

        #endregion

        #region Event Handlers

        private void BaseIconLoaded(object sender, RoutedEventArgs e)
        {
            // if it's Outlined (set in XAML) need to raise the event at the end of the Load
            // to actually load it as Outlined
            if (Outlined)
                OnOutlinedChanged(this, new DependencyPropertyChangedEventArgs(OutlinedProperty, false, true));
        }

        #endregion

    }
}

The full project can be downloaded here.

Dispatcher is long gone, say bye to TaskScheduler and hello to async!

In the old days of TaskScheduler

When .Net 4.0 come out, WPF developers went from using the old Dispatcher and the BeginInvoke to using the TaskScheduler with Tasks. Some new patterns emerged, teams started to inject the WPF scheduler on DI containers and life was good for everyone. This design was particularly better for Junior developers, less space to do mistakes.

Then async showed up

With .Net 4.5, we have the async and await programming model, this is essentially the compiler breaking down methods and method calls into Tasks for us, giving us an easier programming model, with less code noise, that effectively does the same.

The use of async requires things that are “awaitable”, and some frameworks aren’t ready for this yet, for example EntityFramework. But since the early days of the async CTP, this has been a widely accepted change, so work is being done to support async across a wider range of frameworks and modules.

How to use async in a WPF – WCF context

I did a common setup for WPF applications, there’s a WCF Service, very simple that is the Service template with 2 extra lines:
The Interface:

[ServiceContract]
public interface IService
{
    [OperationContract]
    string GetData();
}

The Implementation:

public class Service : IService
{
    public string GetData()
    {
        Thread.Sleep(5000);
        return "some data";
    }
}

This Service has a Thread.Sleep of 5 seconds in it, to simulate some amount of work. On the WPF side the application is very simple, it’s 2 buttons, one to “Start” and a second one that is disabled called “Ready” that does nothing when pressed. The start button does the call to the service, and once over it enables the Ready button.
blog3
This is defined in XAML as:

<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
    <Button Width="100" Click="StartClick">Start</Button>
    <Button Name="ReadyButton" Width="100" IsEnabled="False" Margin="0 20 0 0">Ready</Button>
</StackPanel>

The Service is added to the WPF application as a normal Service Reference out of the box. If we look at the Reference.cs of the Service we can see the 2 calls to our method “GetData” that are available:

public string GetData() {
    return base.Channel.GetData();
}
        
public System.Threading.Tasks.Task<string> GetDataAsync() {
    return base.Channel.GetDataAsync();
}

The top one is the normal synchronous call while the bottom one is the async version of it, and it’s already returning in the form of Task<T> that await requires.

On the code behind of the Window that’s hosting the buttons we just add a Click event handler for the Start button and make it async. Inside we add the necessary code to call GetData on the client and make sure that the call awaits the result:

private async void StartClick(object sender, System.Windows.RoutedEventArgs e)
{
    using (var client = new ServiceClient())
    {
        var someData = await client.GetDataAsync();
        ReadyButton.IsEnabled = true;
    }
}

As you can see, the programming model is very clean, very easy to use. And if you use it correctly you won’t run into the usual WPF problems of making sure that you update the UI only on the WPF thread either trough the Dispatcher or the right TaskScheduler. The code that is outside the await will run in the WPF thread, while the call to the “awaitable” GetData will run in a separate thread, thus the Button.IsEnabled = true will run smoothly on the WPF thread.

If you are using Resharper you get a nice warning when writing async methods and you’re not awaiting anything (which means the method will run synchronously).

blog4

Getting the default WPF control templates in Visual Studio 2012

When designing WPF control templates, one of the most common starting points is the ControlTemplate for that specific control that is shipped with the .Net Framework. By having a dump of that template we can easily start making changes as we go along.

In Visual Studio 2012, the way we get these templates dumped into a code file changed slightly, here’s a tutorial on how to do this.

1. Make a Copy of the control template

In the designer, right click what you want to template, in my example I’m using a Button control. Select Edit Template then select Edit Copy.

blog1

Next another windows appears, that let’s you select the name of the template and where exactly do you want to dump it into. In my example I want it on the code-behind file of the control that’s hosting my button (a Window).

blog2

2. Go to where you dumped it and start editing

This will assign the “ButtonStyle” template to your Button and will add the template to the Window.Resources tag.

<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="FocusVisualStyle">
        <Setter.Value>
            <Style>
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
    <Setter Property="Background" Value="#FFDDDDDD"/>
    <Setter Property="BorderBrush" Value="#FF707070"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                    <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsDefaulted" Value="True">
                        <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" TargetName="border" Value="#FFBEE6FD"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="#FF3C7FB1"/>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" TargetName="border" Value="#FFC4E5F6"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="#FF2C628B"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Background" TargetName="border" Value="#FFF4F4F4"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="#FFADB2B5"/>
                        <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="#FF838383"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Adding Stretching support to Bag of Tricks Reveal control for WPF

One of the components of Bag of Tricks that I use the most is the Reveal control. It allows for great transitions that translate into better User Experiences and all very easy to write. For developers that have used this control, you know that the default behaviour is that the reveal will only occupy the desired space that it’s content requires.

This can be a problem, when we actually want the content inside the reveal to stretch to all the space he is allowed to occupy in the layout. To give an example I’ll illustrate with a problem I had recently: I was developing this RichTextBox control with the Ribbon attached to it, and because I don’t want the Ribbon to showup always, I added a reveal to the Ribbon and showed it on double click, hide it on the “Save” button in the Ribbon. What I got with a Reveal from BOT was this

reveal1

And what I really wanted was the Ribbon stretching to the full length of the RichTextBox, like this

reveal2

Adding a DependencyProperty to support a Stretch Mode

The first thing I had to do was to define an Enum that would list all my supported Stretch Modes:

public enum StretchMode
{
    /// <summary>
    /// No vertical or horizontal stretch.
    /// </summary>
    None,

    /// <summary>
    /// Stretch Horizontally but not Vertically.
    /// </summary>
    Horizontal,

    /// <summary>
    /// Stretch Vertically but not Horizontally.
    /// </summary>
    Vertical,

    /// <summary>
    /// Stretch Vertically and Horizontally.
    /// </summary>
    HorizontalAndVertical,
}

Next I just had to define a DependencyProperty to store the Stretch Mode value, it didn’t had to have any change callbacks because this property is just used for setup:

public StretchMode Stretch
{
    get { return (StretchMode)GetValue(StretchProperty); }
    set { SetValue(StretchProperty, value); }
}

// Using a DependencyProperty as the backing store for StretchMode.  This enables control over how Reveal occupies space
public static readonly DependencyProperty StretchProperty =
    DependencyProperty.Register(
        "Stretch",
        typeof(StretchMode),
        typeof(Reveal),
        new UIPropertyMetadata(StretchMode.None));

Extending the Reveal’s ArrangeOverride to support the new Property

WPF handles space in layouts in two different passes, the first one is MeasureOverride, where it will query the control for how much space would he like to have. On the second pass, the ArrangeOverride, WPF will tell the control how much space if really has, allowing the control to make a final set of computation and return it’s final size.

What we want to do is change the way Reveal is handling the ArrangeOverride, and if we have it to stretch make the childWidth and/or Height to the finalSize value instead of the DesiredSize value.

To accomplish this just add the following switch statement:

var childWidth = child.DesiredSize.Width;
var childHeight = child.DesiredSize.Height;

// Strech Mode support
switch(Stretch)
{
    case StretchMode.None:
        break;
    case StretchMode.Horizontal:
        childWidth = finalSize.Width;
        break;
    case StretchMode.Vertical:
        childHeight = finalSize.Height;
        break;
    case StretchMode.HorizontalAndVertical:
        childWidth = finalSize.Width;
        childHeight = finalSize.Height;
        break;
}

var x = CalculateLeft(childWidth, percent, horizontalReveal);
var y = CalculateTop(childHeight, percent, verticalReveal);