Archive

Archive for the ‘.NET’ Category

A KeyedCollection for int keys

March 5th, 2008

I’m using this class so much that I thought I’d share it with you. This generic class is an implementation of the KeyedCollection<TKey, TItem> class for items that have a numeric key:

public abstract class NumericallyKeyedCollection<T> : KeyedCollection<int, T>
{ }

Using int-Keys takes a bit more work than other types, because KeyedCollection provides indexers for both key and index. However, if the key’s type is int as well, you don’t have that option anymore. This class fills that gap by providing a few things for you:

  • GetByKey, TryGetByKey, and GetByIndex methods
  • Simplified exception handling and much better exception messages that include the invalid parameters. You can even customize exception messages by overriding the virtual GetKeyNotFoundMessage method.
  • An AddRange method which takes an IEnumerable<T>
  • For security reasons, the indexer has been overwritten and throws an InvalidOperationException if it is invoked. I just caught myself too much using it with the wrong parameters (index rather than key or vice versa). You might want to reverse that if you need to automatically serialize the collection to XML.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Hardcodet.Util
{
  /// <summary>
  /// An implementation of a <see cref="KeyedCollection{TKey,TItem}"/> for items that
  /// have a numeric key, an therefore do not provide individual
  /// indexers for both key and index. As an alternative, this class provides
  /// <see cref="GetByIndex"/> and <see cref="GetByKey"/> methods plus more
  /// sophisticated exception handling for invalid keys.<br/>
  /// For security measures, the numeric indexer has been disabled, as using it
  /// is just misleading because one cannot tell whether it returns an item by
  /// index or by key...
  /// </summary>
  public abstract class NumericallyKeyedCollection<T> : KeyedCollection<int, T>
  {
    #region constructors

    /// <summary>
    /// Creates an empty collection.
    /// </summary>
    public NumericallyKeyedCollection()
    {
    }


    /// <summary>
    /// Inits the collection by copying all items of another
    /// collection.
    /// </summary>
    /// <param name="items">A collection of items to be added
    /// to this collection.</param>
    public NumericallyKeyedCollection(IEnumerable<T> items)
    {
      AddRange(items);
    }

    #endregion


    #region get by id

    /// <summary>
    /// Tries to retrieve a given item by its key.
    /// </summary>
    /// <param name="key">The key that was used to store the
    /// item.</param>
    /// <returns>The matching item, if any. Otherwise
    /// <c>default(T)</c> (null in case of standard objects).</returns>
    public T TryGetByKey(int key)
    {
      return Contains(key) ? GetByKey(key) : default(T);
    }


    /// <summary>
    /// Overrides the default implementation in order to provide a more sophisticated
    /// exception handling. In case of an invalid ID, an exception with a message is
    /// being thrown that is built the <see cref="GetKeyNotFoundMessage"/> method.
    /// </summary>
    /// <returns>The item with the matching key.</returns>
    /// <exception cref="KeyNotFoundException">Thrown if no descriptor
    /// with a matching ID was found.</exception>
    public T GetByKey(int key)
    {
      try
      {
        return base[key];
      }
      catch (KeyNotFoundException)
      {
        //throw custom exception that contains the key
        string msg = GetKeyNotFoundMessage(key);
        throw new KeyNotFoundException(msg);
      }
    }


    /// <summary>
    /// Overrides the default implementation in order disable usage of the indexer,
    /// as its usage is no longer clear because index and item keys are both of
    /// the same type.<br/>
    /// Invoking this indexer always results in a <see cref="InvalidOperationException"/>.
    /// </summary>
    /// <returns>Nothing - invoking the indexer results in a <see cref="InvalidOperationException"/>.</returns>
    /// <exception cref="InvalidOperationException">Thrown if the indexer is being invoked.</exception>
    public new virtual T this[int key]
    {
      get
      {
        string msg = "Using the indexer is disabled because both access through index and item key take the same type.";
        msg += " Use the GetByKey or GetByIndex methods instead.";
        throw new InvalidOperationException(msg);
      }
    }


    /// <summary>
    /// Gets an exception message that is being submitted with
    /// the <see cref="KeyNotFoundException"/> which is thrown
    /// if the indexer was called with an unknown key.
    /// This template method might be overridden in order to provide
    /// a more specific message.
    /// </summary>
    /// <param name="key">The submitted (and unknown) key.</param>
    /// <returns>This default implementation returns an error
    /// message that contains the requested key.</returns>
    protected virtual string GetKeyNotFoundMessage(int key)
    {
      string msg = "No matching item found for key '{0}'.";
      return String.Format(msg, key);
    }

    #endregion


    #region get by index

    /// <summary>
    /// Gets the item at the specified <paramref name="index"/>.
    /// </summary>
    /// <param name="index">Index within the collection.</param>
    /// <returns>The item at the specified index.</returns>
    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/>
    /// if not a valid index of the internal list.</exception>
    public virtual T GetByIndex(int index)
    {
      return Items[index];
    }

    #endregion


    #region add items

    /// <summary>
    /// Adds a number of items to the list.
    /// </summary>
    /// <param name="items">The items to be appended.</param>
    public void AddRange(IEnumerable<T> items)
    {
      foreach (T item in items)
      {
        Add(item);
      }
    }


    /// <summary>
    /// Adds an item to the end of the collection.
    /// </summary>
    /// <param name="item">Item to be added to the collection.</param>
    /// <remarks>This override just provides a more sophisticated exception
    /// message.</remarks>
    public new void Add(T item)
    {
      try
      {
        base.Add(item);
      }
      catch (ArgumentException e)
      {
        int key = GetKeyForItem(item);
        if (Contains(key))
        {
          string msg = "An item with key '{0}' has already been added.";
          msg = String.Format(msg, key);
          throw new ArgumentException(msg, "item", e);
        }
        else
        {
          //in case of any other argument exception, just throw the
          //original exception
          throw;
        }
      }
    }

    #endregion
  }
}

Using it is fairly easy. Imagine you have a user collection that stores User objects by their numeric UserId. All you need to get going is this:

/// <summary>
/// Stores <see cref="User"/> instances by their numer
/// <see cref="User.UserId"/>.
/// </summary>
public class UserCollection : NumericallyKeyedCollection<User>
{
  protected override int GetKeyForItem(User item)
  {
    return item.UserId;
  }
}

 

…accordingly, here’s how you manipulate the collection:

public void Test()
{
  UserCollection col = new UserCollection();
  col.Add(new User(123));

  User userByIndex = col.GetByIndex(0);
  User userByKey = col.GetByKey(123);
}
Author: Categories: C# Tags:

Programmatically filtering the WPF TreeView

February 12th, 2008

There will be filtering and multi selection support in the next iteration of my WPF TreeView, but based on a request on the Code Project forum, I decided to implement a simple filtering mechanism on the current version.

First of all, you can provide filtering without even touching the control base class by just applying the filter in your implementation of the abstract GetChildItems method. This method would effectively filter all items of the sample tree:

//returns subcategories that should be available through the tree
public override ICollection<ShopCategory>
                           GetChildItems(ShopCategory parent)
{
  //create a filtered list
  List<ShopCategory> list = new List<ShopCategory>();
  foreach(ShopCategory category in parent.SubCategories)
  {
    if ( ... ) list.Add(category);
  }
  
  return list;
}

In order to have the tree react to changed filter conditions, calling the tree’s Refresh() method takes care of everything.

This approach is dead simple, and it has the advantage that only items that are supposed to be accessible on the tree are being processed by the control. On the other hand, it also means that you would have to recreate the tree every time the tree’s filter changes.

 

In order to provide an alternative, I also looked at filtering the tree on the UI level (filtering == just hide the filtered nodes). The following sample sample operates on the tree implementation of the sample application, and provides a property of type Predicate<ShopCategory>. In order to get it working, I needed to do 3 things:

  • Apply the filter for new nodes that are being created
  • Run the filter if a node is being expanded
  • Refresh the tree if the filter is being set

 

I must say I’m quite satisfied – as the control provides me with virtual methods to intercept everything, the whole thing took about 2 minutes to set up 🙂

 

private Predicate<ShopCategory> filter = null;


/// <summary>
/// Defines a filter for items that are bound to the tree. Set to
/// null in order to disable filtering.
/// </summary>
public Predicate<ShopCategory> Filter
{
  get { return filter; }
  set
  {
    filter = value;

    //recreate the tree in order to apply the filter on
    //all currently visible nodes
    //-> of course, this could be optimized, but it does the job
    Refresh(GetTreeLayout());
  }
}


/// <summary>
/// Applies the filter on all child nodes.
/// </summary>
/// <param name="treeNode"></param>
protected override void OnNodeExpanded(TreeViewItem treeNode)
{
  //make sure child nodes are being created
  base.OnNodeExpanded(treeNode);

  //apply filter
  foreach (TreeViewItem childNode in treeNode.Items)
  {
    ApplyFilter(childNode, (ShopCategory)childNode.Header);
  }
}


/// <summary>
/// Immediately applies the filter on newly created items. This
/// is somewhat redundant (as we're also handling <see cref="OnNodeExpanded"/>),
/// but ensures we also consider root nodes.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
protected override TreeViewItem CreateTreeViewItem(ShopCategory item)
{
  //delegate node creation to base class
  TreeViewItem node = base.CreateTreeViewItem(item);

  //apply the filter and return the node
  ApplyFilter(node, item);
  return node;
}



/// <summary>
/// Filters categories if the <see cref="Filter"/> property
/// is set by simply setting the <see cref="TreeViewItem.Visibility"/>
/// property to <see cref="Visibility.Collapsed"/> if the item does
/// not match the filter.
/// </summary>
private void ApplyFilter(TreeViewItem node, ShopCategory item)
{
  bool visible = filter == null || filter(item);
  node.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
}

 

You can try it out by adding the above code to the sample tree (CategoryTree.cs), and setting the Filter property in an event handler of the sample app. Note that OnNodeExpanded is already overridden, so you’ll end up with two duplicate methods if you paste in the snippet.

Author: Categories: WPF TreeView Tags: ,

WPF application patterns and Unit Testing

February 1st, 2008

Josh Smith, prolific Code Project writer and blogger, has written a great article about MVC (or M-V-poo, as the doctor would say!) and unit testing of WPF apps. The article can be found on Code Project:

http://www.codeproject.com/KB/WPF/MVCtoUnitTestinWPF.aspx

Author: Categories: WPF Tags: ,

Finding an ancestor of a WPF dependency object

February 1st, 2008

This is a simple snippet which helps you to find a specified parent of a given WPF dependency object somewhere in its visual tree:

(Snippet updated 2009.09.14)

 

/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(this DependencyObject child)
    where T : DependencyObject
{
  //get parent item
  DependencyObject parentObject = GetParentObject(child);

  //we've reached the end of the tree
  if (parentObject == null) return null;

  //check if the parent matches the type we're looking for
  T parent = parentObject as T;
  if (parent != null)
  {
    return parent;
  }
  else
  {
    //use recursion to proceed with next level
    return TryFindParent<T>(parentObject);
  }
}

/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Keep in mind that for content element,
/// this method falls back to the logical tree of the element!
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(this DependencyObject child)
{
  if (child == null) return null;
  
  //handle content elements separately
  ContentElement contentElement = child as ContentElement;
  if (contentElement != null)
  {
    DependencyObject parent = ContentOperations.GetParent(contentElement);
    if (parent != null) return parent;

    FrameworkContentElement fce = contentElement as FrameworkContentElement;
    return fce != null ? fce.Parent : null;
  }

  //also try searching for parent in framework elements (such as DockPanel, etc)
  FrameworkElement frameworkElement = child as FrameworkElement;
  if (frameworkElement != null)
  {
    DependencyObject parent = frameworkElement.Parent;
    if (parent != null) return parent;
  }

  //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
  return VisualTreeHelper.GetParent(child);
}

 

 

This snippet works with arbitrary dependency objects that are of Type Visual or Visual3D. So let’s say you need a reference to the Window that hosts a given Button control somewhere, all you need is this:

Button myButton = ...
Window parentWindow = UIHelper.TryFindParent<Window>(myButton);

 

The above TryFindParent method also makes it easy to get an item at a given position. The method below performs a hit test based on a given position. If hit testing does not return the requested item (e.g. a clicked CheckBox on a tree, while you are keen on the TreeViewItem that hosts the CheckBox), the procedure delegates the lookup to TryFindParent.

This comes in very handy for mouse-related events if you just need to now what’s under your mouse pointer:

/// <summary>
/// Tries to locate a given item within the visual tree,
/// starting with the dependency object at a given position. 
/// </summary>
/// <typeparam name="T">The type of the element to be found
/// on the visual tree of the element at the given location.</typeparam>
/// <param name="reference">The main element which is used to perform
/// hit testing.</param>
/// <param name="point">The position to be evaluated on the origin.</param>
public static T TryFindFromPoint<T>(UIElement reference, Point point)
  where T:DependencyObject
{
  DependencyObject element = reference.InputHitTest(point)
                               as DependencyObject;
  if (element == null) return null;
  else if (element is T) return (T)element;
  else return TryFindParent<T>(element);
}
Author: Categories: WPF Tags:

Kaxaml source on CodePlex

January 27th, 2008

Robby Ingebretsen published the source code of his incredibly useful (and beautiful!) Kaxaml on CodePlex. I often prefer Kaxaml over Visual Studio because it combines a bunch of really smart features with a very nice UI that just makes it a pleasure to work with. I’m definitely looking forward to have a peek at its internals 🙂

CodePlex project site: http://www.codeplex.com/Kaxaml
Official Kaxaml download site: http://www.kaxaml.com

Author: Categories: Open Source, Tools, WPF Tags: , , ,

A versatile WPF TreeView control

January 24th, 2008

A tutorial is now available on Code Project, so check the article for a detailed overview. And please leave your rating if you like the control 🙂
http://www.codeproject.com/KB/WPF/versatile_treeview.aspx

Update: The latest version is currently only available through the download link below. I’ll update the CodeProject article once the current filtering mechanism has been rewritten:

Download: wpf-treeview.zip (Current version: 1.0.7, updated 2008.04.06)

TreeView example This is a little something I’ve been working on for a while: A replacement (or better: enhancement) of WPF’s built-in TreeView control.

I became aware of the default control’s limitations during my last project – I naturally started with hierarchical data templates, but was soon confronted with quite a few issues: I missed a simple API to control the tree, and styling of the tree’s nodes proved hard as well. Furthermore, WPF’s TreeView tends to fire all sorts of SelectedItemChanged events if it’s being refreshed or rebound, which caused side-effects with TwoWay data binding.

However, instead of posting a rant that probably nobody would ever read (let alone care about), I worked on an alternative. Here’s the tree’s main features at a glance:

  • Simple declaration:
    <local:ProductTree x:Name="MyTree"
                       Items="{Binding Source={StaticResource Shop},
                              Path=Products}"
                       SelectedItem="{Binding ElementName=MyProductList,
                              Path=ActiveItem, Mode=TwoWay}"
                       NodeContextMenu="{StaticResource CategoryMenu}"
                       TreeNodeStyle="{StaticResource SimpleFolders}"
                       TreeStyle="{StaticResource SimpleTreeStyle}"
                       SelectedItemChanged="OnSelectedItemChanged"
    />
  • Simple and type safe API:
    //bind flat list of business objects to tree
    List<Product> products = GetProducts();
    myTree.Items = products;
    
    //select a given item
    Product foo = GetBestSellingProduct();
    myTree.SelectedItem = foo; 
    
    //SelectedItem is of type Product - no casts required
    Product bar = myTree.SelectedItem;
  • Lazy loading support – does not create tree nodes until the parent node is expanded. Also provides the option to automatically clear invisible tree nodes. This allows either virtualized trees in case getting data is expensive, or low memory trees that keep the number of tree nodes at a minimum.
  • Simple sorting.
  • Convenient context menu handling for tree nodes
  • Optional root node which is not dependent on the tree’s bound items
  • Simple styling on every level: Tree, TreeViewItem, or bound items (via DataTemplates).
  • Tree layout can be cached, saved and reapplied.
  • Access to tree nodes (TreeViewItem) through bound items.
  • AutoCollapse feature / ExpandAll / CollapseAll methods

All this goodness comes at a price: The TreeViewBase class that provides this functionality, is abstract. This means you have to write a little code yourself. However, you’ll probably manage with 3 lines of code, as the base control just needs to know 3 things:

  • How to generate an identifier for a given tree node
  • How to access a bound item’s childs, if there are any
  • How to access a bound item’s parent, if there is one

Here’s the complete implementation of the sample application’s tree control:

//a tree control that handles only ShopCategory objects
public class CategoryTree : TreeViewBase<ShopCategory>
{

  //the sample uses the category's name as the identifier
  public override string GetItemKey(ShopCategory item)
  {
    return item.CategoryName;
  }

  //returns subcategories that should be available through the tree
  public override ICollection<ShopCategory>
                             GetChildItems(ShopCategory parent)
  {
    return parent.SubCategories;
  }

  //get the parent category, or null if it's a root category
  public override ShopCategory GetParentItem(ShopCategory item)
  {
    return item.ParentCategory;
  }

I’m planning to write a CodeProject article for this one, but for now, it’s only available through my place without a tutorial. However: The library comes with a sample project that shows pretty much all features of the control. Project format is currently VS2008 only, but binaries which target .NET 3.0 are included. Enjoy!

Sample Application