Archive

Archive for the ‘WPF Controls’ Category

A WPF File Selection control

March 14th, 2008

 fileselector

This is a pretty simple user control, which allows you to display a file dialog to open or save files. Its look can be easily adjusted, and it provides built-in truncation of the file string to a predefined length if necessary. Here’s the XAML for the above sample control:

<files:FileSelector x:Name="openFileSelector"
                    Mode="Open"
                    MaxDisplayLength="50"
                    Height="24"
                    Width="400" />

 

The TextBlock in the screenshot which displays the full file path was simply bound to the control’s FileName dependency property:

<TextBlock Text="{Binding ElementName=openFileSelector, Path=FileName}" />

 

The control does not provide too many extension or styling points – the idea is that you just copy it into your solution, adjust the styling of the control’s contents (Border, Button etc.) and be on your way. The source comes with a small sample project – enjoy 🙂

Download Control

Author: Categories: WPF Controls Tags: ,

New Blendables

March 6th, 2008

Just saw that Blendables have extended their portfolio of WPF controls. The stuff looks good, but unfortunately, their licensing scheme doesn’t:

A license is required for each machine utilizing the blendables controls. […] As we do not offer a deactivation method, if you must reactivate on a new developer machine you are allowed up to 3 activations. This is for the case of re-imaging or setting up a new developer machine. Once this limit is reached you must contact blendables support at […] to proceed with activation.

I must say, not purchasing their product is a no-brainer…

Author: Categories: WPF Controls Tags:

Preventing WPF TreeView items from being selected

March 6th, 2008

If you have a WPF TreeView control that shows nested data, and you don’t want the user to select nodes that contain child nodes, you can solve this declaratively as TreeViewItem provides all we need:

  • HasItems dependency property (bool)
  • Focusable dependency property (bool)

As both properties have the same type, you can use a binding expression (needs inversion of the boolean), or just use a trigger. Here’s the trigger:

 

<!-- root items can only be expanded, not selected -->
<Trigger Property="HasItems"
         Value="true">
  <Setter Property="Focusable"
          Value="false" />
</Trigger>

 

tree_dialogikWith this trigger in place, simply clicking on a node that contains child items does not change the tree’s current selection, while expanding/collapsing still works.

A common scenario for such a behavior is a tree that contains nodes which are used for grouping purposes only as in the example screenshot.

Author: Categories: WPF, WPF Controls, WPF TreeView 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: ,

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