Navigating WPF’s Visual Tree

October 9, 2011

There are many times when I’ve needed to navigate up or down WPF’s Visual Tree from the code behind to find an object, so I finally decided to put all my visual tree navigation code into a single helper class.

For example, to find controls I can now use the following syntax:

// Search up the VisualTree to find DataGrid 
// containing specific Cell
var parent = VisualTreeHelpers.FindAncestor<DataGrid>(myDataGridCell);

// Search down the VisualTree to find a CheckBox 
// in this DataGridCell
var child = VisualTreeHelpers.FindChild<CheckBox>(myDataGridCell);

// Search up the VisualTree to find a TextBox 
// named SearchTextBox
var searchBox = VisualTreeHelpers.FindAncestor<TextBox>(myDataGridCell, "SeachTextBox");

// Search down the VisualTree to find a Label
// named MyCheckBoxLabel
var specificChild = VisualTreeHelpers.FindChild<Label>(myDataGridCell, "MyCheckBoxLabel");

And here’s the VisualTreeHelpers class

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

namespace MyNamespace
{
    public class VisualTreeHelpers
    {
        /// <summary>
        /// Returns the first ancester of specified type
        /// </summary>
        public static T FindAncestor<T>(DependencyObject current)
        where T : DependencyObject
        {
            current = VisualTreeHelper.GetParent(current);

            while (current != null)
            {
                if (current is T)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            };
            return null;
        }

        /// <summary>
        /// Returns a specific ancester of an object
        /// </summary>
        public static T FindAncestor<T>(DependencyObject current, T lookupItem)
        where T : DependencyObject
        {
            while (current != null)
            {
                if (current is T && current == lookupItem)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            };
            return null;
        }

        /// <summary>
        /// Finds an ancestor object by name and type
        /// </summary>
        public static T FindAncestor<T>(DependencyObject current, string parentName)
        where T : DependencyObject
        {
            while (current != null)
            {
                if (!string.IsNullOrEmpty(parentName))
                {
                    var frameworkElement = current as FrameworkElement;
                    if (current is T && frameworkElement != null && frameworkElement.Name == parentName)
                    {
                        return (T)current;
                    }
                }
                else if (current is T)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            };

            return null;

        }

        /// <summary>
        /// Looks for a child control within a parent by name
        /// </summary>
        public static T FindChild<T>(DependencyObject parent, string childName)
        where T : DependencyObject
        {
            // Confirm parent and childName are valid.
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child.
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                    else
                    {
                        // recursively drill down the tree
                        foundChild = FindChild<T>(child, childName);

                        // If the child is found, break so we do not overwrite the found child.
                        if (foundChild != null) break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }

        /// <summary>
        /// Looks for a child control within a parent by type
        /// </summary>
        public static T FindChild<T>(DependencyObject parent)
            where T : DependencyObject
        {
            // Confirm parent is valid.
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child);

                    // If the child is found, break so we do not overwrite the found child.
                    if (foundChild != null) break;
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }
            return foundChild;
        }    
	}
}

Edit: Finally the link where I got the original code from. I’ve made some modifications to it, but the original code can be found here.


Simplifying PRISM’s EventAggregator

October 9, 2011

I recently started using PRISM’s EventAggregator for messaging in my WPF applications, however I find the syntax confusing, and hard to remember.

Here’s the default syntax for PRISM’s EventAggregator

// Optional: Create Event "Message"
// Needed for multiple parameters, but not for single parameter
// messages. I prefer using one in most cases because I find
// it makes it easier to read the code
public class TickerSymbolSelectedMessage
{
    public string StockSymbol { get; set; }
}

// Create an Event
public class TickerSymbolSelectedEvent : 
    CompositePresentationEvent<TickerSymbolSelectedMessage>{}

// Subscribe to an Event
eventAggregator.GetEvent<ChangeStockEvent>().Subscribe(ShowNews);

public void ShowNews(TickerSymbolSelectedMessage msg)
{
   // Handle Event
}

// Broadcast an Event
eventAggregator.GetEvent<ChangeStockEvent>().Publish(
    new TickerSymbolSelectedMessage{ StockSymbol = “STOCK0” });

There are a few things that irritate me about this syntax:

  • Needing to call GetEvent() all the time before subscribing/publishing
  • Needing a reference to the EventAggregator anywhere I want to use it
  • Needing to create two classes for an Event: the Event itself and the EventMessage (technically this one is optional if you only have one Parameter, but I like to create it anyways because I find it easier to read)

A large portion of the time I am working small apps which usually have a single purpose, so I wrote myself a simple helper class that gets rid of these annoyances. I’m not sure I’d recommend using this in larger apps since it passes all the events over the same channel, but I find it makes my life with small apps much simpler.

Now I just create the EventMessage, and can use some easy Subscribe/Publish calls anywhere in the application. Simple, easy to remember syntax, no need to pass around an EventAggregator object, and all I have to create is my Event Messages.

// Create the Event Message
public class TickerSymbolSelectedMessage
{
    public string StockSymbol { get; set; }
}

// Subscribe to Events
EventSystem.Subscribe<TickerSymbolSelectedMessage>(ShowNews);

public void ShowNews(TickerSymbolSelectedMessage msg)
{
   // Handle Event
}

// Broadcast Events
EventSystem.Publish<TickerSymbolSelectedMessage>(
    new TickerSymbolSelectedMessage{ StockSymbol = “STOCK0”});

And here’s the EventSystem class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.Events;

namespace MyNamespace
{
    /// <summary>
    /// Static EventAggregator
    /// </summary>
    public static class EventSystem
    {
        private static IEventAggregator _current;
        public static IEventAggregator Current
        {
            get
            {
                return _current ?? (_current = new EventAggregator());
            }
        }

        private static CompositePresentationEvent<TEvent> GetEvent<TEvent>()
        {
            return Current.GetEvent<CompositePresentationEvent<TEvent>>();
        }

        public static void Publish<TEvent>()
        {
            Publish<TEvent>(default(TEvent));
        }

        public static void Publish<TEvent>(TEvent @event)
        {
            GetEvent<TEvent>().Publish(@event);
        }

        public static SubscriptionToken Subscribe<TEvent>(Action action, ThreadOption threadOption = ThreadOption.PublisherThread, bool keepSubscriberReferenceAlive = false)
        {
            return Subscribe<TEvent>(e => action(), threadOption, keepSubscriberReferenceAlive);
        }

        public static SubscriptionToken Subscribe<TEvent>(Action<TEvent> action, ThreadOption threadOption = ThreadOption.PublisherThread, bool keepSubscriberReferenceAlive = false, Predicate<TEvent> filter = null)
        {
            return GetEvent<TEvent>().Subscribe(action, threadOption, keepSubscriberReferenceAlive, filter);
        }

        public static void Unsubscribe<TEvent>(SubscriptionToken token)
        {
            GetEvent<TEvent>().Unsubscribe(token);
        }        public static void Unsubscribe<TEvent>(Action<TEvent> subscriber)
        {
            GetEvent<TEvent>().Unsubscribe(subscriber);
        }
    }
}