Home » Blog

rounded header

Archive for the ‘WPF Elements’ category

Awesome DataGrid Productivity Features – DataTemplate Reuse With PropertyEditors

tag icon Tagged as WPF Elements

An interesting case came up in the forums this week regarding the use of ComboBoxes as editors inside DataGrid cells, and whether they can be used to select a particular object by ID in the background while displaying a corresponding Description property on the object instead of the ID. This could of course be implemented with a DataTemplate containing a ComboBox with converters to display the appropriate description, but this approach would get messy as you’d have to add a unique template and converters for each column which needed a ComboBox. Wouldn’t it be much more elegant if you could create just one template but parameterise it so you could pass in different options for each column that used it?

Well, you can! The PropertyEditor class supports an EditContext property which you can set up on a per-property basis, and the editor DataTemplate can bind to this EditContext and customize the template based on its state. I’ll go into detail on the above case in the second part of this post, but first off let’s look at a simpler use case where the EditContext can be used to clean up your XAML, but which is a bit easier for demo purposes!

Imagine a case where we have a model with multiple integer properties representing specifications of some object, for instance personal computers. We’d like to display these in a DataGrid, and it’d be great if the user could change the value in a cell with a Slider (or UpDown control, etc). As the various properties have differing sensible ranges they would normally need their own template containing a unique Slider with its Minimum/Maximum/SmallChange set appropriately.

Instead of creating three separate templates we can set the EditContext of a PropertyEditor, and then bind to this from the template. Firstly, we’ll need a class to hold the values we need to vary between instances of the template. For our numeric editing example this is very simple:

public class ComputerEditInfo
{
  public int Maximum { get; set; }
  public int Minimum { get; set; }
  public int SmallChange { get; set; }
}

Then back in the XAML inside the DataGrid declaration, open a PropertyEditor tag and set its EditContext to be an instance of the above class. Here’s two out of three required:

<ms:DataGrid.Editors>
  <ms:PropertyEditor PropertyName="Ram"
                     EditorTemplate="{StaticResource NumericSliderCellTemplate}" >
    <ms:PropertyEditor.EditContext>
      <local:ComputerEditInfo Minimum="1" Maximum="8" SmallChange="1" />
    </ms:PropertyEditor.EditContext>
  </ms:PropertyEditor>
 
  <ms:PropertyEditor PropertyName="HardDrive"
                     EditorTemplate="{StaticResource NumericSliderCellTemplate}">
    <ms:PropertyEditor.EditContext>
      <local:ComputerEditInfo Minimum="120" Maximum="1024" SmallChange="120" />
    </ms:PropertyEditor.EditContext>                    
  </ms:PropertyEditor>
</ms:DataGrid.Editors>

Notice that we have to use the Editors collection instead of the Columns collection.

Now we can create our reusable DataTemplate. There are a couple of important things to be aware of when creating a reusable DataTemplate for use with a PropertyEditor.

First, because the template is going to be used for multiple properties, you can’t bind to the property by the actual property name on the originating object. Instead, the PropertyEditor plumbing presents the value being edited as Value, and that’s what you have to bind to in the editing control(s).

Second, the PropertyEditor plumbing also supplies an EditContext property that you can bind to. For any given instance of the editor template, this will contain the value of the EditContext in the PropertyEditor declaration that applies to the current instance.

Here’s the editor template for the numeric properties of the computer object:

<DataTemplate x:Key="NumericSliderCellTemplate">
  <Grid VerticalAlignment="Center" >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding EditContext.Minimum}"
               Margin="2,0,0,0"
               Foreground="#3D3D3D" />
    <Slider Value="{Binding Value, Mode=TwoWay}"
            Minimum="{Binding EditContext.Minimum}"
            Maximum="{Binding EditContext.Maximum}"
            SmallChange="{Binding EditContext.SmallChange}"
            Margin="2,0,2,0"
            Grid.Column="1"/>
    <TextBlock Text="{Binding EditContext.Maximum}"
               Grid.Column="2"
               Margin="0,0,4,0"
               Foreground="#3D3D3D" />
  </Grid>
</DataTemplate>

Notice how the Slider and the TextBlocks bind to the EditContext to implement and display the correct per-property settings for minimum and maximum values and change size. One template for multiple columns, each with their values correctly set – that’s pretty clean! And here’s the output, where after the cell is double-clicked into edit mode the Slider appears set with the minimum and maximum.

This of course can be extended for passing in any sort of data that can be reused with adjustments across columns, and next time we’ll look at a detailed implementation of the ComboBox case, where its DisplayMemberPath and SelectedValuePath will update based on the various PropertyEditors set for the columns. Check out our site for more information on the DataGrid and the 50+ other controls included in WPF Elements 5. Until then!

Nightly news, 3 February 2012

It’s the end of another week here at Mindscape HQ and time as usual to check in on what we’ve shipped in the last seven days. Here’s what’s new in the latest nightly builds.

WPF Elements

  • Added handling for race conditions in DataGrid.BringCellIntoView and DataGrid.GetCell
  • Fixed a bug in DataGrid.RemoveInputBinding
  • Added DataGridColumn.AllowGrouping property, which allows you to specify that the user cannot group on that column
  • You can now template DataGrid group row headers

Web Workbench

  • Highlighting for Less pattern matching and guards
  • Fix for file generation error when using checkout-based source control system
  • Updated Less compiler to 1.2.1
  • Improved positioning of opening braces during Format Document
  • Fix for error with unbounded node (e.g. unclosed quote) in CoffeeScript

LightSpeed

  • Apply COLLATE NOCASE when creating SQLite GUID primary keys
  • Fixes relating to one-way associations when the originating side is a concrete table inheritance base class
  • Fixed migrations error with abstract classes

WPF Diagrams

  • Tweak to the ElbowPathfinder

As always the free editions are available on the downloads page and you can get the full editions from the store. Happy coding!

Nightly news, 27 January 2012

The big news in this week’s nightly builds is the new hotness in the WPF Elements DataGrid. Grouping, exporting, new APIs and, well, grouping again because it’s so neat — check it out! Of course, we’ve got a bunch of other updates for you as well. Here’s what’s new this week.

LightSpeed

  • Exclude null associations from polymorphic association validation
  • Fix for not being able to return array values from ICompiledParameterValues to populate an IN clause
  • Fix for LINQ projection that included the result of a Count on a join-into result when there was no Where clause
  • When updating a SQLite database with a GUID field, set COLLATE NOCASE on the column (because it is mapped as a string rather than a native GUID type)
  • Added support for per-table identity block size (for when new tables use KeyTable but the database also contains legacy tables which are also updated by other apps using increment-by-1 sequences)

WPF Diagrams

  • DiagramConnectionPointBase.OnPositionChanged is now exposed to user derived classes
  • Fixed a bug in the ConnectionPointRelocation sample

WPF Elements

  • DataGrid hotness!
  • Fixed a bug in TimeExplorer

As usual, you can get the updated builds of free versions from the downloads page, and of full versions from the store.

Fresh WPF Elements 5 Features – DataGrid Grouping and Exporting!

tag icon Tagged as WPF Elements

One of the great things about having a fast release cycle is being able to deliver cool new functionality as soon as it’s ready – no waiting for the next quarterly release for a crucial feature that could be released today. In the buildup to WPF Elements 5.1 we’ve been developing some great additions to the existing controls in the suite, as well as going through the codebase with a fine-toothed comb to ensure it remains highly stable and bug-free. We’re not cruel enough to make you wait for these improvements, so you can grab them from the nightly builds right now – both feature additions and bugfixes!

There are many small improvements to the API, such as being able to scroll a particular DataGrid cell into view, as well as two significant additions to the DataGrid we’re sure you’ll find very handy – Grouping and Exporting support. For the rest of the post I’ll be discussing how you can use these two features. First up – grouping!

Grouping

Users can now group data by any number of columns through an intuitive drag-and-drop interface – just drag a column header onto the grouping panel above the grid. Groups can be expanded and collapsed as expected, and the column grouping can be be removed by clicking on a button that appears on mouseover. Any number of columns can be added to the panel and the DataGrid will group the data in a nested fashion. Check out the demo below:

DataGrid animated grouping demo

Just set the IsGroupingPanelVisible property to True to enable the grouping UI. Alternatively, if you want to provide your own UI for grouping — or no UI at all, for example if you have an application where the data should always appear grouped — you can group on columns programatically by adding them to the GroupedColumns collection:

MyDataGrid.GroupedColumns.Add(MyDataGrid.EffectiveColumns[0]);

Easy as that. Our DataGrid performance is speedy even with millions of rows, and it stays that way even if the DataGrid has a complex nesting hierarchy.

Exporting

Another handy new feature is the ability to export the data in a DataGrid into an appropriate file format. At the moment we support the near-universal CSV format with more to come, and you can export to file, string or TextWriter. Again, it’s dead easy to use.

DataGridExporter.Csv.WriteFile(MyDataGrid, file);

In combination with a SaveFileDialog, you can export your DataGrid:

DataGrid exporting

and have the resulting file be opened by Excel, complete with columns automatically generated by the DataGrid from your collections.

That saves time! These two features along with bugfixes are available in the nightly builds right now, but they’ll officially be available in the WPF Elements 5.1 soon. You can check out the 60 day trial of WPF Elements 5 here featuring the DataGrid control plus more than 50 others.

DataGrid customization with Elements 5: Sparklines, template selectors and more

tag icon Tagged as WPF Elements

Following on from earlier posts about our new DataGrid control in WPF Elements 5, today I’ll be demonstrating some deeper customization options by creating a financial data display demo. By making a Chart control with an embedded LineSeries and setting a couple of properties we can create a great looking Sparkline graph, which can then be used as a template for a DataGrid column consisting of those graphs. All of this is included in WPF Elements 5, and you can grab the 60 day fully featured trial here to get started. I’ll then look at creating a template selector to apply templates to cells depending on their data, and the demo will also use a bit of custom logic to highlight the largest and smallest values in the collections. The final result will end up like this:

To begin, declare a new DataGrid control in a XAML page and bind its ItemsSource property to a collection in a similar manner to Working with the Data Grid – Day 1. If we are to add charts to this DataGrid its model will need a property of a type that contains an ObservableCollection of DateTimeDouble objects, which the LineGraph will use to draw itself. Here’s an archive with example data structures illustrating the required behavior (which generate random points suitable for the purposes of this demo): DataModel.

Setting up the sparkline charts

Next, in the ResourceDictionary add the following DataTemplates containing the formatted charts. The Chart control in WPF Elements 5 has heaps of customization options, and with a few simple tweaks the LineSeries can display a great looking Sparkline chart. Here’s one possible look for a Sparkline template:

<DataTemplate x:Key="BidChartCell">
  <ms:Chart LegendPosition="None" TitleTemplate="{x:Null}" Padding="3">
    <ms:LineSeries ItemsSource="{Binding BidTrend.Data1}" AlwaysShowYAxisZero="False" YAxisDataBuffer="1000" />
    <ms:Chart.XAxis >
      <ms:ChartAxis Visibility="Collapsed" />
    </ms:Chart.XAxis>
    <ms:Chart.YAxis>
      <ms:ChartAxis Visibility="Collapsed" />
    </ms:Chart.YAxis>
  </ms:Chart>
</DataTemplate>

The chart declared in that DataTemplate is going to look rather unnatural in any place larger than a DataGrid cell, so let’s apply it to a column in our DataGrid by setting its DisplayTemplate property:

<ms:DataGrid ItemsSource="{Binding Data}" AllowEditing="False">
  <ms:DataGrid.Columns>
    <ms:DataGridColumn PropertyName="BidTrend"
                       DisplayTemplate="{StaticResource BidChartCell}"
                       Header="Bid Trend"/>
  </ms:DataGrid.Columns>
</ms:DataGrid>

Pretty easy! If you were to increase the rows in the model you’d notice the performance is speedy even with dozens of charts on screen, and stays that way during resizing, dynamic data updating and similar tasks.

Showing additional data with template selectors

Now how about formatting the Change columns to show an image based on the data in each cell? One solution would be to create a custom class that derives from DataTemplateSelector and overrides SelectTemplate(). Here’s a snippet of the implementation with logic that returns a template based on whether the cell’s Double is positive or negative:

public class ChangeDataTemplateSelector : DataTemplateSelector
{
  public override DataTemplate SelectTemplate(object item, DependencyObject container)
  {
    FrameworkElement element = container as FrameworkElement;
 
    if (element != null && item != null)
    {
      Entry entryItem = item as Entry;
      if (ChangeType == CellChangeType.Bid)
      {
        if (entryItem.BidChange < 0)
        {
          return NegativeChangeTemplate;
        }
        else if (entryItem.BidChange > 0)
        {
          return PositiveChangeTemplate;
        }
      }
      else
      {
        if (entryItem.AskChange < 0)
        {
          return NegativeChangeTemplate;
        }
        else if (entryItem.AskChange > 0)
        {
          return PositiveChangeTemplate;
        }
      }
    }
    return NoChangeTemplate;
  }
 
  public DataTemplate PositiveChangeTemplate { get; set; }
}

The full code with the other two properties and the enum is contained in the archive at the bottom of this post. Back in the XAML, add the DataTemplates that are to be applied to the cell to the ResourceDictionary (here’s one of four required):

<DataTemplate x:Key="PositiveBidChangeTemplate">
  <Grid HorizontalAlignment="Center" x:Name="PositiveBidCell">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="35" />
    <ColumnDefinition Width="20" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding BidChange}" HorizontalAlignment="Right"
               VerticalAlignment="Center" Margin="0,0,6,0" />
    <Image Source="../Images/up.png" Width="16" Height="16" Grid.Column="1" />
  </Grid>
</DataTemplate>

We can now declare an instance of our DataTemplateSelector and set its properties to our DataTemplates. The Bid selector will look like this:

<local:ChangeDataTemplateSelector x:Key="BidChangeSelector"
                                  PositiveChangeTemplate="{StaticResource PositiveBidChangeTemplate}"
                                  NegativeChangeTemplate="{StaticResource NegativeBidChangeTemplate}"
                                  ChangeType="Bid"
                                  NoChangeTemplate="{StaticResource NoChangeBidTemplate}" />

Now that has been set up, it’s a simple matter of setting the DisplayTemplateSelector property on the appropriate DataGridColumn that you wish to apply the templates to. We may as well make the column’s title look a bit nicer at the same time.

<ms:DataGridColumn PropertyName="BidChange" Header="Bid Change"
                   DisplayTemplateSelector="{StaticResource BidChangeSelector}" />

That will get the Change and Trend columns looking like the screenshot at the top of this post. In a future post I’ll show how to extend the model to record the highest and lowest data, and use XAML triggers to format cells based upon that information. In the meantime, check out the archive below for the full code from this post.

DataGrid with sparklines and template selectors

And don’t forget to download the 60-day trial of WPF Elements and try out your own designs!

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