Home » Blog

rounded header

Archive for April, 2008

Smart editor declarations in the WPF Property Grid

We’ve had a couple of customers recently asking about assigning editors in the WPF Property Grid at runtime rather than in XAML. For this article, I’m going to borrow a scenario from one of our forum participants. He’s using an ItemsSource dictionary to populate the grid and wants to hook up every “image” entry to a custom editor he’s developed. Because he can’t know the list of “image” entries at compile time, he’s programmatically adding PropertyEditors to the PropertyGrid.Editors collection as he builds his dictionary: whenever he sees a dictionary entry go past that needs his image editor, he creates a PropertyEditor, sets the PropertyName to the dictionary entry name, and adds it to the grid.

This is a good working solution, and probably the best available using the standard editor types. But he got me wondering if there was a way to make the grid work a bit harder to help him. There’s nothing inherently wrong with procedural code, but it feels a bit antithetical to the usual WPF idioms of declarative XAML and data binding. There’s potentially a minor scalability issue if you end up adding zillions of editors (we’ll see why below). And on a more practical level, having to add editors to the grid as you add entries to the dictionary means that you have to guard access to the dictionary, whereas the data binding idiom says you should be able to add stuff to the dictionary from wherever it’s appropriate, and let the UI layer worry about hooking up the UI.

In order to understand how to achieve this, we need to understand something of how the WPF Property Grid editor infrastructure hangs together. An “editor”, in WPF Property Grid terminology, is actually a declaration that tells the grid to use a certain DataTemplate for certain nodes. A TypeEditor tells the grid to use a certain DataTemplate if the property value is of a certain type; a PropertyEditor tells the grid to use a certain DataTemplate if the property has a certain name. When the WPF Property Grid needs to select a DataTemplate for a node, it goes through the list of editors in its Editors collection until it finds one that can edit the node in question. If it doesn’t find one in the Editors collection, it falls back on some internal logic to select one of the built-in editors. (This, by the way, is why adding zillions of editors might slow things down: for each node in the grid, the grid has to perform a linear search of the Editors collection. But you’d have to add quite a lot of editors for that to be an issue.)

Now, as it happens, the WPF Property Grid has no idea that TypeEditors know about types or that PropertyEditors know about property names: it just asks each editor “can you edit this node?” until it finds one that says “yes,” at which point it says, “give me a DataTemplate for it then.” So if we could create an editor class that answered “yes” to “image” nodes, we wouldn’t need individual PropertyEditor declarations for each image entry. And if we look at the documentation it turns out that the Editor base class does expose the CanEdit method, allowing you to override it to create your own custom hookups.

Before we rush in and start deriving classes from Editor, though, it’s worth glancing at the Editor hierarchy. This is because the other abstract method on Editor, BuildTemplate, is much too tedious to implement ourselves. (It’s not just a matter of returning EditorTemplate.) PropertyEditor doesn’t inherit directly from Editor, but from ObjectWrappingEditor. Since our scenario is motivated by a wish to be “like a PropertyEditor only smarter,” and since ObjectWrappingEditor provides an implementation of BuildTemplate for us, that will actually be a good starting point for us as well.

Finally, let’s write some code, or at least let’s get Visual Studio to write it for us:

public class PropertyNameContainsEditor : ObjectWrappingEditor
{
  protected override void SetContentTemplate(FrameworkElementFactory factory, Node node)
  {
    throw new NotImplementedException();
  }
 
  public override bool CanEdit(Node node)
  {
    throw new NotImplementedException();
  }
}

We have two abstract methods that we need to override: CanEdit and SetContentTemplate.

CanEdit is pretty obvious, though you do need to bear in mind that the method can be called for any and every node in the grid, so don’t make any assumptions about what you’re being passed, and don’t write anything that runs too slow. For our scenario, we have a dead simple criterion: if the name contains the word “image,” we’re interested. We’ll actually make our editor a bit more flexible so we can reuse it (with different templates of course) for other magic words such as “email” or “URL”:

  public string ContainedWord { get; set; }
 
  public override bool CanEdit(Node node)
  {
    // Ignoring issues of casing, culture, etc. for simplicity
    return node.HumanName.Contains(ContainedWord);
  }

SetContentTemplate is a bit more mysterious. The documentation says it “attaches the editor data template to the EditorTemplate.” What this actually means is that the ObjectWrappingEditor has built a bunch of plumbing and now wants you to bolt the DataTemplate specified in EditorTemplate into that plumbing. This is a wart: the reason it exists is because of some internal components that use the ObjectWrappingEditor infrastructure but don’t use the EditorTemplate property. For our purposes, we always want to use the DataTemplate specified in EditorTemplate: that’s what it’s there for, after all. This requires the following cryptic incantation:

  protected override void SetContentTemplate(FrameworkElementFactory factory, Node node)
  {
    factory.SetValue(ContentControl.ContentTemplateProperty, EditorTemplate);
  }

Don’t worry too much about what this is doing: just shove it in a base class and forget about it. We’ll probably provide such a base class ourselves in a future release or sample.

And that’s it for the editor definition. Now we can hook it up in the same way as we hook up TypeEditors and PropertyEditors:

<ms:PropertyGrid AllowModifyCollections="False">
  <ms:PropertyGrid.Editors>
    <local:PropertyNameContainsEditor ContainedWord="Image"
                                      EditorTemplate="{StaticResource ImageSelector}" />
  </ms:PropertyGrid.Editors>
</ms:PropertyGrid>

Here’s a picture of our smart editor doing its stuff:

You can of course take this approach still further. One idea that offers some interesting possibilities is mixing it with smart dictionary keys. For example, another forum participant asked about selectively making dictionary entries read-only. You could do this using a smart dictionary key to carry the IsReadOnly property, and have your CanEdit method examine node.IndexedPropertyArguments[ 0 ] (watch out: the grid may spring hidden nodes on you which don’t have any indexed arguments, so only do this inside a guard clause or an exception handler). If you’re primarily driving the grid off dynamic data via ItemsSource rather than off a static set of properties using SelectedObject, then smart editors are a handy technique to have around!

WPF Elements MulticolumnTreeView and XML data

tag icon Tagged as General, WPF, WPF Elements

A commenter on a previous post asks about using XML data in a TreeListView and specifically about how to vary the display in a way which is sensitive to the XML nodes and attributes. The techniques I posted about for displaying heterogeneous object hierarchies apply pretty much identically to XML, so here’s a brief primer on using XML in the WPF Elements MulticolumnTreeView.

I’m going to use the same taxonomic hierarchy as the previous sample, but now we need to look at the XML format. Here’s a fragment:

<Class EnglishName="Mammals" LatinName="Mammalia">
  <Orders>
    <Order EnglishName="Carnivorous mammals" LatinName="Carnivora">
      <Families>
        <Family EnglishName="Cats" LatinName="Felidae">
          <Genera>
            <Genus EnglishName="Cat" LatinName="Felis">
              <Species>
                <Species EnglishName="Wild cat" LatinName="Felis silvestris" />
                <Species EnglishName="Domestic cat" LatinName="Felis silvestris catus" />
                <Species EnglishName="Jungle cat" LatinName="Felis chaus" />
              </Species>
            </Genus>
            <Genus EnglishName="Big cats" LatinName="Panthera">
              <Species>
                <Species EnglishName="Lion" LatinName="Panthera leo" 
                         ConservationStatus="Vulnerable"
                         ConservationDetails="Estimated 30-50% decline over the last two decades" />
                <Species EnglishName="Tiger" LatinName="Panthera tigris" 
                         ConservationStatus="Endangered"
                         ConservationDetails="Wild population may be as low as 2500 mature breeding individuals" />
                <Species EnglishName="Leopard" LatinName="Panthera pardus" />
              </Species>
            </Genus>
          </Genera>
        </Family>
    </Order>
  </Orders>
</Class>

The structure of this file is very regular; the only wrinkle is that I have all these collection nodes (Orders, Families, Genera) which just serve as groupers within the XML and shouldn’t be displayed. So my hierarchy declaration looks like this:

<HierarchicalDataTemplate x:Key="TaxonomyXml" ItemsSource="{Binding XPath=child::*/child::*}" />

Note the use of XPath in the ItemsSource binding. Our data templates also need to be updated to use XPath:

<DataTemplate x:Key="DefaultEnglishNameTemplate">
  <TextBlock Text="{Binding XPath=@EnglishName}" />
</DataTemplate>
 
<DataTemplate x:Key="LatinNameTemplate">
  <TextBlock Text="{Binding XPath=@LatinName}" />
</DataTemplate>

What about the customisation? In the object example, we could look at the type of object to determine whether to use our custom Species template (with the conservation status flash) or the plain text template. In the XML case, we instead look at the XmlElement that the HierarchicalDataTemplate has given us. We have access to all its properties and attributes, but in this case the element name is sufficient to tell us whether to use the Species template:

public class ClassificationTypeSelectorXml : DataTemplateSelector
{
  public DataTemplate SpeciesTemplate { get; set; }
  public DataTemplate DefaultTemplate { get; set; }
 
  public override DataTemplate SelectTemplate(object item, DependencyObject container)
  {
    XmlElement element = (XmlElement)item;
    if (element.LocalName == "Species")
    {
      return SpeciesTemplate;
    }
    else
    {
      return DefaultTemplate;
    }
  }
}

I won’t spell out the custom Species template or the selector declaration: they’re the same as in the previous article, but with property bindings converted to XPath bindings just as in the plain text templates, and of course using our new selector type. We do have one thing to watch out for: our XML doesn’t include a ConservationStatus attribute when a species has the default value of NotThreatened. So we need to add another trigger with a Value of “{x:Null}” to ensure that the endangerment flash is correctly hidden when the attribute is not present. (We could probably have avoided this if I’d made the default template behaviour line up with the default values in the first place, but you live, you learn.)

With that done, and an XmlDataProvider set up to load in the file, here’s the MulticolumnTreeView declaration to tie it all together:

<ms:MulticolumnTreeView Margin="10" Grid.Row="0"
                        ItemsSource="{Binding Source={StaticResource xml}, XPath=Class/Orders/Order}" ItemTemplate="{StaticResource TaxonomyXml}">
  <ms:MulticolumnTreeView.Columns>
    <GridViewColumn Header="English Name" CellTemplateSelector="{StaticResource ByClassificationLevel}" Width="250" />
    <GridViewColumn Header="Latin Name" CellTemplate="{StaticResource LatinNameTemplate}" Width="200" />
  </ms:MulticolumnTreeView.Columns>
</ms:MulticolumnTreeView>

Again, if you have more extreme display customisation needs, you can continue to use the techniques from the previous article, with similar changes to what we’ve already seen: use element names instead of types and XPath instead of properties, and everything should work in exactly the same way.

Heterogeneous hierarchies in the WPF Elements MulticolumnTreeView

tag icon Tagged as WPF, WPF Elements

The MulticolumnTreeView (aka TreeListView) in WPF Elements combines the functionality of a TreeView and a ListView, supporting the hierarchy and expansion features of a TreeView with the multicolumn display of a ListView.

Most of the time, when you use a MulticolumnTreeView, you’ll be using a homogeneous hierarchy — that is, with the same type of object at each level. This is because the set of columns is set at the control level. If the objects at each level are too different, it doesn’t make much sense to try to present them under the same set of columns.

Sometimes, however, you may have a heterogeneous hierarchy where, although the objects in the hierarchy have different types, there’s enough commonality that a column approach makes sense.

For example, suppose we’re displaying a taxonomic hierarchy, such as the biological classification of organisms into kingdoms, phyla, species and so on. We’ll imagine that the entries in the taxonomy are represented by classes such as Kingdom, Phylum and Species. These classes have some properties in common (say, Description and LatinName), but each may have additional class-specific properties (for example, Species might have a flag indicating the species’ conservation status, or the URL of a picture of an example of the species). We’ll also assume that each class has a different name for its collection of children: the Order class has a Families collection, the Family class a Genera collection and the Genus class a Species collection.

We’ll first tackle the problem of creating the tree. With a homogeneous hierarchy, you can just tell the MulticolumnTreeView what the “children” property is, and all will be well:

<ms:MulticolumnTreeView ItemTemplate="{ms:ChildPath Children}" />

But each of our classification types has a different name for its “children” collection. So this won’t work for our hierarchy.

To address this, we need to go a bit more deeply into the workings of the MulticolumnTreeView. A MulticolumnTreeView is a TreeView, so it builds its hierarchy from a HierarchicalDataTemplate (it just ignores the body of the template in favour of the column-based display). The {ms:ChildPath} syntax is a shortcut for defining a HierarchicalDataTemplate, but we don’t have to take the shortcut. As in a normal TreeView, we can explicitly create a HierarchicalDataTemplate. And, also as in a normal TreeView, as part of that HierarchicalDataTemplate we can, if we want, specify a different ItemsSource to get us to the next level of the hierarchy. So we can tell the MulticolumnTreeView how to navigate our heterogeneous hierarchy as follows:

<Window.Resources>
  <HierarchicalDataTemplate x:Key="SpeciesTemplate" ItemsSource="{x:Null}" />
  <HierarchicalDataTemplate x:Key="GenusTemplate" ItemsSource="{Binding Species}" 
                            ItemTemplate="{StaticResource SpeciesTemplate}" />
  <HierarchicalDataTemplate x:Key="FamilyTemplate" ItemsSource="{Binding Genera}" 
                            ItemTemplate="{StaticResource GenusTemplate}" />
  <HierarchicalDataTemplate x:Key="OrderTemplate" ItemsSource="{Binding Families}" 
                            ItemTemplate="{StaticResource FamilyTemplate}" />
  <HierarchicalDataTemplate x:Key="ClassTemplate" ItemsSource="{Binding Orders}" 
                            ItemTemplate="{StaticResource OrderTemplate}" />
</Window.Resources>

(The reason for the explicit SpeciesTemplate is that, if we didn’t have this, the TreeView would keep trying to apply the nearest template at the Species level. That would be the GenusTemplate, and that would cause binding errors when WPF tried to find the Species property, specified in GenusTemplate’s ItemsSource, on the Species class. These errors are benign, but there’s no harm in keeping things clean.)

Now that we can display all the levels of our hierarchy, what about tailoring the display of specific types? This depends on how much tailoring you need to do. For now, let’s put a little colour indicator next to the names of endangered species.

We can do this by setting the CellTemplateSelector property of the GridViewColumn (instead of the DisplayMemberBinding or CellTemplate). Our selector will examine the type of the object being templated, and return a special template if the object is a Species. Then it’s just a matter of defining the “normal” and “species” templates:

<DataTemplate x:Key="DefaultEnglishNameTemplate">
  <TextBlock Text="{Binding EnglishName}" />
</DataTemplate>
 
<DataTemplate x:Key="SpeciesEnglishNameTemplate">
  <StackPanel Orientation="Horizontal">
    <Rectangle Name="StatusRect" Stroke="Black" StrokeThickness="1" 
               Fill="Orange"
               Height="10" Width="10" Margin="-15,0,4,0" />
    <TextBlock Text="{Binding EnglishName}" />
  </StackPanel>
  <DataTemplate.Triggers>
    <DataTrigger Binding="{Binding ConservationStatus}" Value="NotThreatened">
      <Setter TargetName="StatusRect" Property="Visibility" Value="Hidden" />
    </DataTrigger>
    <DataTrigger Binding="{Binding ConservationStatus}" Value="Endangered">
      <Setter TargetName="StatusRect" Property="Fill" Value="Red" />
    </DataTrigger>
  </DataTemplate.Triggers>
</DataTemplate>
 
<local:ClassificationTypeSelector x:Key="ByClassificationLevel"
                                  DefaultTemplate="{StaticResource DefaultEnglishNameTemplate}"
                                  SpeciesTemplate="{StaticResource SpeciesEnglishNameTemplate}" />

Here’s the result:

MulticolumnTreeView showing custom cell template

What we’ve described here is limited to working within a cell (that is, a single column of the item row). If you need to create some UI that spans cells, breaks out of the cellular structure or replaces the cellular structure, then you need to start templating the MulticolumnTreeViewItems. To get started with this, check out the MulticolumnTreeViewStyling.xaml page in the Elements sample project included with the WPF Elements package. The difference in the case of heteregeneous hierarchies is that — depending on how radical your level-specific customisations are — you’ll probably need to use ItemContainerStyleSelector instead of ItemContainerStyle to determine the template.

In our example, let’s extend the conservation status colour to be the background to the row instead of just a little indicator, and add a message expanding on this. (I’ve also kept some other chrome from the Elements sample.)

First, we need a StyleSelector. This will have exactly the same logic as the DataTemplateSelector from the previous iteration, but now it’s returning Styles instead of DataTemplates.

Now we define the styles. I’m going to base these closely on the Elements sample. For my default style, in fact, I’m just going to use the Elements sample style, with some application-specific bits taken out. For my species style, I’m going to add a coloured rectangle and a TextBlock, with appropriate data bindings and triggers. Here’s the species style — I’ve left out the default style because it’s similar but simpler — and the selector declaration:

<Style x:Key="SpeciesMulticolumnTreeViewItem" TargetType="ms:MulticolumnTreeViewItem">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="ms:MulticolumnTreeViewItem">
        <Border Margin="0,2,5,2" Background="Transparent" BorderThickness="0">
          <Grid>
            <Rectangle Fill="Orange" RadiusX="6" RadiusY="6" Name="StatusRect"
                       Margin="{Binding Level, Converter={StaticResource LevelToIndentConverter}, RelativeSource={RelativeSource AncestorType={x:Type ms:MulticolumnTreeViewItem}}}" />
            <StackPanel>
              <GridViewRowPresenter Name="PART_Header" Content="{TemplateBinding Header}"
                                    Columns="{Binding WrappedColumns, RelativeSource={RelativeSource AncestorType={x:Type ms:MulticolumnTreeView}}}" />
              <Border Margin="{Binding Level, Converter={StaticResource LevelToIndentConverter}, RelativeSource={RelativeSource AncestorType={x:Type ms:MulticolumnTreeViewItem}}}"
                      Padding="20,3,3,3">
                <TextBlock Name="StatusText" Text="{Binding ConservationDetails}" 
                           FontFamily="Times New Roman" FontStyle="Italic" />
              </Border>
              <ItemsPresenter Name="ItemsHost" />
            </StackPanel>
          </Grid>
        </Border>
        <ControlTemplate.Triggers>
          <Trigger Property="IsExpanded" Value="False">
            <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
          </Trigger>
          <DataTrigger Binding="{Binding ConservationStatus}" Value="NotThreatened">
            <Setter TargetName="StatusRect" Property="Visibility" Value="Hidden" />
            <Setter TargetName="StatusText" Property="Visibility" Value="Hidden" />
          </DataTrigger>
          <DataTrigger Binding="{Binding ConservationStatus}" Value="Endangered">
            <Setter TargetName="StatusRect" Property="Fill" Value="Red" />
          </DataTrigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
 
<local:ClassificationStyleSelector x:Key="StyleByClassificationLevel"
                                   DefaultStyle="{StaticResource CustomMulticolumnTreeViewItem}"
                                   SpeciesStyle="{StaticResource SpeciesMulticolumnTreeViewItem}" />

And here’s the result:

MulticolumnTreeView showing type-specific item styles

So, to summarise:

  • If everything in your hierarchy is the same (or at least the bits you’re interested in are the same), you can use a single ItemTemplate and the DisplayMemberBinding or CellTemplate properties.
  • If the “children” collections have different names at different levels, use multiple HierarchicalDataTemplates and specify the ItemsSource property on each.
  • To vary the appearance or content of a cell, use CellTemplateSelector.
  • To vary the appearance or content of an entire row, use ItemContainerStyleSelector.
  • You can mix and match these techniques as required to customise your user interface precisely to the kind of data you need to present.

kick it on DotNetKicks.com

LightSpeed 2.0 Model Designer

tag icon Tagged as LightSpeed, News, Products

A couple of days ago I dropped a teaser on my blog about an upcoming addition for LightSpeed 2.0 – the introduction of an design surface for Visual Studio which allows you to visually work with your LightSpeed models.

This feature has been added directly from feedback you gave us which asked us to consider code generation and visual modeling capabilities for LightSpeed to make it even easier to build up your models, particularly when working against an existing database schema (or a database schema you are maintaining on the side).

So with LightSpeed 2.0 we will be shipping 2 new additions:

1. A command line generation tool (think SQLMetal) which allows you to target an existing database and generate your model classes based on the existing schema. You can also use this to progressively regenerate if you are evolving your schema.

2. An add-in for Visual Studio 2008 which gives you the visual design surface. Let me elaborate on this a bit more..

Once you install the LightSpeed Designer you can add LightSpeed Model (.lsmodel) files to your project. These will hold the XML based definition of our model.

Add a new LightSpeed Model

Opening our LightSpeed Model presents a design surface, a toolbox and a property pane for tweaking our entities. We support manipulating Entities through the design surface giving you the full flexibility you would expect when working with LightSpeed entities such as setting Validators, toggling EagerLoad behavior and specifying aggregate or back reference names.

Using the design surface is easy – you can start by using the Server Explorer and connecting to a local database. Tables can then be dragged on to the surface as Entities.

Easy modeling via Drag and Drop

The Designer has some smarts, understanding the conventions of LightSpeed; So if we drag a second table called Team on to the surface, which already has a relationship to Employee defined in the database, and has the appropriate Id column defined – then the designer wires up and displays the relationship.

The Designer understands LightSpeed

The output of all of this work is our model classes. Behind the scenes when we compile our project, a background code generation runs and updates a class file containing the definition of our model. We can extend these using partial classes if we have some customizations that we don’t want to be obliterated by the codegen.

Here is an example of some of the code it produces:

Both of these new additions are still in beta and we are just about to start getting them into the hands of our LightSpeed 2.0 beta testers for feedback. If you are keen to have a play, drop us a line and we can get you on to the beta program :)

kick it on DotNetKicks.com

Boosting the WPF Property Grid with WPF Elements

The WPF Property Grid includes several simple editors to provide an improved user experience when editing values of numeric, date, colour etc. types. WPF Elements, Mindscape’s suite of line of business controls for WPF, offers a set of controls with a similar role but with significantly enhanced capabilities. Here’s a run-down of why you might want to move up from the built-in editors to the WPF Elements controls:

  • NumericUpDown: WPF Elements provides support for currency formatting, integer-only entry and keyboard-based value adjustments (hiding the up-down buttons).
  • MaskedTextBox: Improved visual feedback and a more intelligent input filtering mechanism.
  • DateEditor: Supports times as well as dates, and allows users to edit values directly instead of having to drop down the calendar.

You can use the Elements controls in PropertyEditors or TypeEditors just as you can any other control. For example, here’s how to configure a property to use an integer-only numeric up-down editor:

<DataTemplate x:Key="IntegerEditor">
  <ms:SpinDecorator Change="1" BorderThickness="0">
    <ms:IntegerTextBox Value="{Binding Value}" BorderThickness="0" />
  </ms:SpinDecorator>
</DataTemplate>
 
<!-- ... and in the PropertyGrid.Editors section... -->
<ms:PropertyEditor PropertyName="SomeProperty" EditorTemplate="{StaticResource IntegerEditor}" />

One catch to look out for is that several of the WPF Elements editors allow users to adjust values using the cursor-up and cursor-down keys. By default, the WPF Property Grid interprets cursor-up and cursor-down as moving the selection up or down in the grid, which prevents this WPF Elements feature from working. We’ve added a CursorKeyMode property to the grid to allow you to override this behaviour and let the editors have first crack at the cursor keys. If you set CursorKeyMode=”PassToEditor” on the grid, then the value-adjustment behaviour will be restored. Users can still navigate using the cursor keys, but only when the focus is on the property name rather than inside an editor. CursorKeyMode is available in the nightly builds of the WPF Property Grid: you can download the trial edition from http://www.mindscape.co.nz/Products/WpfPropertyGrid/nightlybuilds.aspx or the retail edition from the store.

Data Products Visual Controls Community Store
LightSpeed ORM
NHibernate Designer
SimpleDB Tools
SharePoint Tools
WPF Elements
WPF Diagrams
Silverlight Elements
Forums
Blog
Register
Login
Subscribe to newsletter
Buy Now
My Account
Volume Discounts
Purchase Orders
Contact Us