Archive

Archive for January, 2008

Writing on Wikipedia

January 30th, 2008

I abandoned the LGPL in favor of the Code Project Open License (CPOL) this morning (starting with the TreeView control), and I was pretty surprised that Googling for the license didn’t show up the license’s home page in the upper ranks.

As a result, I decided to write my first Wikipedia article in order to make this – IMO very nice license – at bit more accessible. The article itself is no big deal, but I must say, writing on Wikipedia is very convenient – I think I might do that again :)

Author: Categories: Open Source Tags:

Newsletters available

January 29th, 2008

I included a newsletter module into the blog – in case you would like to stay up-to-date about what’s going on here, you can now subscribe to a specific newsletter.

I’ll provide newsletter categories for all projects that are being released here, starting with the WPF TreeView, so this will be a convenient way to get notified if a new version has been made available.

Author: Categories: Uncategorized 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

Loaded event of a WPF control may be fired repeatedly

January 21st, 2008

In all my Windows Forms applications, I usually start with a common base class for forms and controls that provides a bunch of convenience properties, subscribes to common events, handles proper cleanup etc. I also implement an empty virtual InitControl method which can be overridden by deriving controls to initialize themselves.

As you can see in the Windows Forms snippet below, InitControl is being called right after the control’s Load event has been fired:

/// <summary>
/// Inits the base class and registers event listeners.
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{     
  base.OnLoad(e);
  
  if (!DesignMode)
  {
    InitControlInternal();       
    InitControl();
    initControlCompleted = true;
  }
}


/// <summary>
/// An empty template method which is being invoked after the
/// control has been loaded during <see cref="OnLoad"/>.
/// </summary>
/// <remarks>The base class does not invoke this method if
/// the control is in design mode.</remarks>
protected virtual void InitControl()
{
  //this method is supposed to be overridden. Initialization of
  //the base class itself happens in InitControlInternal
}

 

Naturally, I kept this practice in WPF using the Loaded event which is available for all WPF controls. But to my surprise, InitControl method was getting called repeatedly while I expected it to be called just once, which causes some controls to be re-initialized over and over again. The reason in my case was a TabControl that hosted my user controls. As it turned out, TabControl unloads/reloads user controls with every tab switch.

Unfortunately, there seems to be no common solution to this very problem – you will have to decide how to handle Loaded events specifically for your initialization logic. However:

  • Using Loaded may not be problem in a lot of scenarios, but keep in mind that you might run in trouble when depending on it, especially with a reusable control.
  • You can use the Initialized event of a control rather than Loaded. Check the API documentation for a description of the two events.
  • Use a boolean field to track whether your initialization code has alreay been invoked:

    protected override void InitControl()
    {
      if (initControlCalled) return;
    
      initControlCalled = true;
      //init control
      ...
    }

 

I’ve implemented a short sample that demonstrates the issue. Basically, it’s a tab control that hosts a user control on one of its tab items:

<TabControl>
  <TabItem Header="Tab 1">
    <!-- the user control that counts Loaded events -->
    <local:LoadedCounter />
  </TabItem>

  <TabItem Header="Tab 2">
    <TextBlock>Switch back to trigger event in tab 1.</TextBlock>
  </TabItem>
</TabControl>

 

Once you switch back and forth, you can see that Loaded is being invoked with every switch:

TabControl

Download Sample Project (VS2008)

Author: Categories: WPF Tags: ,

Simplify MessageBox handling with WPF

January 18th, 2008

This is really trivial, but I’ve always used a similar helper class for my WinForms apps, and it was something I immediately missed when starting my first WPF project. So I thought I should share it with you – a simple helper class to display message boxes without having to worry about a lot of parameters just to show a message with a title and an icon.

This is basically just a façade to the standards MessageBox class, but more convenient. As an example, if you want to show a message box with a warning icon, you currently have to submit quite a few parameters:

string msg = "This is a warning.";
string title = "My Application";
MessageBox.Show(msg, title, MessageBoxButton.OK, MessageBoxImage.Warning);


With the Dialogs helper class, you can get the same result quite painless:

Dialogs.ShowWarning("This is a warning.");

The class provides most common operations, including Yes/No message boxes, and if you need more overloads, adding them isn’t really a problem.

Dialogs Helper Class

One last note: As you can see in the class diagram, the class contains a hard coded ApplicationName field which is used for the dialog’s title: This is bad practice. I’d recommend to create a string in Resources.resx, remove the field, and update the four or five references to the field accordingly.

using System.Windows;

namespace Evolve.Wpf.Samples
{
  /// <summary>
  /// Provides convenience methods to display message boxes.
  /// </summary>
  public static class Dialogs
  {
    //TODO rather put a string in a resource file - it's just cleaner
    public static string ApplicationName = "Change ApplicationName in Dialogs.cs";


    /// <summary>
    /// Displays an error dialog with a given message.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    public static void ShowError(string message)
    {
      ShowMessage(message, MessageBoxImage.Error);
    }


    /// <summary>
    /// Displays an error dialog with a given message.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    public static void ShowInformation(string message)
    {
      ShowMessage(message, MessageBoxImage.Information);
    }


    /// <summary>
    /// Displays an error dialog with a given message.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    public static void ShowWarning(string message)
    {
      ShowMessage(message, MessageBoxImage.Warning);
    }


    /// <summary>
    /// Displays an error dialog with a given message and icon.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    /// <param name="icon">The icon to be displayed with the message.</param>
    public static void ShowMessage(string message, MessageBoxImage icon)
    {
      string appName = ApplicationName;
      MessageBox.Show(message, appName, MessageBoxButton.OK, icon);
    }


    /// <summary>
    /// Displays an OK / Cancel dialog and returns the user input.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    /// <param name="icon">The icon to be displayed.</param>
    /// <returns>User selection.</returns>
    public static MessageBoxResult ShowOkCancel(string message, MessageBoxImage icon)
    {
      string appName = ApplicationName;
      return MessageBox.Show(message, appName, MessageBoxButton.OKCancel, icon);
    }


    /// <summary>
    /// Displays a Yes/No dialog and returns the user input.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    /// <param name="icon">The icon to be displayed.</param>
    /// <returns>User selection.</returns>
    public static MessageBoxResult ShowYesNo(string message, MessageBoxImage icon)
    {
      string appName = ApplicationName;
      return MessageBox.Show(message, appName, MessageBoxButton.YesNo, icon);
    }


    /// <summary>
    /// Displays an Yes / No / Cancel dialog and returns the user input.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    /// <param name="icon">The icon to be displayed.</param>
    /// <returns>User selection.</returns>
    public static MessageBoxResult ShowYesNoCancel(string message, MessageBoxImage icon)
    {
      string appName = ApplicationName;
      return MessageBox.Show(message, appName, MessageBoxButton.YesNoCancel, icon);
    }
  }
}
Author: Categories: WPF Tags: ,