WPF. Simplify your life with Linq extension methods, Canvas and Visual Tree helpers.


This article dwells on .net 3.5 and Visual C# 2008 Express Edition.

Up to current moment I’ve been using the Canvas element very much with the diagramming stuff that I described earlier. As you implement something similar you definitely come across inconvenience using the standard Children collection property. There’s very little changes that you will be using only UIElement objects. For example you have some shapes exposing different interfaces, something like ISelectable, IDraggable or anything else. Additionally you have a variety of visual connections and drawings inheriting Paths, Geometry or Figures.  So each time you come across type casting and code longer that it could be 😉

 

1. Extending base classes

When you need three or more core functions for manipulating canvas children and don’t have the possibility of inheriting/implementing additional stuff for your own classes there’s a good opportunity of extending the Canvas class itself using a couple of Linq extension methods for that.

using System.Windows;
using System.Windows.Controls;

namespace UsefulExtensions.Extensions
{
  public static class CanvasExtension
  {
    public static void AddChild<T>(this Canvas canvas, T element)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement != null && !canvas.Children.Contains(uiElement))
        canvas.Children.Add(uiElement);
    }

    public static void RemoveChild<T>(this Canvas canvas, T element)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement != null && canvas.Children.Contains(uiElement))
        canvas.Children.Remove(uiElement);
    }

    public static void InsertChild<T>(this Canvas canvas, int index, T element)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement != null && !canvas.Children.Contains(uiElement))
        canvas.Children.Insert(index, uiElement);
    }
  }
}

Of course you should carefully read the documentation towards the extension methods at least at your local VS MSDN library. There’s a good explanation how it works and where and when to use these features.

Similar to "System.Linq" whenever you declare the namespace "UserfulExtensions.Extensions" (or your own namespace name) you get the additional methods for all the Canvas based classes.

image

 

The best thing I like using generics is the transparency of T provided. That’s a really great feature having the possibility to omit the T declaration within the generic method. Of course sometimes it may bring to small problems but in most cases helps you a lot.

Having shapes exposing for example IDraggable and ISelectable interfaces you don’t need to explicitly define the type as T when calling generic methods.

The method call will look like following:

myCanvas.AddChild(myShape1);

myCanvas.AddChild(myShape2)

The T will be automatically resolved by the type/interface of the parameter provided. You can actually pass anything you to the method but that’s not the fact the element will be added to the Canvas. As you have seen earlier the extension methods require the element be at least of UIElement type to perform an action, so the next thing will also compile but won’t be executed at runtime.

myCanvas.AddChild(0)

myCanvas.AddChild(new Point(10,10))

As I’ve already said I like the generics feature of resolving the <T> and I think it’s really useful.

 

2. Implementing helper classes

2.1. Canvas helper

Helper classes is the thing each developer implement very often. When you use the canvas child maintenance using the methods above you come to conclusion that the standard "Canvas.GetLeft" or "Canvas.GetTop" are not enough flexible for you as they also deal with UIElement objects while you require some generics approach for that. It’s very easy to implement a static helper class to correct that.

Here’s a quick snippet I use often

CanvasHelper.cs

using System;
using System.Windows;
using System.Windows.Controls;

namespace UsefulExtensions.Helpers
{
  public static class CanvasHelper
  {
    public static double GetLeft<T>(T element)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement == null)
        throw new ArgumentNullException("element");
      return (double)uiElement.GetValue(Canvas.LeftProperty);
    }

    public static double GetTop<T>(T element)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement == null)
        throw new ArgumentNullException("element");
      return (double)uiElement.GetValue(Canvas.TopProperty);
    }

    public static Point GetPosition<T>(T element)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement == null)
        throw new ArgumentNullException("element");

      return new Point(
        (double)uiElement.GetValue(Canvas.LeftProperty),
        (double)uiElement.GetValue(Canvas.TopProperty));
    }

    public static void SetLeft<T>(T element, double length)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement == null)
        throw new ArgumentNullException("element");
      uiElement.SetValue(Canvas.LeftProperty, length);
    }

    public static void SetTop<T>(T element, double length)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement == null)
        throw new ArgumentNullException("element");
      uiElement.SetValue(Canvas.TopProperty, length);
    }

    public static void SetPosition<T>(T element, Point value)
    {
      UIElement uiElement = element as UIElement;
      if (uiElement == null)
        throw new ArgumentNullException("element");
      uiElement.SetValue(Canvas.LeftProperty, value.X);
      uiElement.SetValue(Canvas.TopProperty, value.Y);
    }
  }
}

I didn’t provide any supplementary information for the sources above because I hope it’s quite self-describing. As static Canvas members "GetLeft" and "GetTop" deal with the attached dependency properties of the UIElement provided there’s quite easy to extend the functionality a bit for supporting the generics approach.

image

Of course this can also be placed as extension to the Canvas class but my intention was to expose the helper classes that can be implemented at any stage of the project life cycle. Also I think these methods deal with attached properties of UIElement the extension method for Canvas won’t be the right place of storing our generics stuff.

 

2.2. Visual Tree Helper

Standard VisualTreeHelper class contains a lot of important and useful stuff helping developers a lot. Usually I use if for hit testing and it helped me much with rubberband selection tool implementation.

The only thing it lacks very much is the more complicated Parent processing functionality. According to the MSDN forums a lot of people came across one problem related to the control templates and templating stuff.

When dealing with hit testing it’s rather difficult from the first steps provide hit testing features for the elements totally based on Control Templates because hit testing will work for each element within the template separately. Assuming that you might have some sort of shape containing a text box, an image element and something like text block I clearly understand the troubles you might experience when trying to hit-test it or detect clicks.

The only good solution can be found across forums is navigating with the visual tree to get the parent of the Control Template and so find the element required instead of controls within the template. Simple recursive function is quite enough but as I prefer working with generics I’ve implemented another look of the function.

VisualTreeHelperEx.cs

using System.Windows;
using System.Windows.Media;

namespace UsefulExtensions.Helpers
{
  /// <summary>
  /// Provides additional functionality alongside standard VisualTreeHelper.
  /// </summary>
  public static class VisualTreeHelperEx
  {
    /// <summary>
    /// Returns current object or ony parent object that expose the type provided.
    /// Very useful when accessing the main container for the ControlTemplate or for any elements within the ControlTemplate.
    /// </summary>
    /// <typeparam name="T">The type of the object to be searched.</typeparam>
    /// <param name="obj">Starting object to search for parents or itself.</param>
    /// <returns>Returns current object if it exposes the required type or any parent from the upper levels or null.</returns>
    public static DependencyObject GetParent<T>(DependencyObject obj)
    {
      if (obj is T) return obj;

      DependencyObject parent = VisualTreeHelper.GetParent(obj);
      if (parent == null)
        return null;
      else if (!(parent is T))
        return GetParent<T>(parent);

      return parent;
    }
  }
}

As for  hit testing itself you will find a lot of information across the MSDN library so I won’t dwell on it. This method helps getting exactly the element you are looking for when performing the test.

Assume you are implementing the rubberband/multi-selection tool some sort of diagramming application and you definitely know that each of your shape expose something like ISelectable interface. Of course you need to hit-test all the ISelectable based elements on the Canvas. But each you shape also contains a very rich content template making each shape look very fancy. In this case you will simply move to your helper methods defining the shapes regardless their content templates:

 

private List<ISelectable> hits = new List<ISelectable>();
private List<ISelectable> GetSelectablesHit(Geometry region)
{
  hits.Clear();
  GeometryHitTestParameters parameters = new GeometryHitTestParameters(region);
  HitTestResultCallback callback = new HitTestResultCallback(HitTestCallback);
  VisualTreeHelper.HitTest((Visual)DrawingSurface, null, callback, parameters);
  return hits;
}

private HitTestResultBehavior HitTestCallback(HitTestResult result)
{
  GeometryHitTestResult geometryResult = (GeometryHitTestResult)result;

  // Get the object regardless ControlTemplate content
  ISelectable visual = VisualTreeHelperEx.GetParent<ISelectable>(result.VisualHit) as ISelectable;

  if (visual != null &&
    !hits.Contains(visual) &&
    geometryResult.IntersectionDetail == IntersectionDetail.FullyInside)
    hits.Add(visual);
  return HitTestResultBehavior.Continue;
}

You influence the performance a lot because the function doesn’t navigate the element tree each time.

if (obj is T) return obj;

The first line of the function ensures the actual element is what you are querying and stops the recursion. In the rest cases it will go upper level until it founds the required <T> or return null if nothing was found.

 

 

3. Extending interfaces.

As I’ve been using IServiceProvider interface for organizing the basic service bus for my diagramming sample application I again got tired of type casting stuff when querying some service host for the services by their interfaces. So I’ve decided to extend it without any additional inheritance and changes to interface. Extension methods features helped me very much with it. Here’s a quick sample:

IServiceProviderExtension.cs

using System;

namespace UsefulExtensions.Extensions
{
  public static class IServiceProviderExtension
  {
    public static T GetService<T>(this IServiceProvider provider)
    {
      return (T)provider.GetService(typeof(T));
    }

    public static bool Contains<T>(this IServiceProvider provider)
    {
      return (provider.GetService(typeof(T)) != null);
    }
  }
}

What does it give you? In all classes exposing IServiceProvider or interfaces inheriting it you get additional methods GetSetvice and Contains

image

This will help you get rid of common type casting and long strings like

provider.GetService(typeof(ISelectionManager)) as ISelectionManager

by simple

provider.GetService<ISelectionManager>()

Additionally it provides an extension helping you determine whether the service exists within the provider collection. Of course the "Contains" method is just a sample of implementing the extension method.

The best thing as I’ve mentioned above is that you get this extensions to each inheritance case

image

 

The only thing you should remember is adding the namespace of your extension classes to the source files where you deal with IServiceProvider or interfaces exposing it.

Hope this will help anyone to increase the coding performance.

Source code for the article

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s