Archive

Archive for the ‘Uncategorized’ Category

Detecting Double Click Events on the WPF DataGrid

March 21st, 2009

Either I missed the obvious solution, or there is indeed no simple way to catch double click events on a given row of Microsoft’s WPF DataGrid.

This snippet here fires the event whenever the grid is clicked – it doesn’t matter whether the user double-clicks into an empty area (no rows) or a row:

<!-- fires even if the user does not click a given row -->
<dg:DataGrid
  dg:DataGridRow.MouseDoubleClick="OnDoubleClick" />

 

As a result, I reverted to a workaround by searching the Visual Tree of the event source for an instance of type DataGridRow. In order to find the ancestor item, I used a snippet I posted here a while ago. Here’s the full code:

 

XAML:

<!-- just register a listener on the grid -->
<dg:DataGrid
  MouseDoubleClick="OnDoubleClick" />

 

Code-Behind:

/// <summary>
/// Handles double-clicks on datagrid rows.
/// </summary>
private void OnDoubleClick(object sender, MouseButtonEventArgs e)
{
  //search the object hierarchy for a datagrid row
  DependencyObject source = (DependencyObject) e.OriginalSource;
  var row = UIHelpers.TryFindParent<DataGridRow>(source);

  //the user did not click on a row
  if (row == null) return;

  //[insert great code here...]

  e.Handled = true;
}

 

UIHelper class providing the TryFindParent method:

(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);
}

 

Enjoy 🙂

Author: Categories: DataGrid, Uncategorized Tags:

First Anniversary

January 16th, 2009

Wow, already a year since my first blog post! It’s been fun and I’m far from running out of ideas – stay tuned 🙂

Author: Categories: Uncategorized Tags:

Combining WPF Validation Rules and IDataErrorInfo to Resolve Conversion Errors

January 8th, 2009

This article discusses a few approaches to overcome binding conversion errors in WPF by combining model validation (through IDataErrorInfo) and WPF validation rules. Practically, I’m going to outline three approaches to the same solution:

  • Inline declaration
  • Using attached properties
  • Using custom binding classes

 

image

The Problem

WPF validation through IDataErrorInfo is a great feature if you already have validation logic in you model. However: There is a problem if the user enters a value that cannot be written to model due to value conversion errors. Let’s start with a simple sample:

  • A TextBox is bound to a numeric Age property of your model.
  • As soon as the user enters a value in the Textbox, the property on the model is updated. The model then validates whether the entered value is valid (range from 1 to 130 years).
  • However: If the user enters a string rather than a number (e.g. “30a”), the value conversion to an integer fails. In this case, WPF does not show an error at all:

image

 

Karl Shifflet recently updated his article on this very subject, where he outlines an MVVM-based approach to the problem (highly recommended reading material!).

In my case, however, I was looking for a simpler solution. I had validation and views already in place and just wanted a visual representation of invalid input that cannot be bound to the model.

 

WPF Validation Rules to the Rescue

And this is where the good old WPF Validation Rules come back into play:

  1. A binding with ValidatesOnDataErrors=True ensures that the model validation logic is being invoked.
  2. Additional WPF Validation Rules take care of invalid input that can not be bound to the model.

 

<TextBox x:Name="txtAge">
  
  <TextBox.Text>
    <Binding Path="Age" ValidatesOnDataErrors="True">
      <Binding.ValidationRules>
        <rules:NumericRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>

</TextBox>

 

That’s it already! An invalid numeric value (below minimum, above maximum) is handled by the model, everything else is caught be the NumericRule (see attached sample for the sources).

image

 

Simplify Markup 1: Attached Properties

The above sample has one catch – it forces me to write rather verbose XAML. This is where attached properties come to the rescue. Here’s the same thing using an attached property:

 

<TextBox x:Name="txtAge"
  Text="{Binding Path=Age, ValidatesOnDataErrors=True}"
  rules:Editors.TextRule="{StaticResource numericRule}"  
/>

 

In the sample above, I declared an attached property of type ValidationRule that points to by custom rule. However, depending on your case, you can make your attached property as simple/complex as you need. A great article on this subject was published by WPF master Josh Smith on CodeProject, where he outlines this exact pattern.

 

Simplify Markup 2: Custom Binding Classes

As an alternative to attached properties – especially in case you want to define multiple parameters – a custom binding expression might be a simpler approach. If you use a simple Decorator helper class, creating your custom binding class is a breeze:

 

<TextBox x:Name="txtCustomBinding"
  Text="{local:RuleBinding Path=Age, MinValueOverride=20}"
/>

 

Here’s my custom binding class, derived from BindingDecoratorBase:

using System;
using System.Windows.Controls;
using System.Windows.Data;
using Hardcodet.Wpf.ValidationRules;

namespace Hardcodet.Wpf.CustomBinding
{
  /// <summary>
  /// A binding extension that checks for a given
  /// numeric value.
  /// </summary>
  public class RuleBinding : BindingDecoratorBase
  {
    /// <summary>
    /// An optional override for the minimum value,
    /// that can be used to narrow the allowed range.
    /// </summary>
    public int? MinValueOverride { get; set; }


    /// <summary>
    /// Creates a new instance of the binding with default values.
    /// </summary>
    public RuleBinding()
    {
      //set default binding directives
      ValidatesOnDataErrors = true;
      UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    }


    /// <summary>
    /// This method is being invoked during initialization.
    /// </summary>
    /// <param name="provider">Provides access to the bound items.</param>
    /// <returns>The binding expression that is created by
    /// the base class.</returns>
    public override object ProvideValue(IServiceProvider provider)
    {
      //create the validation rule
      ValidationRule rule;
      if (MinValueOverride.HasValue)
      {
        //create a rule that also narrows the minimum value
        rule = new MinNumericRule() {MinValue = MinValueOverride.Value};
      }
      else
      {
        //just make sure the value is numeric
        rule = new NumericRule();
      }

      Binding.ValidationRules.Add(rule);

      //delegate binding creation etc. to the base class
      object val = base.ProvideValue(provider);
      return val;
    }
  }
}

 

The attached sample shows you all the techniques I described above.

Download sample project: validationrules.zip

kick it on DotNetKicks.com

Author: Categories: Uncategorized Tags: ,

Putting the Fun back to Sketching: Balsamiq

December 8th, 2008

Ayende wrote about it and I felt I just have to spread the word: balsamiq is GREAT! I played around with the tool for about 10 minutes and then had to have it. Below is my first sketch with the tool – this is about 20 minutes work:

balsamiq

Love it 😀

Author: Categories: Tools, Uncategorized Tags:

A Covariant ObservableCollection for .NET 3.x

November 28th, 2008

When starting with generics, I was somewhat suprised that something like this didn’t work:

 

public interface IAnimal
{
}

public class Pig : IAnimal
{
}

public class AnimalFarm
{
  private ObservableCollection<Pig> pigs;

  public IEnumerable<IAnimal> Animals
  {
    get { return pigs; } //DOES NOT COMPILE
  }
}

 

The problem is that generics aren’t covariant, which is sometimes a bit of a problem when working with interfaces. However, while we’re waiting for C# 4.0, there is a poor man’s solution to covariance – the idea is to just expose the required IEnumerable<IAnimal> interface explicitly for the interface. And of course, there’s a generic solution to that problem:

 

/// <summary>
/// An implementation of <see cref="ObservableCollection{T}"/> that provides
/// an <see cref="IEnumerable{X}"/> interface for a super type of
/// <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type of items to be stored in the collection.</typeparam>
/// <typeparam name="X">A super type of <typeparamref name="X"/>, for which this
/// collection provides an <see cref="IEnumerable{X}"/> interface, thus providing
/// covariance.</typeparam>
public class HybridObservableCollection<T, X> : ObservableCollection<T>,
                                                IEnumerable<X> where T : X
{

  /// <summary>
  /// Provides enumeration over type <see cref="X"/>.
  /// </summary>
  /// <returns>A <see cref="IEnumerator{X}"/>> that can be used to iterate
  /// through the collection.</returns>
  IEnumerator<X> IEnumerable<X>.GetEnumerator()
  {
    foreach (T t in this)
    {
      yield return t;
    }
  }

}

 

Note the type constraint: The second type parameter (X) must be convertible to the first one (T), which ensures that you can’t break the collection.

And as a result, we can return the collection as both IEnumerable<Pig> or IEnumerable<IAnimal>:

public class AnimalFarm
{
  //the collection provides both IEnumerable<Pig>, IEnumerable<IAnimal>
  private HybridObservableCollection<Pig, IAnimal> pigs;

  public IEnumerable<IAnimal> Animals
  {
    get { return pigs; } //WORKS
  }

  public IEnumerable<Pig> Pigs
  {
    get { return pigs; } //WORKS TOO
  }
}

 

Of course, rather than just IEnumerable<IAnimal>, you could easily expose IList<IAnimal> that way, but you would risk runtime exceptions if somebody tried to inject another IAnimal implementation into the base class that is not an instance of type Pig. However, in a safe environment, this might be well feasible if it lets you expose your collections as lists and still stick to interfaces.

Author: Categories: C#, Uncategorized, WPF 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: