[WPF] Declare global hotkeys in XAML with NHotkey

Very poorPoorAverageGoodExcellent (1 votes) 
Loading ... Loading ...

A common requirement for desktop applications is to handle system-wide hotkeys, in order to intercept keyboard shortcuts even when they don’t have focus. Unfortunately, there is no built-in feature in the .NET framework to do it.

Of course, this is not a new issue, and there are quite a few open-source libraries that address it (e.g. VirtualInput). Most of them rely on a global system hook, which allow them to intercept all keystrokes, even the ones you’re not interested in. I used some of those libraries before, but I’m not really happy with them:

  • they’re often tied to a specific UI framework (usually Windows Forms), which makes them a bit awkward to use in another UI framework (like WPF)
  • I don’t really like the approach of intercepting all keystrokes. It usually means that you end up with a big method with lots of if/else if to decide what to do based on which keys were pressed.

A better option, in my opinion, is to listen only to the keys you’re interested in, and declare what to do for each of those. The approach used in WPF for key bindings is quite elegant:

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />
    <KeyBinding Gesture="Ctrl+Alt+Subtract" Command="{Binding DecrementCommand}" />
</Window.InputBindings>

But of course, key bindings are not global, they require that your app has focus… What if we could change that?

NHotkey is a very simple hotkey library that enables global key bindings. All you have to do is set an attached property to true on your key bindings:

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
                HotkeyManager.RegisterGlobalHotkey="True" />
    <KeyBinding Gesture="Ctrl+Alt+Subtract" Command="{Binding DecrementCommand}"
                HotkeyManager.RegisterGlobalHotkey="True" />
</Window.InputBindings>

And that’s it; the commands defined in the key bindings will now be invoked even if your app doesn’t have focus!

You can also use NHotkey from code:

HotkeyManager.Current.AddOrReplace("Increment", Key.Add, ModifierKeys.Control | ModifierKeys.Alt, OnIncrement);
HotkeyManager.Current.AddOrReplace("Decrement", Key.Subtract, ModifierKeys.Control | ModifierKeys.Alt, OnDecrement);

The library takes advantage of the RegisterHotkey function. Because it also supports Windows Forms, it is split into 3 parts, so that you don’t need to reference the WinForms assembly from a WPF app or vice versa:

  • The core library, which handles the hotkey registration itself, independently of any specific UI framework. This library is not directly usable, but is used by the other two.
  • The WinForms-specific API, which uses the Keys enumeration from System.Windows.Forms
  • The WPF-specific API, which uses the Key and ModifierKeys enumerations from System.Windows.Input, and supports global key bindings in XAML.

If you install the library from Nuget, add either the NHotkey.Wpf or the NHotkey.WindowsForms package; the core package will be added automatically.

[WPF] Using Linq to shape data in a CollectionView

Very poorPoorAverageGoodExcellent (7 votes) 
Loading ... Loading ...

WPF provides a simple mechanism for shaping collections of data, via the ICollectionView interface and its Filter, SortDescriptions and GroupDescriptions properties:

// Collection to which the view is bound
public ObservableCollection People { get; private set; }
...

// Default view of the People collection
ICollectionView view = CollectionViewSource.GetDefaultView(People);

// Show only adults
view.Filter = o => ((Person)o).Age >= 18;

// Sort by last name and first name
view.SortDescriptions.Add(new SortDescription("LastName", ListSortDirection.Ascending));
view.SortDescriptions.Add(new SortDescription("FirstName", ListSortDirection.Ascending));

// Group by country
view.GroupDescriptions.Add(new PropertyGroupDescription("Country"));

Even though this technique is not difficult to use, it has a few drawbacks:

  • The syntax is a bit clumsy and unnatural: the fact that the filter parameter is an object whereas we know it’s actually of type Person makes the code less readable because of the cast, and the specification of the sort and group descriptions is a little repetitive
  • Specifying the property names as strings introduces a risk of error, since they’re not verified by the compiler

In the last few years, we got used to use Linq to do this kind of things… it would be nice to be able to do the same for the shaping of an ICollectionView.

Let’s see what syntax we could use to do it with Linq… something like this perhaps?

People.Where(p => p.Age >= 18)
      .OrderBy(p => p.LastName)
      .ThenBy(p => p.FirstName)
      .GroupBy(p => p.Country);

Or, with the Linq query comprehension syntax:

from p in People
where p.Age >= 18
orderby p.LastName, p.FirstName
group p by p.Country;

Obviously, this is not enough: this code only creates a query on the collection, it doesn’t modify the CollectionView… but with just a little extra work, we can get the desired result:

var query =
    from p in People.ShapeView()
    where p.Age >= 18
    orderby p.LastName, p.FirstName
    group p by p.Country;

query.Apply();

The ShapeView method returns a wrapper which encapsulates the default view of the collection, and exposes Where, OrderBy and GroupBy methods with appropriate signatures to specify the shaping of the CollectionView. Creating the query has no direct effect, the changes are only applied to the view when Apply is called: that’s because it’s better to apply all changes at the same time, using ICollectionView.DeferRefresh, to avoid causing a refresh of the view for each clause of the query. When Apply is called, we can see that the view is correctly updated to reflect the query.

This solution allows to define the filter, sort and grouping in a strongly-typed way, which implies that the code is verified by the compiler. It’s also more concise and readable than the original code… Just be careful with one thing: some queries that are correct from the compiler’s point of view won’t be applicable to a CollectionView. For instance, if you try to group the data by the first letter of the last name (p.LastName.Substring(0, 1)), the GroupBy method will fail because only properties are supported by PropertyGroupDescription.

Note that the wrapper won’t overwrite the shaping properties of the CollectionView if you don’t specify the corresponding Linq clause, so you can just modify the current view without specifying everything again. If you need to clear the properties, you can use the ClearFilter, ClearSort and ClearGrouping methods:

// Remove the grouping and add a sort criteria
People.ShapeView()
      .ClearGrouping()
      .OrderBy(p => p.LastName);
      .Apply();

Note that as for a normal Linq query, it’s possible to use either the query comprehension syntax or to call the methods directly, since the former is just syntactic sugar for the latter.

Finally, here’s the complete code of the wrapper and the associated extension methods:

    public static class CollectionViewShaper
    {
        public static CollectionViewShaper<TSource> ShapeView<TSource>(this IEnumerable<TSource> source)
        {
            var view = CollectionViewSource.GetDefaultView(source);
            return new CollectionViewShaper<TSource>(view);
        }

        public static CollectionViewShaper<TSource> Shape<TSource>(this ICollectionView view)
        {
            return new CollectionViewShaper<TSource>(view);
        }
    }

    public class CollectionViewShaper<TSource>
    {
        private readonly ICollectionView _view;
        private Predicate<object> _filter;
        private readonly List<SortDescription> _sortDescriptions = new List<SortDescription>();
        private readonly List<GroupDescription> _groupDescriptions = new List<GroupDescription>();

        public CollectionViewShaper(ICollectionView view)
        {
            if (view == null)
                throw new ArgumentNullException("view");
            _view = view;
            _filter = view.Filter;
            _sortDescriptions = view.SortDescriptions.ToList();
            _groupDescriptions = view.GroupDescriptions.ToList();
        }

        public void Apply()
        {
            using (_view.DeferRefresh())
            {
                _view.Filter = _filter;
                _view.SortDescriptions.Clear();
                foreach (var s in _sortDescriptions)
                {
                    _view.SortDescriptions.Add(s);
                }
                _view.GroupDescriptions.Clear();
                foreach (var g in _groupDescriptions)
                {
                    _view.GroupDescriptions.Add(g);
                }
            }
        }
            
        public CollectionViewShaper<TSource> ClearGrouping()
        {
            _groupDescriptions.Clear();
            return this;
        }

        public CollectionViewShaper<TSource> ClearSort()
        {
            _sortDescriptions.Clear();
            return this;
        }

        public CollectionViewShaper<TSource> ClearFilter()
        {
            _filter = null;
            return this;
        }

        public CollectionViewShaper<TSource> ClearAll()
        {
            _filter = null;
            _sortDescriptions.Clear();
            _groupDescriptions.Clear();
            return this;
        }

        public CollectionViewShaper<TSource> Where(Func<TSource, bool> predicate)
        {
            _filter = o => predicate((TSource)o);
            return this;
        }

        public CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
        {
            return OrderBy(keySelector, true, ListSortDirection.Ascending);
        }

        public CollectionViewShaper<TSource> OrderByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
        {
            return OrderBy(keySelector, true, ListSortDirection.Descending);
        }

        public CollectionViewShaper<TSource> ThenBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
        {
            return OrderBy(keySelector, false, ListSortDirection.Ascending);
        }

        public CollectionViewShaper<TSource> ThenByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
        {
            return OrderBy(keySelector, false, ListSortDirection.Descending);
        }

        private CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector, bool clear, ListSortDirection direction)
        {
            string path = GetPropertyPath(keySelector.Body);
            if (clear)
                _sortDescriptions.Clear();
            _sortDescriptions.Add(new SortDescription(path, direction));
            return this;
        }

        public CollectionViewShaper<TSource> GroupBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
        {
            string path = GetPropertyPath(keySelector.Body);
            _groupDescriptions.Add(new PropertyGroupDescription(path));
            return this;
        }

        private static string GetPropertyPath(Expression expression)
        {
            var names = new Stack<string>();
            var expr = expression;
            while (expr != null && !(expr is ParameterExpression) && !(expr is ConstantExpression))
            {
                var memberExpr = expr as MemberExpression;
                if (memberExpr == null)
                    throw new ArgumentException("The selector body must contain only property or field access expressions");
                names.Push(memberExpr.Member.Name);
                expr = memberExpr.Expression;
            }
            return String.Join(".", names.ToArray());
        }
    }

kick it on DotNetKicks.com

Posted in WPF. Tags: , , . 3 Comments »

[WPF] Creating parameterized styles with attached properties

Very poorPoorAverageGoodExcellent (5 votes) 
Loading ... Loading ...

Today I’d like to share a trick that I used quite often in the past few months. Let’s assume that in order to improve the look of your application, you created custom styles for the standard controls:

OK, I’m not a designer… but it will serve the purpose well enough to illustrate my point ;). These styles are very simple, they’re just the default styles of CheckBox and RadioButton in which I only changed the templates to replace the BulletChromes with these awesome blue tick marks. Here’s the code:

        <Style x:Key="{x:Type CheckBox}" TargetType="{x:Type CheckBox}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Background" Value="{StaticResource CheckBoxFillNormal}"/>
            <Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="FocusVisualStyle" Value="{StaticResource EmptyCheckBoxFocusVisual}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CheckBox}">
                        <BulletDecorator Background="Transparent"
                                         SnapsToDevicePixels="true">
                            <BulletDecorator.Bullet>
                                <Border BorderBrush="{TemplateBinding BorderBrush}"
                                        Background="{TemplateBinding Background}"
                                        BorderThickness="1"
                                        Width="11" Height="11" Margin="0,1,0,0">
                                    <Grid>
                                        <Path Name="TickMark"
                                              Fill="Blue"
                                              Data="M0,4 5,9 9,0 4,5"
                                              Visibility="Hidden" />
                                        <Rectangle Name="IndeterminateMark"
                                                   Fill="Blue"
                                                   Width="7" Height="7"
                                                   HorizontalAlignment="Center"
                                                   VerticalAlignment="Center"
                                                   Visibility="Hidden" />
                                    </Grid>
                                </Border>
                            </BulletDecorator.Bullet>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              Margin="{TemplateBinding Padding}"
                                              RecognizesAccessKey="True"
                                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </BulletDecorator>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="true">
                                <Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
                                <Setter Property="Padding" Value="4,0,0,0"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="TickMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <Trigger Property="IsChecked" Value="{x:Null}">
                                <Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="{x:Type RadioButton}" TargetType="{x:Type RadioButton}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Background" Value="#F4F4F4"/>
            <Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RadioButton}">
                        <BulletDecorator Background="Transparent">
                            <BulletDecorator.Bullet>
                                <Grid VerticalAlignment="Center" Margin="0,1,0,0">
                                    <Ellipse Width="11" Height="11"
                                             Stroke="{TemplateBinding BorderBrush}"
                                             StrokeThickness="1"
                                             Fill="{TemplateBinding Background}" />
                                    <Ellipse Name="TickMark"
                                             Width="7" Height="7"
                                             Fill="Blue"
                                             Visibility="Hidden" />
                                    <Ellipse Name="IndeterminateMark"
                                             Width="3" Height="3"
                                             Fill="Blue"
                                             Visibility="Hidden" />
                                </Grid>
                            </BulletDecorator.Bullet>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              Margin="{TemplateBinding Padding}"
                                              RecognizesAccessKey="True"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </BulletDecorator>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="true">
                                <Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
                                <Setter Property="Padding" Value="4,0,0,0"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="TickMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <Trigger Property="IsChecked" Value="{x:Null}">
                                <Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

OK, so you now have beautiful controls that are going to make the app a big success, management is happy, everything is for the best… until you realize that in another view of the application, the controls need to have the same style, but with green tick marks!

The first solution that comes to mind is to duplicate the style, and replace blue with green in the copy. But since you’re a good developer who cares about best practices, you know that duplicate code is evil: if you ever need to make changes to the style of the blue CheckBox, you will also have to modify the green one… and perhaps the red one, and the black one, etc. Clearly it would soon become unmanageable. So we need to refactor, but how? Ideally we would pass parameters to the style, but a style is not a method that you can call with various parameters…

What we need is an extra property that controls the color of the tick marks, so we can bind to this property in the template. A possible approach is to create custom controls that inherit CheckBox and RadioButton, with an extra TickBrush property… but personnally I don’t really like this approach, I always prefer to use the built-in controls when they can fit the bill.

Anyway, there is an easier solution: we just need to create a ThemeProperties class with an attached property of type Brush:

    public static class ThemeProperties
    {
        public static Brush GetTickBrush(DependencyObject obj)
        {
            return (Brush)obj.GetValue(TickBrushProperty);
        }

        public static void SetTickBrush(DependencyObject obj, Brush value)
        {
            obj.SetValue(TickBrushProperty, value);
        }

        public static readonly DependencyProperty TickBrushProperty =
            DependencyProperty.RegisterAttached(
                "TickBrush",
                typeof(Brush),
                typeof(ThemeProperties),
                new FrameworkPropertyMetadata(Brushes.Black));
    }

We change the templates a bit to replace the hard-coded color with a binding to this property:

                                ...

                                <!-- CheckBox -->
                                        <Path Name="TickMark"
                                              Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                              Data="M0,4 5,9 9,0 4,5"
                                              Visibility="Hidden" />
                                        <Rectangle Name="IndeterminateMark"
                                                   Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                                   Width="7" Height="7"
                                                   HorizontalAlignment="Center"
                                                   VerticalAlignment="Center"
                                                   Visibility="Hidden" />

                                ...

                                <!-- RadioButton -->
                                    <Ellipse Name="TickMark"
                                             Width="7" Height="7"
                                             Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                             Visibility="Hidden" />
                                    <Ellipse Name="IndeterminateMark"
                                             Width="3" Height="3"
                                             Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                             Visibility="Hidden" />

And when we use the controls, we set the property to the desired tick color:

<CheckBox Content="Checked" IsChecked="True" my:ThemeProperties.TickBrush="Blue" />

So we can now have controls that share the same style, but have different colors for the tick mark:

Isn’t it great? However there is a small problem left: since controls on the same view all use the same tick color, it’s not very convenient to repeat the color on each one. It would be nice to be able to specify the color just once, on the root of the view… Well, as it happens, dependency properties have a nice feature that allows to do exactly that: value inheritance. We just need to specify the Inherits flag in the declaration of the TickBrush property:

        public static readonly DependencyProperty TickBrushProperty =
            DependencyProperty.RegisterAttached(
                "TickBrush",
                typeof(Brush),
                typeof(ThemeProperties),
                new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.Inherits));

With this flag, the property becomes “ambient”: we only need to specify its value on a parent control, and all descendant controls will automatically inherit the value. So if you need a view where all the checkboxes and radiobuttons are red, just set the TickBrush property to Red on the root element of the view.

Obviously this concept can be extended to other cases: actually, every time an element of the template must change based on arbitrary criteria, this technique can be used. It can be a good alternative to duplicating a template when you only need to change a small part of it.

kick it on DotNetKicks.com

[WPF 4.5] Subscribing to an event using a markup extension

Very poorPoorAverageGoodExcellent (3 votes) 
Loading ... Loading ...

It’s been a while since I last wrote about markup extensions… The release of Visual Studio 11 Developer Preview, which introduces a number of new features to WPF, just gave me a reason to play with them again. The feature I’m going to discuss here is perhaps not the most impressive, but it fills in a gap of the previous versions: the support of markup extensions for events.

Until now, it was possible to use a markup extension in XAML to assign a value to a property, but we couldn’t do the same to subscribe to an event. In WPF 4.5, it is now possible. So here is a small example of the kind we can do with it…

When using the MVVM pattern, we often associate commands of the ViewModel with controls of the view, via the binding mechanism. This approach usually works well, but it has some downsides:

  • it introduces a lot of boilerplate code in the ViewModel
  • not all controls have a Command property (actually, most don’t), and when this property exists, it corresponds only to one event of the control (e.g. the click on a button). There is no really easy way to “bind” the other events to commands of the ViewModel

It would be nice to be able to bind events directly to ViewModel methods, like this:

        <Button Content="Click me"
                Click="{my:EventBinding OnClick}" />

With the OnClick method defined in the ViewModel:

        public void OnClick(object sender, EventArgs e)
        {
            MessageBox.Show("Hello world!");
        }

Well, this is now possible! Here’s a proof of concept… The EventBindingExtension class shown below first gets the DataContext of the control, then looks for the specified method on the DataContext, and eventually returns a delegate for this method:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;


    public class EventBindingExtension : MarkupExtension
    {
        public EventBindingExtension() { }

        public EventBindingExtension(string eventHandlerName)
        {
            this.EventHandlerName = eventHandlerName;
        }

        [ConstructorArgument("eventHandlerName")]
        public string EventHandlerName { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(EventHandlerName))
                throw new ArgumentException("The EventHandlerName property is not set", "EventHandlerName");

            var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

            EventInfo eventInfo = target.TargetProperty as EventInfo;
            if (eventInfo == null)
                throw new InvalidOperationException("The target property must be an event");
            
            object dataContext = GetDataContext(target.TargetObject);
            if (dataContext == null)
                throw new InvalidOperationException("No DataContext found");

            var handler = GetHandler(dataContext, eventInfo, EventHandlerName);
            if (handler == null)
                throw new ArgumentException("No valid event handler was found", "EventHandlerName");

            return handler;
        }

        #region Helper methods

        static object GetHandler(object dataContext, EventInfo eventInfo, string eventHandlerName)
        {
            Type dcType = dataContext.GetType();

            var method = dcType.GetMethod(
                eventHandlerName,
                GetParameterTypes(eventInfo));
            if (method != null)
            {
                if (method.IsStatic)
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, method);
                else
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, dataContext, method);
            }

            return null;
        }

        static Type[] GetParameterTypes(EventInfo eventInfo)
        {
            var invokeMethod = eventInfo.EventHandlerType.GetMethod("Invoke");
            return invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        }

        static object GetDataContext(object target)
        {
            var depObj = target as DependencyObject;
            if (depObj == null)
                return null;

            return depObj.GetValue(FrameworkElement.DataContextProperty)
                ?? depObj.GetValue(FrameworkContentElement.DataContextProperty);
        }

        #endregion
    }

This class can be used as shown in the example above.

As it is now, this markup extension has an annoying limitation: the DataContext must be set before the call to ProvideValue, otherwise it won’t be possible to find the event handler method. A solution could be to subscribe to the DataContextChanged event to look for the method after the DataContext is set, but in the meantime we still need to return something… and we can’t return null, because it would cause an exception (since you can’t subscribe to an event with a null handler). So we need to return a dummy handler generated dynamically from the event signature. It makes things a bit harder… but it’s still feasible.

Here’s a second version that implements this improvement :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Windows;
using System.Windows.Markup;

    public class EventBindingExtension : MarkupExtension
    {
        private EventInfo _eventInfo;

        public EventBindingExtension() { }

        public EventBindingExtension(string eventHandlerName)
        {
            this.EventHandlerName = eventHandlerName;
        }

        [ConstructorArgument("eventHandlerName")]
        public string EventHandlerName { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (string.IsNullOrEmpty(EventHandlerName))
                throw new ArgumentException("The EventHandlerName property is not set", "EventHandlerName");

            var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

            var targetObj = target.TargetObject as DependencyObject;
            if (targetObj == null)
                throw new InvalidOperationException("The target object must be a DependencyObject");

            _eventInfo = target.TargetProperty as EventInfo;
            if (_eventInfo == null)
                throw new InvalidOperationException("The target property must be an event");

            object dataContext = GetDataContext(targetObj);
            if (dataContext == null)
            {
                SubscribeToDataContextChanged(targetObj);
                return GetDummyHandler(_eventInfo.EventHandlerType);
            }

            var handler = GetHandler(dataContext, _eventInfo, EventHandlerName);
            if (handler == null)
            {
                Trace.TraceError(
                    "EventBinding: no suitable method named '{0}' found in type '{1}' to handle event '{2'}",
                    EventHandlerName,
                    dataContext.GetType(),
                    _eventInfo);
                return GetDummyHandler(_eventInfo.EventHandlerType);
            }

            return handler;
            
        }

        #region Helper methods

        static Delegate GetHandler(object dataContext, EventInfo eventInfo, string eventHandlerName)
        {
            Type dcType = dataContext.GetType();

            var method = dcType.GetMethod(
                eventHandlerName,
                GetParameterTypes(eventInfo.EventHandlerType));
            if (method != null)
            {
                if (method.IsStatic)
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, method);
                else
                    return Delegate.CreateDelegate(eventInfo.EventHandlerType, dataContext, method);
            }

            return null;
        }

        static Type[] GetParameterTypes(Type delegateType)
        {
            var invokeMethod = delegateType.GetMethod("Invoke");
            return invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        }

        static object GetDataContext(DependencyObject target)
        {
            return target.GetValue(FrameworkElement.DataContextProperty)
                ?? target.GetValue(FrameworkContentElement.DataContextProperty);
        }

        static readonly Dictionary<Type, Delegate> _dummyHandlers = new Dictionary<Type, Delegate>();

        static Delegate GetDummyHandler(Type eventHandlerType)
        {
            Delegate handler;
            if (!_dummyHandlers.TryGetValue(eventHandlerType, out handler))
            {
                handler = CreateDummyHandler(eventHandlerType);
                _dummyHandlers[eventHandlerType] = handler;
            }
            return handler;
        }

        static Delegate CreateDummyHandler(Type eventHandlerType)
        {
            var parameterTypes = GetParameterTypes(eventHandlerType);
            var returnType = eventHandlerType.GetMethod("Invoke").ReturnType;
            var dm = new DynamicMethod("DummyHandler", returnType, parameterTypes);
            var il = dm.GetILGenerator();
            if (returnType != typeof(void))
            {
                if (returnType.IsValueType)
                {
                    var local = il.DeclareLocal(returnType);
                    il.Emit(OpCodes.Ldloca_S, local);
                    il.Emit(OpCodes.Initobj, returnType);
                    il.Emit(OpCodes.Ldloc_0);
                }
                else
                {
                    il.Emit(OpCodes.Ldnull);
                }
            }
            il.Emit(OpCodes.Ret);
            return dm.CreateDelegate(eventHandlerType);
        }

        private void SubscribeToDataContextChanged(DependencyObject targetObj)
        {
            DependencyPropertyDescriptor
                .FromProperty(FrameworkElement.DataContextProperty, targetObj.GetType())
                .AddValueChanged(targetObj, TargetObject_DataContextChanged);
        }

        private void UnsubscribeFromDataContextChanged(DependencyObject targetObj)
        {
            DependencyPropertyDescriptor
                .FromProperty(FrameworkElement.DataContextProperty, targetObj.GetType())
                .RemoveValueChanged(targetObj, TargetObject_DataContextChanged);
        }

        private void TargetObject_DataContextChanged(object sender, EventArgs e)
        {
            DependencyObject targetObj = sender as DependencyObject;
            if (targetObj == null)
                return;

            object dataContext = GetDataContext(targetObj);
            if (dataContext == null)
                return;

            var handler = GetHandler(dataContext, _eventInfo, EventHandlerName);
            if (handler != null)
            {
                _eventInfo.AddEventHandler(targetObj, handler);
            }
            UnsubscribeFromDataContextChanged(targetObj);
        }

        #endregion
    }

So this is the kind of things we can do thanks to this new WPF feature. We could also imagine a behavior system similar to what we can do with attached properties, e.g. to execute a standard action when an event occurs. There are lots of possible applications for this, I leave it to you to find them ;)

kick it on DotNetKicks.com

[WPF] Display an animated GIF image

Very poorPoorAverageGoodExcellent (13 votes) 
Loading ... Loading ...

Note: The code in this article is out of date; the current code is hosted on Codeplex.

WPF is a great technology, but sometimes it seems to be missing some really basic features… A frequently mentioned example is the lack of support for animated GIF images. Actually, the GIF format itself is supported by the imaging API, but the Image control only shows the first frame of the animation.

Many solutions to this problem have been proposed on technical forums and blogs, usually variations of the following approaches:

  • Use the MediaElement control: unfortunately this control only supports URI like file:// or http://, not the pack:// URI schema used for WPF resources; this means the image can’t be included in the resources, it has to be in a separate file. Furthermore, transparency for GIF images isn’t supported in MediaElement, which makes the final result quite ugly
  • Use the PictureBox control from Windows Forms, via a WindowsFormsHost: I personnally dislike using WinForms controls in WPF, it really looks like a hack…
  • Create a custom control that inherits Image and handles the animation. Some solutions take advantage of the ImageAnimator class from System.Drawing (GDI), others use a WPF animation to change the current frame. It’s a rather “clean” approach, but it forces you to use a specific control for GIF images. Also, the solution using ImageAnimator turns out not to be very smooth, the animation is quite jerky.

As you might have guessed, I don’t find any of these solutions really satisfying… Furthermore, none of the implementations I’ve seen of the third approach handles the duration of each frame properly, they only assume that all frames last 100ms (which is almost always true, but almost isn’t good enough IMHO…). So I kept the best ideas from each approach I’ve seen, and I came up with my own solution. Here are the goals I set to attain:

  • No dependency on Windows Forms or GDI
  • Display the animated image in a standard Image control
  • Use the same XAML code for normal and animated images
  • Support for transparency
  • Correct handling of frame duration

To achieve this result, I started from a very simple, even obvious idea: to animate the image, all you have to do is apply an animation to the Source property of the Image control. WPF provides all the necessary tools to do that; in this case, the ObjectAnimationUsingKeyFrames class fits the bill perfectly: it allows to specify at what exact time a given value should be assigned to the property, which makes it easy to take the frame duration into account.

The next problem is to extract the frames from the image: fortunately WPF supports this natively, and the BitmapDecoder class provides a Frames property to do exactly that. So, no big difficulty so far…

Finally, last obstacle: extract the duration of each frame. It’s the part that took me the longest, because I needed to do some research… I first thought I would need to read the file manually and decode the binary data myself. But eventually the solution is quite simple, and takes advantage of the BitmapMetadata class. The only difficulty has been to find the “path” of the metadata that contains the delay, but after a few minutes of trial and error, here it is: /grctlext/Delay.

The final solution is implemented as an attached property named AnimatedSource, that applies to the Image control, and can be used instead of Source:

<Image Stretch="None" my:ImageBehavior.AnimatedSource="/Images/animation.gif" />

This property can also be assigned a normal (not animated) image, it will be displayed normally; therefore this property can be used without worrying about whether the image to display will be animated or not.

So in the end, all the goals have been achieved, and we even get some icing on the cake: this solution also works in the designer (at least in Visual Studio 2010), so the animation is immediately visible when you set the AnimatedSource property :)

Without further ado, here’s the complete code:

    public static class ImageBehavior
    {
        #region AnimatedSource

        [AttachedPropertyBrowsableForType(typeof(Image))]
        public static ImageSource GetAnimatedSource(Image obj)
        {
            return (ImageSource)obj.GetValue(AnimatedSourceProperty);
        }

        public static void SetAnimatedSource(Image obj, ImageSource value)
        {
            obj.SetValue(AnimatedSourceProperty, value);
        }

        public static readonly DependencyProperty AnimatedSourceProperty =
            DependencyProperty.RegisterAttached(
              "AnimatedSource",
              typeof(ImageSource),
              typeof(ImageBehavior),
              new UIPropertyMetadata(
                null,
                AnimatedSourceChanged));

        private static void AnimatedSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            Image imageControl = o as Image;
            if (imageControl == null)
                return;

            var oldValue = e.OldValue as ImageSource;
            var newValue = e.NewValue as ImageSource;
            if (oldValue != null)
            {
                imageControl.BeginAnimation(Image.SourceProperty, null);
            }
            if (newValue != null)
            {
                imageControl.DoWhenLoaded(InitAnimationOrImage);
            }
        }

        private static void InitAnimationOrImage(Image imageControl)
        {
            BitmapSource source = GetAnimatedSource(imageControl) as BitmapSource;
            if (source != null)
            {
                var decoder = GetDecoder(source) as GifBitmapDecoder;
                if (decoder != null && decoder.Frames.Count > 1)
                {
                    var animation = new ObjectAnimationUsingKeyFrames();
                    var totalDuration = TimeSpan.Zero;
                    BitmapSource prevFrame = null;
                    FrameInfo prevInfo = null;
                    foreach (var rawFrame in decoder.Frames)
                    {
                        var info = GetFrameInfo(rawFrame);
                        var frame = MakeFrame(
                            source,
                            rawFrame, info,
                            prevFrame, prevInfo);

                        var keyFrame = new DiscreteObjectKeyFrame(frame, totalDuration);
                        animation.KeyFrames.Add(keyFrame);
                        
                        totalDuration += info.Delay;
                        prevFrame = frame;
                        prevInfo = info;
                    }
                    animation.Duration = totalDuration;
                    animation.RepeatBehavior = RepeatBehavior.Forever;
                    if (animation.KeyFrames.Count > 0)
                        imageControl.Source = (ImageSource)animation.KeyFrames[0].Value;
                    else
                        imageControl.Source = decoder.Frames[0];
                    imageControl.BeginAnimation(Image.SourceProperty, animation);
                    return;
                }
            }
            imageControl.Source = source;
            return;
        }

        private static BitmapDecoder GetDecoder(BitmapSource image)
        {
            BitmapDecoder decoder = null;
            var frame = image as BitmapFrame;
            if (frame != null)
                decoder = frame.Decoder;

            if (decoder == null)
            {
                var bmp = image as BitmapImage;
                if (bmp != null)
                {
                    if (bmp.StreamSource != null)
                    {
                        bmp.StreamSource.Position = 0;
                        decoder = BitmapDecoder.Create(bmp.StreamSource, bmp.CreateOptions, bmp.CacheOption);
                    }
                    else if (bmp.UriSource != null)
                    {
                        Uri uri = bmp.UriSource;
                        if (bmp.BaseUri != null && !uri.IsAbsoluteUri)
                            uri = new Uri(bmp.BaseUri, uri);
                        decoder = BitmapDecoder.Create(uri, bmp.CreateOptions, bmp.CacheOption);
                    }
                }
            }

            return decoder;
        }

        private static BitmapSource MakeFrame(
            BitmapSource fullImage,
            BitmapSource rawFrame, FrameInfo frameInfo,
            BitmapSource previousFrame, FrameInfo previousFrameInfo)
        {
            DrawingVisual visual = new DrawingVisual();
            using (var context = visual.RenderOpen())
            {
                if (previousFrameInfo != null && previousFrame != null &&
                    previousFrameInfo.DisposalMethod == FrameDisposalMethod.Combine)
                {
                    var fullRect = new Rect(0, 0, fullImage.PixelWidth, fullImage.PixelHeight);
                    context.DrawImage(previousFrame, fullRect);
                }

                context.DrawImage(rawFrame, frameInfo.Rect);
            }
            var bitmap = new RenderTargetBitmap(
                fullImage.PixelWidth, fullImage.PixelHeight,
                fullImage.DpiX, fullImage.DpiY,
                PixelFormats.Pbgra32);
            bitmap.Render(visual);
            return bitmap;
        }

        private class FrameInfo
        {
            public TimeSpan Delay { get; set; }
            public FrameDisposalMethod DisposalMethod { get; set; }
            public double Width { get; set; }
            public double Height { get; set; }
            public double Left { get; set; }
            public double Top { get; set; }

            public Rect Rect
            {
                get { return new Rect(Left, Top, Width, Height); }
            }
        }

        private enum FrameDisposalMethod
        {
            Replace = 0,
            Combine = 1,
            RestoreBackground = 2,
            RestorePrevious = 3
        }

        private static FrameInfo GetFrameInfo(BitmapFrame frame)
        {
            var frameInfo = new FrameInfo
            {
                Delay = TimeSpan.FromMilliseconds(100),
                DisposalMethod = FrameDisposalMethod.Replace,
                Width = frame.PixelWidth,
                Height = frame.PixelHeight,
                Left = 0,
                Top = 0
            };

            BitmapMetadata metadata;
            try
            {
                metadata = frame.Metadata as BitmapMetadata;
                if (metadata != null)
                {
                    const string delayQuery = "/grctlext/Delay";
                    const string disposalQuery = "/grctlext/Disposal";
                    const string widthQuery = "/imgdesc/Width";
                    const string heightQuery = "/imgdesc/Height";
                    const string leftQuery = "/imgdesc/Left";
                    const string topQuery = "/imgdesc/Top";

                    var delay = metadata.GetQueryOrNull<ushort>(delayQuery);
                    if (delay.HasValue)
                        frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value);

                    var disposal = metadata.GetQueryOrNull<byte>(disposalQuery);
                    if (disposal.HasValue)
                        frameInfo.DisposalMethod = (FrameDisposalMethod) disposal.Value;

                    var width = metadata.GetQueryOrNull<ushort>(widthQuery);
                    if (width.HasValue)
                        frameInfo.Width = width.Value;

                    var height = metadata.GetQueryOrNull<ushort>(heightQuery);
                    if (height.HasValue)
                        frameInfo.Height = height.Value;

                    var left = metadata.GetQueryOrNull<ushort>(leftQuery);
                    if (left.HasValue)
                        frameInfo.Left = left.Value;

                    var top = metadata.GetQueryOrNull<ushort>(topQuery);
                    if (top.HasValue)
                        frameInfo.Top = top.Value;
                }
            }
            catch (NotSupportedException)
            {
            }

            return frameInfo;
        }

        private static T? GetQueryOrNull<T>(this BitmapMetadata metadata, string query)
            where T : struct
        {
            if (metadata.ContainsQuery(query))
            {
                object value = metadata.GetQuery(query);
                if (value != null)
                    return (T) value;
            }
            return null;
        }

        #endregion
    }

And here’s the DoWhenLoaded extension method used in the code above:

public static void DoWhenLoaded<T>(this T element, Action<T> action)
    where T : FrameworkElement
{
    if (element.IsLoaded)
    {
        action(element);
    }
    else
    {
        RoutedEventHandler handler = null;
        handler = (sender, e) =>
        {
            element.Loaded -= handler;
            action(element);
        };
        element.Loaded += handler;
    }
}

Enjoy :)

Update: the code that retrieves the frame duration only works on Windows Seven, and on Windows Vista if the Platform Update is installed (untested). The default duration (100ms) will be used instead on other versions of Windows. I will update the article if I find a solution that works on all operating systems (I know I could use System.Drawing.Bitmap, but I’d rather not depend on this…)

Update 2: as pointed out by Klaus in the comments, the ImageBehavior class didn’t handle some important attributes of the frames: the diposal method (whether a frame should entirely replace the previous one, or be combined with it), and the frame position (Left/Top/Width/Height). I updated the code to handle these attributes properly. Thank you Klaus!

Update 3: a commenter on the French version of my blog pointed out a problem when the AnimatedSource is an image in a resource dictionary; the UriSource wasn’t correctly interpreted when it was a relative URI. This problem is now fixed. Thank you, “anonymous”!

Update 4: uploaded an example project to demonstrate the code.

Update 5: yet another bug fix, for when you use a BitmapImage initialized from a stream. Thanks to Mizutama for spotting this one!

Update 6: rather than posting improvements to this blog post, I eventually created a project on CodePlex where this class will be maintained. You can also install it using NuGet, the package id is WpfAnimatedGif. Thanks to Diego Mijelshon for the suggestion!

kick it on DotNetKicks.com

Posted in WPF. Tags: , , . 60 Comments »

[WPF] How to bind to data when the DataContext is not inherited

Very poorPoorAverageGoodExcellent (30 votes) 
Loading ... Loading ...

The DataContext property in WPF is extremely handy, because it is automatically inherited by all children of the element where you assign it; therefore you don’t need to set it again on each element you want to bind. However, in some cases the DataContext is not accessible: it happens for elements that are not part of the visual or logical tree. It can be very difficult then to bind a property on those elements…

Let’s illustrate with a simple example: we want to display a list of products in a DataGrid. In the grid, we want to be able to show or hide the Price column, based on the value of a ShowPrice property exposed by the ViewModel. The obvious approach is to bind the Visibility of the column to the ShowPrice property:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding ShowPrice,
                        Converter={StaticResource visibilityConverter}}"/>

Unfortunately, changing the value of ShowPrice has no effect, and the column is always visible… why? If we look at the Output window in Visual Studio, we notice the following line:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ShowPrice; DataItem=null; target element is ‘DataGridTextColumn’ (HashCode=32685253); target property is ‘Visibility’ (type ‘Visibility’)

The message is rather cryptic, but the meaning is actually quite simple: WPF doesn’t know which FrameworkElement to use to get the DataContext, because the column doesn’t belong to the visual or logical tree of the DataGrid.

We can try to tweak the binding to get the desired result, for instance by setting the RelativeSource to the DataGrid itself:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding DataContext.ShowPrice,
                        Converter={StaticResource visibilityConverter},
                        RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

Or we can add a CheckBox bound to ShowPrice, and try to bind the column visibility to the IsChecked property by specifying the element name:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding IsChecked,
                        Converter={StaticResource visibilityConverter},
                        ElementName=chkShowPrice}"/>

But none of these workarounds seems to work, we always get the same result…

At this point, it seems that the only viable approach would be to change the column visibility in code-behind, which we usually prefer to avoid when using the MVVM pattern… But I’m not going to give up so soon, at least not while there are other options to consider ;)

The solution to our problem is actually quite simple, and takes advantage of the Freezable class. The primary purpose of this class is to define objects that have a modifiable and a read-only state, but the interesting feature in our case is that Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree. I don’t know the exact mechanism that enables this behavior, but we’re going to take advantage of it to make our binding work…

The idea is to create a class (I called it BindingProxy for reasons that should become obvious very soon) that inherits Freezable and declares a Data dependency property:

    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }

We can then declare an instance of this class in the resources of the DataGrid, and bind the Data property to the current DataContext:

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

The last step is to specify this BindingProxy object (easily accessible with StaticResource) as the Source for the binding:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding Data.ShowPrice,
                        Converter={StaticResource visibilityConverter},
                        Source={StaticResource proxy}}"/>

Note that the binding path has been prefixed with “Data”, since the path is now relative to the BindingProxy object.

The binding now works correctly, and the column is properly shown or hidden based on the ShowPrice property.

[WPF] A simpler Grid using XAML attribute syntax

Very poorPoorAverageGoodExcellent (5 votes) 
Loading ... Loading ...

The Grid control is one of the most frequently used containers in WPF. It allows to layout elements easily in rows and columns. Unfortunately the code to declare it, while simple to write, is made quite awkward by the use of the property element syntax:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="5"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="60" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    
    <Label Content="Name" Grid.Row="0" Grid.Column="0" />
    <TextBox Text="Hello world" Grid.Row="0" Grid.Column="1"/>
    <Rectangle Fill="Black" Grid.Row="1" Grid.ColumnSpan="2"/>
    <Label Content="Image" Grid.Row="2" Grid.Column="0" />
    <Image Source="Resources/Desert.jpg" Grid.Row="2" Grid.Column="1" />
</Grid>

In that example, more than half the code is made of the grid definition ! Even though this syntax offers a great flexibility and a precise control of the layout, in mot cases we just need to define the height of rows and the width of columns… so it would be much simpler if we could declare the grid using the attribute syntax, as follows:

<Grid Rows="Auto,5,*" Columns="60,*">
    ...
</Grid>

This article shows how to reach that goal, by creating a SimpleGrid class derived from Grid.

First of all, our class needs two new properties: Rows and Columns. These properties define the heights and widths of rows and columns, respectively. These dimensions are not just numbers: values such as "*", "2*" ou "Auto" are valid dimensions for grid bands. WPF has a specific type to represent these values: the GridLength structure. So our new properties will be collections of GridLength objects. Here’s the signature of the SimpleGrid class:

public class SimpleGrid : Grid
{
    public IList<GridLength> Rows { get; set; }
    public IList<GridLength> Columns { get; set; }
}

Since these properties are in charge of defining the grid’s rows and columns, they have to modify the RowDefinitions and ColumnDefinitions properties of the base class. Here’s how to implement them to get the desired result :

        private IList<GridLength> _rows;
        public IList<GridLength> Rows
        {
            get { return _rows; }
            set
            {
                _rows = value;
                RowDefinitions.Clear();
                if (_rows == null)
                    return;
                foreach (var length in _rows)
                {
                    RowDefinitions.Add(new RowDefinition { Height = length });
                }
            }
        }

        private IList<GridLength> _columns;
        public IList<GridLength> Columns
        {
            get { return _columns; }
            set
            {
                _columns = value;
                ColumnDefinitions.Clear();
                if (_columns == null)
                    return;
                foreach (var length in _columns)
                {
                    ColumnDefinitions.Add(new ColumnDefinition { Width = length });
                }
            }
        }

At this point, our SimpleGrid is already usable… from C# code, which doesn’t really help us since we’re trying to make the XAML code simpler. So we need to find a way to declare the values of these properties in XAML attributes, which isn’t obvious since they are collections…

In XAML, all attributes are written in the form of strings. To convert these strings to values of the required type, WPF makes use of converters, which are classes derived from TypeConverter, associated with each type which supports conversion to and from other types. For instance, the converter for the GridLength structure is the GridLengthConverter class, which can convert numbers and strings to GridLength objects, and back. The conversion mechanism is described in more detail in this MSDN article.

So we need to create a converter and associate it to the type of the Rows and Columns properties. Since we don’t have control over the IList<T> type, we’ll start by creating a specific GridLengthCollection type to be used instead of IList<GridLength>, and we’ll associate a custom converter with it (GridLengthCollectionConverter):

    [TypeConverter(typeof(GridLengthCollectionConverter))]
    public class GridLengthCollection : ReadOnlyCollection<GridLength>
    {
        public GridLengthCollection(IList<GridLength> lengths)
            : base(lengths)
        {
        }
    }

Why is that collection read-only ? That just because allowing to add or remove rows and columns would make the implementation more complex, and it wouldn’t bring any benefit for our objective, which is to make it easier to define a Grid in XAML. So, let’s keep it simple, at least for now… The ReadOnlyCollection<T> does exactly what we need, so we just inherit from it, rather than reinventing the wheel.

Notice the use of the TypeConverter attribute: that’s how we tell the framework which converter should be used with the GridLengthCollection type. Now, all we need to do is to implement that converter :

    public class GridLengthCollectionConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string))
                return true;
            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            string s = value as string;
            if (s != null)
                return ParseString(s, culture);
            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string) && value is GridLengthCollection)
                return ToString((GridLengthCollection)value, culture);
            return base.ConvertTo(context, culture, value, destinationType);
        }

        private string ToString(GridLengthCollection value, CultureInfo culture)
        {
            var converter = new GridLengthConverter();
            return string.Join(",", value.Select(v => converter.ConvertToString(v)));
        }

        private GridLengthCollection ParseString(string s, CultureInfo culture)
        {
            var converter = new GridLengthConverter();
            var lengths = s.Split(',').Select(p => (GridLength)converter.ConvertFromString(p.Trim()));
            return new GridLengthCollection(lengths.ToArray());
        }
    }

This class can converte a GridLengthCollection to and from a string, in which individual dimensions are separated by commas. Notice the use of the GridLengthConverter: since there already is a converter for the elements of the collections, we’d better use it rather than try to reimplement the logic to parse a GridLength

Now that all pieces are ready, we can try our new simple grid:

<my:SimpleGrid Rows="Auto,5,*" Columns="60,*">
    <Label Content="Name" Grid.Row="0" Grid.Column="0" />
    <TextBox Text="Hello world" Grid.Row="0" Grid.Column="1"/>
    <Rectangle Fill="Black" Grid.Row="1" Grid.ColumnSpan="2"/>
    <Label Content="Image" Grid.Row="2" Grid.Column="0" />
    <Image Source="Resources/Desert.jpg" Grid.Row="2" Grid.Column="1" />
</my:SimpleGrid>

We end up with a much shorter and more readable code than with a normal Grid, and the result is the same: mission complete :)

Of course, we could improve this class in a number of ways: implement Rows and Columns as dependency properties in order to allow binding, handle addition and removal of rows and columns… However, this grid is intended for very simple scenarios, where the grid is defined once and for all, and is not modified at runtime (which is presumably the most frequent use case), so it seems sensible to keep it as simple as possible. For more specific needs, like specifying a minimum/maximum width or a shared sized group, we’ll stick to the standard Grid.

For reference, here’s the final code of the SimpleGrid class:

    public class SimpleGrid : Grid
    {
        private GridLengthCollection _rows;
        public GridLengthCollection Rows
        {
            get { return _rows; }
            set
            {
                _rows = value;
                RowDefinitions.Clear();
                if (_rows == null)
                    return;
                foreach (var length in _rows)
                {
                    RowDefinitions.Add(new RowDefinition { Height = length });
                }
            }
        }

        private GridLengthCollection _columns;
        public GridLengthCollection Columns
        {
            get { return _columns; }
            set
            {
                _columns = value;
                if (_columns == null)
                    return;
                ColumnDefinitions.Clear();
                foreach (var length in _columns)
                {
                    ColumnDefinitions.Add(new ColumnDefinition { Width = length });
                }
            }
        }
    }
Posted in Code sample, WPF. Tags: , , . 2 Comments »

[VS2010] Binding support in InputBindings

Very poorPoorAverageGoodExcellent (2 votes) 
Loading ... Loading ...

THE feature that was missing from WPF !

Visual Studio 2010 beta 2 has been released last week, and it brings to WPF a long awaited feature : support for bindings in InputBindings.

As a reminder, the issue in previous releases was that the Command property of the InputBinding class wasn’t a DependencyProperty, so it wasn’t possible to bind it. Furthermore, InputBindings didn’t inherit the parent DataContext, which made it difficult to provide alternative implementations…

Until now, in order to bind the Command of a KeyBinding or MouseBinding to a property of the DataContext, we had to resort to clumsy workarounds… I had eventually came up with an acceptable solution, described in this post, but I wasn’t really satisfied with it (it used reflection on private members, and had annoying limitations).

More recently, I found a better solution in the MVVM toolkit : a CommandReference class, inherited from Freezable, allows to put a reference to a ViewModel command in the page or control resources, so that it can be used later with StaticResource. It’s much cleaner than my previous solution, but still not very straightforward…

WPF 4.0 solves that problem once and for all : the InputBinding class now inherits from Freezable, which allows it to inherit the DataContext, and the Command, CommandParameter and CommandTarget properties are now dependency properties. So, at last, we can forget about the clumsy workarounds described above, and go straight to the point :

    <Window.InputBindings>
        <KeyBinding Key="F5"
                    Command="{Binding RefreshCommand}" />
    </Window.InputBindings>

This new feature should make it much easier to develop MVVM applications !

Help 3

Other than that, I would like to say a few words about the new offline help system that comes with Visual Studio 2010, called “Help 3″. It’s quite a big change compared to previous versions… First, it’s not a standalone application anymore, but a locally hosted web application, so you can access the documentation with your favorite web browser. On the whole, it’s better than the previous system… much faster and more responsive than the old Document Explorer included in previous Visual Studio releases.

However, the new system misses the feature that was the most useful to me : the index ! Now there’s only the hierarchical view, and a search textbox. IMHO, the index was the most convenient way of looking up something in the doc, it made it very easy to access a class or member directly, even without knowing its namespace… why on earth did they remove it ? Worse still : the search results don’t show the namespace, only the class or member name. For instance, if you search “button class”, in the results there is no way to see the difference between System.Windows.Forms.Button, System.Windows.Controls.Button and System.Web.UI.WebControls ! You have to click each link and see where it leads… In Document Explorer, the Index Results pane showed this information clearly.

So, eventually I have mixed feelings about this new help system, because I will have to change the way I use the documentation. But except for this annoying detail, I must concede that it’s objectively a big improvement over the old system…

[WPF] Markup extensions and templates

Very poorPoorAverageGoodExcellent (2 votes) 
Loading ... Loading ...

Note : This post follows the one about a a markup extension that can update its target, and reuses the same code.

You may have noticed that using a custom markup extension in a template sometimes lead to unexpected results… In this post I’ll explain what the problem is, and how to create a markup extensions that behaves correctly in a template.

The problem

Let’s take the example from the previous post : a markup extension which gives the state of network connectivity, and updates its target when the network is connected or disconnected :

<CheckBox IsChecked="{my:NetworkAvailable}" Content="Network is available" />

Now let’s put the same CheckBox in a ControlTemplate :

<ControlTemplate x:Key="test">
  <CheckBox IsChecked="{my:NetworkAvailable}" Content="Network is available" />
</ControlTemplate>

And let’s create a control which uses this template :

<Control Template="{StaticResource test}" />

If we disconnect from the network, we notice that the CheckBox is not automatically updated by the NetworkAvailableExtension, whereas it was working fine when we used it outside the template…

Explanation and solution

The markup expression is evaluated when it is encountered by the XAML parser : in that case, when the template is parsed. But at this time, the CheckBox control is not created yet, so the ProvideValue method can’t access it… When a markup extension is evaluated inside a template, the TargetObject is actually an instance of System.Windows.SharedDp, an internal WPF class.

For the markup extension to be able to access its target, it has to be evaluated when the template is applied : we need to defer its evaluation until this time. It’s actually pretty simple, we just need to return the markup extension itself from ProvideValue : this way, it will be evaluated again when the actual target control is created.

To check if the extension is evaluated for the template or for a “real” control, we just need to test whether the type of the TargetObject is System.Windows.SharedDp. So the code of the ProvideValue method becomes :

        public sealed override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            if (target != null)
            {
                if (target.TargetObject.GetType().FullName == "System.Windows.SharedDp")
                    return this;
                _targetObject = target.TargetObject;
                _targetProperty = target.TargetProperty;
            }

            return ProvideValueInternal(serviceProvider);
        }

Cool, it’s now fixed, the CheckBox is updated when the network connectivity changes :)

Last, but not least

OK, we have a solution that apparently works fine, but let’s not count our chickens before they’re hatched… What if we now want to use our ControlTemplate on several controls ?

<Control Template="{StaticResource test}" />
<Control Template="{StaticResource test}" />

Now let’s run the application and unplug the network cable : the second CheckBox is updated, but the first one is not…

The reason for this is simple : there are two CheckBox controls, but only one instance of NetworkAvailableExtension, shared between all instances of the template. Now, NetworkAvailableExtension can only reference one target object, so only the last one for which ProvideValue has been called is kept…

So we need to keep track of not one target object, but a collection of target objects, which will all be update by the UpdateValue method. Here’s the final code of the UpdatableMarkupExtension base class :

    public abstract class UpdatableMarkupExtension : MarkupExtension
    {
        private List<object> _targetObjects = new List<object>();
        private object _targetProperty;

        protected IEnumerable<object> TargetObjects
        {
            get { return _targetObjects; }
        }

        protected object TargetProperty
        {
            get { return _targetProperty; }
        }

        public sealed override object ProvideValue(IServiceProvider serviceProvider)
        {
            // Retrieve target information
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

            if (target != null && target.TargetObject != null)
            {
                // In a template the TargetObject is a SharedDp (internal WPF class)
                // In that case, the markup extension itself is returned to be re-evaluated later
                if (target.TargetObject.GetType().FullName == "System.Windows.SharedDp")
                    return this;

                // Save target information for later updates
                _targetObjects.Add(target.TargetObject);
                _targetProperty = target.TargetProperty;
            }

            // Delegate the work to the derived class
            return ProvideValueInternal(serviceProvider);
        }

        protected virtual void UpdateValue(object value)
        {
            if (_targetObjects.Count > 0)
            {
                // Update the target property of each target object
                foreach (var target in _targetObjects)
                {
                    if (_targetProperty is DependencyProperty)
                    {
                        DependencyObject obj = target as DependencyObject;
                        DependencyProperty prop = _targetProperty as DependencyProperty;

                        Action updateAction = () => obj.SetValue(prop, value);

                        // Check whether the target object can be accessed from the
                        // current thread, and use Dispatcher.Invoke if it can't

                        if (obj.CheckAccess())
                            updateAction();
                        else
                            obj.Dispatcher.Invoke(updateAction);
                    }
                    else // _targetProperty is PropertyInfo
                    {
                        PropertyInfo prop = _targetProperty as PropertyInfo;
                        prop.SetValue(target, value, null);
                    }
                }
            }
        }

        protected abstract object ProvideValueInternal(IServiceProvider serviceProvider);
    }

The UpdatableMarkupExtension is now fully functional… until proved otherwise ;). This class makes a good starting point for any markup extension that needs to update its target, without having to worry about the low-level aspects of tracking and updating target objects.

[WPF] Automatically sort a GridView (continued)

Very poorPoorAverageGoodExcellent (23 votes) 
Loading ... Loading ...

A few months ago, I wrote a post where I explained how to automatically sort a GridView when a column header is clicked. I had mentioned a possible improvement : add a sort glyph in the column header to show which column is sorted. In today’s post, I present a new version of the GridViewSort class, which displays the sort glyph.

GridViewSort sample with sort glyph

GridViewSort sample with sort glyph

To achieve this result, I used an Adorner : this is a component which allows to draw over existing UI elements, on an independant rendering layer.

The new version of the GridViewSort class can be used as before, in that case the grid displays default sort glyphs. These default glyphs are not particularly good-looking, so if you have some artistic skills you can provide you own images, as shown in the code below :

        <ListView ItemsSource="{Binding Persons}"
                  IsSynchronizedWithCurrentItem="True"
                  util:GridViewSort.AutoSort="True"
                  util:GridViewSort.SortGlyphAscending="/Images/up.png"
                  util:GridViewSort.SortGlyphDescending="/Images/down.png">

It is also possible to disable the sort glyphs, by setting the ShowSortGlyph attached property to false :

        <ListView ItemsSource="{Binding Persons}"
                  IsSynchronizedWithCurrentItem="True"
                  util:GridViewSort.AutoSort="True"
                  util:GridViewSort.ShowSortGlyph="False">

Note that in the current version, the sort glyph is only displayed when using the automatic sort mode (AutoSort = true). The case of a custom sort using the Command property is not handled yet.

Here is the complete code of the new version of the class :

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Documents;

namespace Wpf.Util
{
    public class GridViewSort
    {
        #region Public attached properties

        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    null,
                    (o, e) =>
                    {
                        ItemsControl listView = o as ItemsControl;
                        if (listView != null)
                        {
                            if (!GetAutoSort(listView)) // Don't change click handler if AutoSort enabled
                            {
                                if (e.OldValue != null && e.NewValue == null)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (e.OldValue == null && e.NewValue != null)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static bool GetAutoSort(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSortProperty);
        }

        public static void SetAutoSort(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSortProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSort.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSortProperty =
            DependencyProperty.RegisterAttached(
                "AutoSort",
                typeof(bool),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    false,
                    (o, e) =>
                    {
                        ListView listView = o as ListView;
                        if (listView != null)
                        {
                            if (GetCommand(listView) == null) // Don't change click handler if a command is set
                            {
                                bool oldValue = (bool)e.OldValue;
                                bool newValue = (bool)e.NewValue;
                                if (oldValue && !newValue)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (!oldValue && newValue)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static string GetPropertyName(DependencyObject obj)
        {
            return (string)obj.GetValue(PropertyNameProperty);
        }

        public static void SetPropertyName(DependencyObject obj, string value)
        {
            obj.SetValue(PropertyNameProperty, value);
        }

        // Using a DependencyProperty as the backing store for PropertyName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.RegisterAttached(
                "PropertyName",
                typeof(string),
                typeof(GridViewSort),
                new UIPropertyMetadata(null)
            );

        public static bool GetShowSortGlyph(DependencyObject obj)
        {
            return (bool)obj.GetValue(ShowSortGlyphProperty);
        }

        public static void SetShowSortGlyph(DependencyObject obj, bool value)
        {
            obj.SetValue(ShowSortGlyphProperty, value);
        }

        // Using a DependencyProperty as the backing store for ShowSortGlyph.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowSortGlyphProperty =
            DependencyProperty.RegisterAttached("ShowSortGlyph", typeof(bool), typeof(GridViewSort), new UIPropertyMetadata(true));

        public static ImageSource GetSortGlyphAscending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphAscendingProperty);
        }

        public static void SetSortGlyphAscending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphAscendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphAscending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphAscendingProperty =
            DependencyProperty.RegisterAttached("SortGlyphAscending", typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        public static ImageSource GetSortGlyphDescending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphDescendingProperty);
        }

        public static void SetSortGlyphDescending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphDescendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphDescending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphDescendingProperty =
            DependencyProperty.RegisterAttached("SortGlyphDescending", typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Private attached properties

        private static GridViewColumnHeader GetSortedColumnHeader(DependencyObject obj)
        {
            return (GridViewColumnHeader)obj.GetValue(SortedColumnHeaderProperty);
        }

        private static void SetSortedColumnHeader(DependencyObject obj, GridViewColumnHeader value)
        {
            obj.SetValue(SortedColumnHeaderProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortedColumn.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty SortedColumnHeaderProperty =
            DependencyProperty.RegisterAttached("SortedColumnHeader", typeof(GridViewColumnHeader), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Column header click event handler

        private static void ColumnHeader_Click(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked != null && headerClicked.Column != null)
            {
                string propertyName = GetPropertyName(headerClicked.Column);
                if (!string.IsNullOrEmpty(propertyName))
                {
                    ListView listView = GetAncestor<ListView>(headerClicked);
                    if (listView != null)
                    {
                        ICommand command = GetCommand(listView);
                        if (command != null)
                        {
                            if (command.CanExecute(propertyName))
                            {
                                command.Execute(propertyName);
                            }
                        }
                        else if (GetAutoSort(listView))
                        {
                            ApplySort(listView.Items, propertyName, listView, headerClicked);
                        }
                    }
                }
            }
        }

        #endregion

        #region Helper methods

        public static T GetAncestor<T>(DependencyObject reference) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(reference);
            while (!(parent is T))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            if (parent != null)
                return (T)parent;
            else
                return null;
        }

        public static void ApplySort(ICollectionView view, string propertyName, ListView listView, GridViewColumnHeader sortedColumnHeader)
        {
            ListSortDirection direction = ListSortDirection.Ascending;
            if (view.SortDescriptions.Count > 0)
            {
                SortDescription currentSort = view.SortDescriptions[0];
                if (currentSort.PropertyName == propertyName)
                {
                    if (currentSort.Direction == ListSortDirection.Ascending)
                        direction = ListSortDirection.Descending;
                    else
                        direction = ListSortDirection.Ascending;
                }
                view.SortDescriptions.Clear();

                GridViewColumnHeader currentSortedColumnHeader = GetSortedColumnHeader(listView);
                if (currentSortedColumnHeader != null)
                {
                    RemoveSortGlyph(currentSortedColumnHeader);
                }
            }
            if (!string.IsNullOrEmpty(propertyName))
            {
                view.SortDescriptions.Add(new SortDescription(propertyName, direction));
                if (GetShowSortGlyph(listView))
                    AddSortGlyph(
                        sortedColumnHeader,
                        direction,
                        direction == ListSortDirection.Ascending ? GetSortGlyphAscending(listView) : GetSortGlyphDescending(listView));
                SetSortedColumnHeader(listView, sortedColumnHeader);
            }
        }

        private static void AddSortGlyph(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            adornerLayer.Add(
                new SortGlyphAdorner(
                    columnHeader,
                    direction,
                    sortGlyph
                    ));
        }

        private static void RemoveSortGlyph(GridViewColumnHeader columnHeader)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            Adorner[] adorners = adornerLayer.GetAdorners(columnHeader);
            if (adorners != null)
            {
                foreach (Adorner adorner in adorners)
                {
                    if (adorner is SortGlyphAdorner)
                        adornerLayer.Remove(adorner);
                }
            }
        }

        #endregion

        #region SortGlyphAdorner nested class

        private class SortGlyphAdorner : Adorner
        {
            private GridViewColumnHeader _columnHeader;
            private ListSortDirection _direction;
            private ImageSource _sortGlyph;

            public SortGlyphAdorner(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph)
                : base(columnHeader)
            {
                _columnHeader = columnHeader;
                _direction = direction;
                _sortGlyph = sortGlyph;
            }

            private Geometry GetDefaultGlyph()
            {
                double x1 = _columnHeader.ActualWidth - 13;
                double x2 = x1 + 10;
                double x3 = x1 + 5;
                double y1 = _columnHeader.ActualHeight / 2 - 3;
                double y2 = y1 + 5;

                if (_direction == ListSortDirection.Ascending)
                {
                    double tmp = y1;
                    y1 = y2;
                    y2 = tmp;
                }

                PathSegmentCollection pathSegmentCollection = new PathSegmentCollection();
                pathSegmentCollection.Add(new LineSegment(new Point(x2, y1), true));
                pathSegmentCollection.Add(new LineSegment(new Point(x3, y2), true));

                PathFigure pathFigure = new PathFigure(
                    new Point(x1, y1),
                    pathSegmentCollection,
                    true);

                PathFigureCollection pathFigureCollection = new PathFigureCollection();
                pathFigureCollection.Add(pathFigure);

                PathGeometry pathGeometry = new PathGeometry(pathFigureCollection);
                return pathGeometry;
            }

            protected override void OnRender(DrawingContext drawingContext)
            {
                base.OnRender(drawingContext);

                if (_sortGlyph != null)
                {
                    double x = _columnHeader.ActualWidth - 13;
                    double y = _columnHeader.ActualHeight / 2 - 5;
                    Rect rect = new Rect(x, y, 10, 10);
                    drawingContext.DrawImage(_sortGlyph, rect);
                }
                else
                {
                    drawingContext.DrawGeometry(Brushes.LightGray, new Pen(Brushes.Gray, 1.0), GetDefaultGlyph());
                }
            }
        }

        #endregion
    }
}

I hope you’ll find that useful :)

Update: uploaded example project to demonstrate how to use the code

css.php