Hello world! – Once again…

Unfortunately I had an issue with my previous hosting in that my blog just got deleted and lost a series of comments and posts which I will try to recover and re-post them here – for now I did manage to restore info I had from my latest backup.

It’s weird to revive this blog… but certainly good to be back! Tons of memories come to mind, on writing the posts, comments and samples I have shared with some of you back then. I had forgotten how good it actually feels…

It is in fact amazing how much has changed since my last post back in August 2010 and how life itself has changed on me! From the C# and WPF focused me of the past you will likely be seeing me  blogging more on managing Software Development than actual coding – of course one never knows, once you are in love with programming there is little you can do to avoid betting back in the code, even if for a little while only! It is what it is… once you are Inspired by Software!

Stay tuned, let’s have some fun together! 😉

RhinoMocks Stub not retaining value when property shadowed on inheriting interface

I cannot remember having a Sprint like this! First the issue with debugging in VS2008 on Windows7 x64, then the issue with BadImageFormatException throwing when inheritance constraints were applied to a generic method, and now this one!

 

The Problem

 

I was still fixing the tests that had failed after upgrading to Castle.Windsor 2.1 and RhinoMocks 3.6 when I stumble on this one. Basically I had a unit test that passed before I upgraded and as soon as I switched to using the most recent versions of both libraries the test failed.

The issue manifested on a stub for an Interface that inherited from another interface re-implementing a property member with the keyword new (not a recommended approach but necessary because we were handling legacy code). Here is an example of how our interfaces are designed:

public interface INamedObject
{
   string DisplayName {get;set;}
}

public interface IFeature : INamedObject
{
   new string DisplayName {get;set;}
}

There is a base interface that implements a property which is being shadowed by the inheriting interface.

At some point in our unit test we need to access DisplayName by its base interface INamedObject. Something like the following:

 [TestMethod] 
 public void TestMethodThatFails() 
 { 
     var feature = MockRepository.GenerateStub<IFeature>(); 
     feature.DisplayName = "some name";

     Assert.AreEqual("some name", feature.DisplayName); 
     Assert.AreEqual("some name", ((INamedObject)feature).DisplayName); 
 }

It seems that in the previous Castle.DynamicProxy implementation the proxy created for such interface would result in DisplayName being available on both base interface and inheriting interface. With the new version, when we try accessing DisplayName after casting the object to INamedObject we get a null value. The only way to access it is through the inheriting interface.

It is my understanding that this is not the expected behavior for a stub. I would expect it to retain the value as a regular class does when it implements IFeature.

 

A workaround

 

In the end I had to explicitly set DisplayName value on feature by previously casting it to INamedObject, likewise:

 [TestMethod] 
 public void TestMethodThatPasses() 
 { 
     var feature = MockRepository.GenerateStub<IFeature>(); 
     feature.DisplayName = "some name"; 
     ((INamedObject)feature).DisplayName = "some name";

     Assert.AreEqual("some name", feature.DisplayName);
     Assert.AreEqual("some name", ((INamedObject)feature).DisplayName);
 }

It is true that we need to keep up with the evolution of libraries we consume in our projects, but has more tests are created the price we have to pay for this inconsistencies between versions of the same library starts to raise. It is always up to us to assure that the effort is worth it, and in this case, now that I have migrated all the tests, it was not that bad after all. 🙂

Shout it kick it on DotNetKicks.com

BadImageFormatException when debugging interface mocked with RhinoMocks

My last couple of days has been quite hilarious. First there was the issue with not being able to set breakpoints on my tests, and then started having tests that failed one way when executed normally and a different way when I ran them with debugger attached!

 

The Problem

 

Recently I updated the project I am working on to Castle.Windsor version 2.1. Since we are using an abstraction over the container (for resolving purposes only), we had to adapt it to conform with the new restrictions for Resolving dependencies, thus:

public interface IFaroContainer 
{ 
    object Resolve(Type type);  
    object TryResolve(Type type);  
    IEnumerable ResolveAll(Type type);  
    void RegisterTypeIfMissing<TI, T>(bool registerAsSingleton) 
where T : class, TI;
object BuildUp(Type type); }

With the new Castle.Windsor version we have to enforce that our T class implements TI, which makes perfect sense and helps reducing erroneous registration issues that one could perform.

The problem is that when we try to Mock this interface (I am currently using RhinoMocks v3.6) while in debug mode we get a BadImageFormatException:

The problem is that there is a bug in the System.Reflection.Emit

implementation which cause the runtime to emit invalid code for this

scenario.

You can read more here.

 

How I fixed my issue

 

Since I am depending on third party lib Castle.Windsor, I cannot change the way the fluent interface is designed. It seems though that this constraint is enforced only by Castle.Windsor’s fluent interface, if you use standard component registration you will be able to bypass that argument check thus not unleashing the Emit bug.

I just changed my registrations from using the fluent interface to use the traditional AddComponent and the tests started passing!

Shout it kick it on DotNetKicks.com

Why breakpoints do not get hit when targeting x86 on Windows 7 x64?

This is a very particular scenario. It happened to me and cost me over 3 hours to fix it.

 

Background

I am currently working on a fully managed WPF application, that references a certified C# DLL that was purposely compiled targeting x86 platform (part of the certification process I think). Since I recently got an upgrade on my development machine, and I wanted to take full advantage of my 8GB of RAM I ended up installing Windows 7 x64.

Because of the particular x86 certified DLL, I had to force my application to run in a 32bit process, thus, I changed my application build target to x86. This was about one week ago.

Since I am in the middle of a code cleanup and refactoring work item, I did not had the need to debug my source up until three days after I changed that setting, thus moving if to a back drawer on my brain.

Now I get to a point where I actually need a breakpoint, set it on my code, and run my application. The breakpoint does not get it! I then move the breakpoint to the first line of code on my App.xaml.cs constructor. It does not get hit either. Then I rebuild my project, cleanup my bin and obj folders (something could be inconsistent with those files).

 

Got it working

After spending a couple of hours on this, even getting older change sets to see if the issue was a problem with the source code, I re-opened that back drawer and remembered to try to set the target CPU back to any (it would only crash when the actual certified DLL was loaded).

The breakpoint finally got hit!

 

The solution

Obviously this did not make any sense. Since I had elaborated on the issue I was now able to successfully search for what I needed: “vs2008 breakpoint x64 target x86” and this came out as a result:

Debugging x86 application on Windows 7 x64

What happens is that when you install Visual Studio 2008 on Windows 7, the OS will set the compatibility mode to “Windows XP SP3”. All you have to do is to change this setting to “Windows Vista SP2” and your problem will be fixed. :S

Shout it kick it on DotNetKicks.com

Why does the DependencyProperty I set using a DataTrigger does not get its value?

This is a common mistake when developing WPF applications. It has happened to me countless times and I have seen people asking questions about it many times.

 

The Problem

I have a Style which defines a DataTrigger to change some property on a control based on a bounded value from the ViewModel. Let’s take a look at this example view XAML (I have removed irrelevant source code):

(...)
<Style x:Key="{x:Type Border}" TargetType="{x:Type Border}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding BuildStatus}" Value="Ok">
            <Setter Property="Background" Value="Green"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding BuildStatus}" Value="Broken">
            <Setter Property="Background" Value="Red"/>
        </DataTrigger>
    </Style.Triggers>
</Style>
(...)
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
        Width="64" Height="64" Background="Transparent"
        BorderBrush="Black" BorderThickness="1,1,1,1" />
(...)

So we have a DataTrigger that changes the Background color of a Border accordingly to a BuildStatus property on our ViewModel. We do not want any color up until the ViewModel actually gets the build status, so we set the Border’s Background to Transparent. We then add a Button that can be used to toggle between two possible build status (ok and broken).

After we tie our View and its corresponding ViewModel displaying it on a window this is how our application looks when it is launched:

clip_image002

If you click on the change color Button you will see that, although we have specified our DataTriggers to change the color upon changes made to the build status nothing will happen to the build status indicator. Why is this happening?

 

The solution

The problem is that by setting the transparent color directly on the Border tag corresponding to the initial state color you override any value that may be set from the Style. So, in order for DataTrigger changed properties actually get set on your control you have to specify the default values on the Style through a property setter and not on the actual control instantiation:

(...)
<Style x:Key="{x:Type Border}" TargetType="{x:Type Border}">
    <Setter Property="Background" Value="Transparent"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding BuildStatus}" Value="Ok">
            <Setter Property="Background" Value="Green"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding BuildStatus}" Value="Broken">
            <Setter Property="Background" Value="Red"/>
        </DataTrigger>
    </Style.Triggers>
</Style>
(...)
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
        Width="64" Height="64"
        BorderBrush="Black" BorderThickness="1,1,1,1" />
(...)

I have bolded the changes that had to be done in order to fix this issue.

You can download a working copy of this sample by following this link.

Shout it kick it on DotNetKicks.com

Using FileSystemWatcher to synchronize ComboBox options

In this post I will present you with my latest addition to my personal user experience arsenal: direct file system synchronization. You will be shown how easily it is for you to setup a FileSystemWatcher to observe file changes on disc, astonishing your user when he least expects.

 

Download sample source code

You can download a running sample for this blog post by following this link.

 

Introduction

Suppose you have a report templates folder under your application documents folder containing a series of report formats that you want your user to be able to add, edit or remove through the file system.

FileSystemWatcher-ss01

In that folder you store Report1.rpt, Report2.rpt and Report3.rpt. Your application has a dialog that enables the user to select which report template he should use to print data. He is running the application and notices that the template he wants to use is not on the application’s template folder. He goes to the email that his boss had sent him, and gets the NewReport.rpt file. He copies the file to your Software’s template folder.

 

The Problem

At this point the user has the report on his folder but the combo box on the UI he is using has not update. There is no way for him to select the new template without reopening the UI, or maybe the application (if you are caching the report templates list). This may force him to lose some work.

A normal approach in this scenario is to place a refresh button in the UI that may be used to refresh the templates list. Although this fixes the problem, it forces the user to an additional step.

 

Let FileSystemWatcher do its magic

A much clear solution is to use the FileSystemWatcher class that is part of System.IO and it has lived there for a long time now! You would create a method that updates the report templates from disk. Then create a new FileSystemWatcher that monitors *.rpt files that change on the templates folder.

var watcher = new FileSystemWatcher(TemplatesPath, TemplatesSearchPattern);
watcher.Created += (fileSystemWatcher_FilesChanged);
watcher.Changed += (fileSystemWatcher_FilesChanged);
watcher.Deleted += (fileSystemWatcher_FilesChanged);
watcher.Renamed += (fileSystemWatcher_FilesChanged);
watcher.EnableRaisingEvents = true;

Has you can see you can monitor specific events only. In this case we want to monitor every change that might occur in our report templates folder that respects to rpt files. Now whenever a change is made on the file system the UpdateAvailableTemplates method will be called and we can synchronize the report templates list.

The only additional catch is to guarantee that UpdateAvailableTemplates runs on the thread it is supposed to run. We do this by saving the SynchronizationContext in which our FileSystemWatcher is created.

this.syncContext = SynchronizationContext.Current;

Then when a change is detected we post the UpdateAvailableTemplates to the context we saved on the constructor:

private void fileSystemWatcher_FilesChanged(object sender, FileSystemEventArgs e)
{
    this.syncContext.Post(UpdateAvailableTemplates, null);
}

To test your application go ahead and delete, rename, create some files and see how your application keeps on updating the list it displays. Notice that it is instantaneous.

You can download a running sample for this blog post by following this link.

 

Conclusion

Obviously that the templates list will not change that often, you may be wondering if this extra mile is worth the effort. Either way you will have to implement the UpdateAvailableTemplates logic. At the cost of these extra lines of code you can add that extra touch of excellence to your user’s experience.

Most of your users will not require this feature but when they actually need it, it will probably save them a lot of effort. For the effort it takes to implement such thing, if you save 1 costumer the endeavor of filling that form again it will be worth it.

When I added this to the software I am currently working on, I had a few WOW’s, when people saw that the UI just worked. After several deletes, adds, edits, etc, on the files the software updated in real time!

Shout it kick it on DotNetKicks.com

Playing Storyboards on DataTriggers to animate a Path

In the application I am working on, we had to display an image accordingly to the value of a field in our ViewModel represented by an Enum. The Enum had 3 possible values: normal, up or down. An arrow pointing up, right or down was to be drawn as a result of the bounded field’s value.WpfAnim-ss001

 

Download Sample Source code

You can download the sample source code for this blog post by following this link.

 

Initial sample setup

The plan was to draw an arrow using a Path. Then use animations to rotate the arrow accordingly to what was set on the ViewModel.

I went ahead and created a sample application that draws all this stuff on the UI. It also adds three buttons at the bottom which allow us to manipulate the value on the bounded field to control the tendency that should be displayed:

WpfAnim-ss002

As mentioned before, a Path was used to create the arrow. For us to be able to animate the arrow’s rotation we had to create a render transform with the initial value to animate from:

<Path x:Name="path"
      Fill="#FFCC0707" Stretch="Fill" Stroke="#FF000000"
      Height="32" Width="64" RenderTransformOrigin="0.5,0.5"
      Data="M78,88.333333 L141.5,87.500336 142.16667,65.833664 175.83283,96.833545 143.49989,127.16709 142.16656,109.16693 78.166666,108.16694 z">
    <Path.RenderTransform>
        <TransformGroup>
            <ScaleTransform ScaleX="1" ScaleY="1"/>
            <SkewTransform AngleX="0" AngleY="0"/>
            <RotateTransform Angle="0"/>
            <TranslateTransform X="0" Y="0"/>
        </TransformGroup>
    </Path.RenderTransform>
</Path>

Next I added the three Storyboards: OnTendencyUp, OnTendencyNormal and OnTendencyDown. Each storyboard animates the rotation of the arrow to its corresponding Enum value.

<Storyboard x:Key="OnTendencyUp">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                                   Storyboard.Target="{Binding TemplatedParent}" 
                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="-45"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnTendencyNormal">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                                   Storyboard.Target="{Binding TemplatedParent}" 
                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnTendencyDown">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                                   Storyboard.Target="{Binding TemplatedParent}"
                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="45"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

Everything is now in place for us to start playing with the animation. We have a path and three animations that change its rotation.

 

Controlling Storyboards with DataTriggers based on the Enum value

The next step was to create the DataTriggers. The plan was that whenever the tendency changes we kick off the consequent storyboard. For this we would use DataTrigger.EnterActions to initiate the storyboard. The DataTriggers should be added to the style of the Path:

<Path.Style>
    <Style>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Tendency}" Value="Down">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource OnTendencyDown}"  x:Name="OnTendencyDown_BeginStoryboard"/>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding Tendency}" Value="Up">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource OnTendencyUp}" x:Name="OnTendencyUp_BeginStoryboard"/>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding Tendency}" Value="Normal">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource OnTendencyNormal}" x:Name="OnTendencyNormal_BeginStoryboard"/>
                </DataTrigger.EnterActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Path.Style>

In theory, whenever a value changes the DataTrigger fires the storyboard thus animating the rotation either way. The strange thing is that no animation is played, none whatsoever, zip, rien, niente, nenhuma! This happens because we have competing DataTriggers over the same binding making the last one the winner! (For additional information check this link)

If you try and change the order by which the DataTriggers are displayed you will notice that a single animation will be played (the last one on the list), and when the rotation gets to its target value no other animation will play.

 

Controlling the Storyboards through Buttons and EventTriggers

Now if you trigger the animations via normal event triggers all animations will play appropriately. I went ahead and produced a sample that fires the storyboards based on the clicking event on each button:

<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnDown">
    <BeginStoryboard Storyboard="{StaticResource OnTendencyDown}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnNormal">
    <BeginStoryboard Storyboard="{StaticResource OnTendencyNormal}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnUp">
    <BeginStoryboard Storyboard="{StaticResource OnTendencyUp}"/>
</EventTrigger>

In order for the animations to play correctly we have to change the bindings on the storyboards that lead to the rotation of the Path. Instead of using TemplatedParent we will use a binding to a specific element. For each storyboard I changed the binding to:

(…)
Storyboard.Target="{Binding ElementName=path}"
(…)

Now, all the animations play appropriately every time you click one of the buttons.

 

How to enable controlling the Storyboards with DataTriggers

The solution to the problem with the DataTriggers is for us to remove the Storyboard on each DataTrigger whenever DataTrigger.ExitActions is invoked, likewise:

<DataTrigger Binding="{Binding Tendency}" Value="Down">
    <DataTrigger.EnterActions>
        <BeginStoryboard Storyboard="{StaticResource OnTendencyDown}"
                         x:Name="OnTendencyDown_BeginStoryboard"/>
    </DataTrigger.EnterActions>
    <DataTrigger.ExitActions>
        <RemoveStoryboard BeginStoryboardName="OnTendencyDown_BeginStoryboard" />
    </DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Tendency}" Value="Up">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource OnTendencyUp}"
x:Name="OnTendencyUp_BeginStoryboard"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="OnTendencyUp_BeginStoryboard" />
</DataTrigger.ExitActions>
</DataTrigger> <DataTrigger Binding="{Binding Tendency}" Value="Normal"> <DataTrigger.EnterActions> <BeginStoryboard Storyboard="{StaticResource OnTendencyNormal}" x:Name="OnTendencyNormal_BeginStoryboard"/> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <RemoveStoryboard BeginStoryboardName="OnTendencyNormal_BeginStoryboard" /> </DataTrigger.ExitActions> </DataTrigger>

By removing the Storyboards on ExitActions we guarantee that animations will play correctly every time the Storyboard is initiated.

 

Conclusion

This solution feels a bit hacky but the truth is that it fixed our issue allowing our animations to play smoothly!

You can download a sample application for this post by following this link.

 

Updates

2009/07/10: I have updated the order by which the DataTriggers are defined on the ‘How to enable controlling the Storyboards with DataTriggers’. The sample source code was updated as well. An issue remains however: when you click up and then click down there will be a jump in the animation from the up to normal and then animate to down. I am still trying to figure this one out…

Thank you to Carlos for spotting this glitch!

 

Shout it kick it on DotNetKicks.com

WPF Toolkit DataGrid Part IV: TemplateColumns and Row Grouping

This post is part of the WPF Toolkit DataGrid series. Here is a list with the complete set of blog posts:

Introduction

In Part III we got to know a bit better how stuff gets rendered on the WPF DataGrid. We have gone through several samples on how to configure the way data gets displayed on each column by either creating new column types or by styling existing ones.

In Part IV we will have a look into more advanced concepts. First we will see how we can use the DataGridTemplateColumn to imitate the behavior of a DataGridComboBoxColumn and to show a small chart representing the age deviation from average for each player. We will then have a look on how we can group rows on our DataGrid using two separate methodologies.

 

Roadmap

  • Creating a ComboBox column from a TemplateColumn
  • Using DataGridTemplateColumns to create a chart column
  • Grouping rows using ItemsControl.GroupStyle
  • Fake grouping with the help of the ViewModel

Download sample source code

Here is a list with the samples presented on this blog post:

Creating a ComboBox column from a TemplateColumn

The ItemsSource property of DataGridComboBox column does not allow for you to directly bind to data on your DataContext, at least for now (WPF Toolkit – March 2009 release). It is likely that in a future release binding support will be added (if you want further information on this checkout this CodePlex workitem).

There are a lot of ways in which you could implement this! For instance, you could extend DataGridComboBoxColumn adding binding support on it, you could add the binding support directly to DataGridComboBoxColumn recompiling WPF Toolkit, etc.

The solution I came up with in my application was a simpler one, IMHO. I restyled a DataGridTemplateColumn to behave like a DataGridComboBox column.

This is very simple to accomplish. The only issue I found was that the effort required to actually finding the property to bind to from within the created template – I had to use a tricky binding expression.

You must start by adding to your view model the data you are going to bind to:

(…)
public IList<string> Categories { get; set; }
(…)
public TeamModel()
{
    Categories = new List<string> 
        { "Mini", "Cadet", "Junior", "Senior", "Veteran" };
}
(…)

In order for you to use a DataGridTemplateColumn you must create two specific templates, one for the cell in its default view and one for when it is in editing mode. In the default view of our cell only text will be displayed. You can just add a new template that uses a label to render the selected text:

<DataTemplate x:Key="ComboBoxCellDataTemplate">
    <Label x:Name="lblCombo" Content="{Binding Category}" 
           Style="{StaticResource BaseLabelCellStyle}" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Categories}" Value="Both">
            <Setter TargetName="lblCombo" Property="IsEnabled" Value="False" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

As you may have noticed you had to use RelativeSource to find the properties to bind to. Now you have to create the editing mode template that uses a ComboBox and lets the users select which category he wants to set a player in.

<DataTemplate x:Key="ComboBoxCellEditingTemplate">
    <ComboBox x:Name="comboBox"
              ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Categories}"
              SelectedItem="{Binding Category}"/>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Enabled}" Value="False">
            <Setter TargetName="comboBox" Property="IsEnabled" Value="False" />
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Categories}" Value="Both">
            <Setter TargetName="comboBox" Property="IsEnabled" Value="False" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

After adding these two DataTemplates to the resources you can change the definition of the Category column to use the DataGridTemplateColumn with these two specific templates.

<WpfToolkit:DataGridTemplateColumn
    Header="Category" Width="1*"
    CellTemplate="{StaticResource ComboBoxCellDataTemplate}"
    CellEditingTemplate="{StaticResource ComboBoxCellEditingTemplate}"/>

It could not get simpler than this! You have just made a ComboBoxColumn out of a DataGridTemplateColumn.

This is how our sample application is looking after these changes:

WpfToolkitDataGrid-ss015a

You can download a working sample of this code by following this link.

 

Using DataGridTemplateColumns to create a chart column

Each player shown in the DataGrid has an age associated with him. For each player we will be presenting a chart column picturing the deviation of his age towards the team average.

All calculations regarding the team age average and each players deviation should be done in the backend. Basically you will have to change the code behind file for TeamView since there is where we setup the view model for the view (this is definitely a bad practice – it is being done this way for simplicity). In the DeviationChart column we will expose the DeviationPercentage previously calculated. This value will be used to calculate the width of the bars to draw our chart with.

Essentially this is the code used to calculate these values:

(…)
SetupTeamAgeAverage(playerAges.Where(player => player.Parent == alphaTeam));

(…)

private static void SetupTeamAgeAverage(IEnumerable<IPlayerAge> teamPlayers)
{
    double teamAgeAverage = teamPlayers.Average(x => x.Age);
    foreach (var player in teamPlayers)
    {
        player.Deviation = player.Age - teamAgeAverage;
        player.DeviationPercentage = 
            (player.Age - teamAgeAverage) / teamAgeAverage;
    }
}
(…)

Now that the data is processed you need to create a new element style to apply on the DataGridTemplateColumn that picks this value and transforms it to a chart.

Deviation percentage goes from -1 to 1. If it is negative we will create a bar on the left of the chart, otherwise a bar will be added on the right. The length of the bar will be proportional to the amount of deviation. All this will be done using standard grids, background colors and star notation on grid column widths.

Since we do not have a direct way of converting a value into a percentage of width we will be doing so in a hacky way by using an IValueConverter (you can see the actual implementation of the converter by downloading the sample code).

After creating the converter you will have to add this new style to the resources of your view:

<Style x:Key="BarChartCellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                <Grid Background="{TemplateBinding Background}">
                    <Grid x:Name="BarChart" Background="LightBlue" 
                          Height="14"  Margin="2,0,2,0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition x:Name="lowerToleranceColumn"
                                              Width="*"/>
                            <ColumnDefinition Width="1"/>
                            <ColumnDefinition x:Name="upperToleranceColumn"
                                              Width="*"/>
                        </Grid.ColumnDefinitions>

                        <Grid Margin="2,0,1,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding DeviationPercentage, 
                                    Converter={StaticResource percentageWidthConverter}, 
                                    ConverterParameter=1}"/>
                                <ColumnDefinition Width="{Binding DeviationPercentage,
                                    Converter={StaticResource percentageWidthConverter},
                                    ConverterParameter=2}"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="LowerBarChart" Grid.Column="1" 
                                    Height="10" Background="MediumBlue">
                                <TextBlock x:Name="LowerToleranceLabel" 
                                           Text="&lt;" Foreground="White"
                                           HorizontalAlignment="Left" VerticalAlignment="Center" 
                                           Margin="2,-2,0,0"/>
                            </Border>
                        </Grid>

                        <Border Grid.Column="1" Width="1" Background="Gray" />

                        <Grid Grid.Column="2" Margin="1,0,2,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding DeviationPercentage, 
                                    Converter={StaticResource percentageWidthConverter}, 
                                    ConverterParameter=3}"/>
                                <ColumnDefinition Width="{Binding DeviationPercentage,
                                    Converter={StaticResource percentageWidthConverter},
                                    ConverterParameter=4}"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="UpperBarChart" Height="10" Background="DarkOrange">
                                <TextBlock x:Name="UpperToleranceLabel" 
                                           Text="&gt;" Foreground="White" 
                                           HorizontalAlignment="Right" VerticalAlignment="Center"
                                           Margin="0,-2,2,0"/>
                            </Border>
                        </Grid>

                        <Border BorderBrush="White" BorderThickness="1,1,0,0" Grid.ColumnSpan="3"/>
                        <Border BorderBrush="LightGray" BorderThickness="0,0,1,1" Grid.ColumnSpan="3"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now that you have a new style on your view’s resource dictionary you can go ahead and set the corresponding styles in the DataGridTemplateColumn. To do so you will have to replace the Deviation Chart column on your DataGrid by a DataGridTemplateColumn:

(…)
<WpfToolkit:DataGridTemplateColumn
    Header="Deviation Chart" Width="1*"
    HeaderStyle="{StaticResource CenterAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource BarChartCellStyle}"/>
(…)

By replacing this column we gave our DataGrid a much cooler look. For each player an age chart is displayed on the right:

WpfToolkitDataGrid-ss015 

You can download a working sample of this code by following this link.

 

Grouping rows using ItemsControl.GroupStyle

From the beginning of this series we have displayed data that was supposed to be grouped. As you have noticed there are two teams and several players per team. A team should not be displayed as a regular row on our DataGrid, it should be some sort of group header that we can collapse and expand as required, showing the players that are part of it.

In this implementation we will follow the recommendations on how to do grouping on WPF Toolkit’s DataGrid.

For you to be able to do grouping you will have to create a CollectionViewSource and specify by which field the grouping will be done. In this case grouping will be done on each player’s Parent field, which corresponds to the actual Team instance.

<CollectionViewSource x:Key="PlayerData">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Parent"/>
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

Note that Source is not specified for the CollectionViewSource. This is due to the fact that the list is being created in code behind; as a consequence, it cannot be instantiated in xaml. You will have to set the Source for this collection after building the players list on the code behind file:

var dataProvider = (CollectionViewSource)FindResource("PlayerData");
dataProvider.Source = playerAges;

After setting up this grouped collection view source you will have to change the way binding is done on the DataGrid. Previously we were binding to a field on our view model. Now you will have to bind to a static resource.

(…)
<WpfToolkit:DataGrid
    ItemsSource="{Binding Source={StaticResource PlayerData}}"
    HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
    CanUserAddRows="False" CanUserDeleteRows="False"
    CanUserResizeRows="False" CanUserSortColumns="False"
    AutoGenerateColumns="False"
    RowHeaderWidth="17" RowHeight="25">
 (…)

The only thing left is for you to create the style of the group’s header. An expander will be used to present the grouped data. On the expander’s header each team’s name will be displayed:

<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type GroupItem}">
                <Expander IsExpanded="True" 
                          Background="{StaticResource GroubHeaderBackgroundBrush}"
                          Foreground="{StaticResource DefaultControlForegroundBrush}">
                    <Expander.Header>
                        <TextBlock Text="{Binding Name.Name}"/>
                    </Expander.Header>
                    <ItemsPresenter />
                </Expander>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now that everything is in place you have to setup the DataGrid to use this specific style when rendering the groups:

<WpfToolkit:DataGrid.GroupStyle>
    <GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
        <GroupStyle.Panel>
            <ItemsPanelTemplate>

                <WpfToolkit:DataGridRowsPresenter/>
            </ItemsPanelTemplate>
        </GroupStyle.Panel>
    </GroupStyle>
</WpfToolkit:DataGrid.GroupStyle>

After these changes this is how our DataGrid is looking:

WpfToolkitDataGrid-ss016 
If you click on a group header so that it collapses its contents this is how it will look:

WpfToolkitDataGrid-ss017 
This implementation works well when you do not allow editing on you cells. If you allow the users to edit something on your DataGrid you will notice that the edited value goes to the bottom of the list instead of remaining in its actual row. The following screenshot is the outcome of editing the age of John Smith. Notice that John was on top and is now presented at the bottom of the Alpha Team player list.

WpfToolkitDataGrid-ss018

Our grouped data is being handled by ListCollectionView. I took a peek on the code for ListCollectionView using Reflector and found out that the source of this problem is that when you edit an item the item is resorted for grouping purposes like follows:

public void CommitEdit()
{
(…)
        if (this.IsGrouping)
        {
            this.RemoveItemFromGroups(item);
            this.AddItemToGroups(item);
        }
(…)
}

BAAM! Obviously that by removing and adding the item back again to the list will cause the item to be presented last. Not only does this affect the order of our list –which is not the intended result – it also deselects the row we are working with (I confess I did not take the time to see this through, I was short on time to finish up what I was working on time, thus the workaround presented on the following subject).

You can download a working sample of this code by following this link.

 

Fake grouping with the help of the ViewModel

The grouping method previously demonstrated, although of clear implementation, has several issues that could not be easily fixed. In my application I ended up using another method for grouping data, a more archaic method but will allow us greater control over how data behaves.

The actual grouping of data will be our view model’s responsibility. Group headers will be authentic rows but with a different style. The logic for hiding or showing rows accordingly to their groups being expanded or not will be controlled by the view model through data binding.

In the PlayerAge class – the data behind each row – there is a field named IsExpanded that is only set on Parent rows (the teams). We can control if the child rows are collapsed or not by adding a DataTrigger that binds to the IsExpanded property of the Parent row for each player.

There will be two different control templates: one for players and one for teams. You will have to create a new style for your DataGridRows. By default you will set the ControlTemplate to be the one regarding the Players:

<ControlTemplate TargetType="{x:Type WpfToolkit:DataGridRow}">
    <Border x:Name="DGR_Border"
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            SnapsToDevicePixels="True">
        <WpfToolkit:SelectiveScrollingGrid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <WpfToolkit:DataGridCellsPresenter
                Grid.Column="1"
                ItemsPanel="{TemplateBinding ItemsPanel}"
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <WpfToolkit:DataGridDetailsPresenter 
                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding 
                    RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}}, 
                    Path=AreRowDetailsFrozen,
                    Converter={x:Static WpfToolkit:DataGrid.RowDetailsScrollingConverter},
                    ConverterParameter={x:Static WpfToolkit:SelectiveScrollingOrientation.Vertical}}"
                Grid.Column="1" Grid.Row="1"
                Visibility="{TemplateBinding DetailsVisibility}" />
            <WpfToolkit:DataGridRowHeader 
                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"  Grid.RowSpan="2"
                Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}}, 
                    Path=HeadersVisibility, 
                    Converter={x:Static WpfToolkit:DataGrid.HeadersVisibilityConverter}, 
                    ConverterParameter={x:Static WpfToolkit:DataGridHeadersVisibility.Row}}"/>
        </WpfToolkit:SelectiveScrollingGrid>
    </Border>
    <ControlTemplate.Triggers>
        <DataTrigger Binding="{Binding Parent.IsExpanded}" Value="False">
            <Setter TargetName="DGR_Border" Property="Visibility" Value="Collapsed"/>
        </DataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

The most important part of this ControlTemplate is the DataTrigger on the bottom. Has you can see we set the visibility of our row based on if the parent is expanded or not.

Now you will have to add a new ControlTemplate for the parent rows (teams). For this purpose you will have to set a DataTrigger on your Style that checks whether the Parent of the bounded item is null or not – indicating that it is a parent, or not (for the complete style please see the sample code for this post).

 

<Style.Triggers>
    <DataTrigger Binding="{Binding Parent}" Value="{x:Null}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridRow}">
                    <Border x:Name="DGR_Border"
                            Background="{StaticResource GroubHeaderBackgroundBrush}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            SnapsToDevicePixels="True">
                        <WpfToolkit:SelectiveScrollingGrid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>

                            <WpfToolkit:DataGridCellsPresenter
                                Grid.Column="1"
                                Background="Red"
                                ItemsPanel="{TemplateBinding ItemsPanel}"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            <WpfToolkit:DataGridDetailsPresenter
                                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding
                                    RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}},
                                    Path=AreRowDetailsFrozen,
                                    Converter={x:Static WpfToolkit:DataGrid.RowDetailsScrollingConverter},
                                    ConverterParameter={x:Static WpfToolkit:SelectiveScrollingOrientation.Vertical}}"
                                Grid.Column="1" Grid.Row="1"
                                Visibility="{TemplateBinding DetailsVisibility}" />
                            <WpfToolkit:DataGridRowHeader 
                                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
                                Grid.RowSpan="2"
                                Visibility="{Binding RelativeSource={RelativeSource 
                                    AncestorType={x:Type WpfToolkit:DataGrid}},
                                    Path=HeadersVisibility,
                                    Converter={x:Static WpfToolkit:DataGrid.HeadersVisibilityConverter},
                                    ConverterParameter={x:Static WpfToolkit:DataGridHeadersVisibility.Row}}"/>
                        </WpfToolkit:SelectiveScrollingGrid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </DataTrigger>
</Style.Triggers>

The ControlTemplate’s trigger was removed given that this row is a parent and will not collapse. After inserting this new style for DataGridRows on the view’s resource dictionary, you will immediately see the result, were group headers display different from other cells. The problem is that we still have not implemented a mechanism that allows us to modify the expanded state of the group.

An example on how to control the IsExpanded state of your team is to create a specific CellStyle for the first column that by knowing which type of row it is binding to will display a toggle button that allows the user to expand or collapse the group:

<Style x:Key="FirstColumnCellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                <Grid Background="{TemplateBinding Background}">
                    <ContentPresenter HorizontalAlignment="Right"
                                      VerticalAlignment="Center" Margin="0,0,4,0" />
                </Grid>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding ToleranceState}"
                                 Value="NotToleranced">
                        <Setter Property="IsEnabled" Value="False" />
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Parent}" Value="{x:Null}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                        <Grid Background="{TemplateBinding Background}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <ToggleButton VerticalAlignment="Center" 
                                          IsChecked="{Binding IsExpanded}" 
                                          Height="12" Width="12" Content="+" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

By now you will be able to expand or collapse each team’s players. This is how the DataGrid looks when these styles get applied.

WpfToolkitDataGrid-ss019

Now when you run the sample you can edit any cell and see that it does not change. I have kept the same style for the header rows but you can easily change it so that no cells are rendered on it, you could even make it look like the one on the previous topic using an expander.

You can download a working sample of this code by following this link.

 

End of Part IV

In this part we have delve into more advanced concepts. We have seen how to work around the issue with binding and the DataGridComboBoxColumn. We also saw the amazing power and flexibility of the DataGridTemplateColumn. The second half of the blog post walked you through two different methods of grouping data within the DataGrid.

By now, and assuming you have gone through all the IV parts, you should be able to represent any type of data as you see fit.

In the next and final part of this series we will look at DataGridCellDetails as well as handling insertion / removal of data from your DataGrid. Stay tuned for Part V Dear Reader!

Shout it kick it on DotNetKicks.com

WPF Toolkit DataGrid, Part III – Playing with Columns and Cells

This post is part of the WPF Toolkit DataGrid series. Here is a list with the complete set of blog posts:

Introduction

In Part II we gave a bluish style to our DataGrid. In this part we will go through setting up how our data gets displayed within the DataGrid. We will have a look on how to create styles for our cells and cell elements along with creating some new types of DataGridColumns that extend existing ones in order to enrich them with further functionality.

 

Roadmap

  1. Aligning DataGridColumnHeaders and DataGridCells content using Styles
  2. Aligning DataGridCells content by extending a DataGridColumn
  3. Enabling and disabling DataGridRows
  4. Creating a LabeledTextBoxColumn
  5. Creating an AutoCommitCheckBoxColumn

Download sample source code

Here is a list with the samples presented on this blog post:

Aligning DataGridColumnHeaders and DataGridCells content

If you are using the DataGrid then you will surely want to control the alignment of text within the cells and column headers. Unfortunately the WPF Toolkit DataGrid does not support setting content alignment on a DataGridColumn. To align cells content you will have to create specific styles for DataGridColumnHeaders based on their default style and assign them individually to each column.

By the end of Part II this was how our DataGrid was looking:

WpfToolkitDataGrid-ss010

To be able to align DataGridColumnHeaders content go to your “DataGrid.Generic.xaml” file and add a couple of styles for right and center aligned column headers:

<!-- Right Aligned DataGridColumnHeader Style -->
<Style x:Key="RightAlignedColumnHeaderStyle" 
       TargetType="{x:Type WpfToolkit:DataGridColumnHeader}"
       BasedOn="{StaticResource ColumnHeaderStyle}">
    <Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
<!-- Center Aligned DataGridColumnHeader Style -->
<Style x:Key="CenterAlignedColumnHeaderStyle" 
       TargetType="{x:Type WpfToolkit:DataGridColumnHeader}"
       BasedOn="{StaticResource ColumnHeaderStyle}">
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>

With these styles defined you be able to apply them to your column headers by changing their design. For this you will need to set the DataGridColumnHeader.HeaderStyle property. In your sample go ahead and set the Age and Deviation columns HeaderStyle to the RightAlignedColumnHeaderStyle and the Deviation Chart to CenterAlignedColumnHeaderStyle:

(…)
<WpfToolkit:DataGridTextColumn
    Header="Age" Width="1*"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    Binding="{Binding Path=Age}"/>
(…)

After changing the DataGridColumnHeaders you will have to specify styles for our cells so that they also get aligned. In you resources file create specific styles to align the content vertically on the center and horizontally on the right, another style to align it horizontally on center and another to align it on the left:

(…)
<!-- Left Aligned DataGridCell Style -->
<Style x:Key="LeftAlignedCellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                <Grid Background="{TemplateBinding Background}">
                    <ContentPresenter HorizontalAlignment="Left"
                                      VerticalAlignment="Center"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
(…)

As done previously with the DataGridColumnHeaders, you will now have to assign each style to the corresponding DataGridColumn. Go ahead and set the PlayerName’s CellStyle to LeftAlignedCellStyle – since we do need to align its content vertically. Then you will have to set Age and Deviation CellStyles to RightAlignedColumnHeaderStyle. Enabled and DeviationChart will be center aligned so you will have to set the corresponding CellStyle on their DataGridColumns.

<WpfToolkit:DataGridCheckBoxColumn
    Header="Enabled" Width=".5*"
    CellStyle="{StaticResource CenterAlignedCellStyle}"
    Binding="{Binding Path=IsEnabled}"/>
<WpfToolkit:DataGridTextColumn
    Header="Player Name" Width="2*"
    CellStyle="{StaticResource LeftAlignedCellStyle}"
    Binding="{Binding Path=Name}"/>
<WpfToolkit:DataGridTextColumn
    Header="Age" Width="1*"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource RightAlignedCellStyle}"
    Binding="{Binding Path=Age}"/>
<WpfToolkit:DataGridTextColumn
    Header="Deviation" Width="1*"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource RightAlignedCellStyle}"
    Binding="{Binding Path=Deviation}"/>
<WpfToolkit:DataGridComboBoxColumn
    Header="Category" Width="1*"
    ItemsSource="{DynamicResource Categories}"
    SelectedValueBinding="{Binding Path=Category}"
    TextBinding="{Binding Path=Category}" />
<WpfToolkit:DataGridTextColumn
    Header="Deviation Chart" Width="1*"
    HeaderStyle="{StaticResource CenterAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource CenterAlignedCellStyle}"
    Binding="{Binding Path=DeviationPercentage}"/>

After correctly aligning our cells this is how our DataGrid is looking:

WpfToolkitDataGrid-ss011

You can download a working sample of this code by following this link.

 

Aligning DataGridCells content by extending a DataGridColumn

A little background before moving on! Each cell can be in either two states: normal state or editing state. For each of these states we can specify the template to be used (by setting DataGridColumn.ElementTemplate and DataGridColumn.EditingElementTemplate) or apply to them a specific style (by setting DataGridColumn.ElementStyle and DataGridColumn.EditingElementStyle). CellStyles are applied to the DataGridCell itself, not the elements within it. This brings us to why our previous solution has some undesired side effects.

If you noticed when you select a cell for editing you see that the Textbox does not occupy the full content of the DataGridCell. The reason for this is that the new style we have defined for our cells applies the alignment to the TextBox within the cell and not to its content.

This is WPF – 10 different ways for doing the same thing. One solution to this, and a more clean solution than the previous one, is to extend the DataGridTextColumn giving it support for content alignment by adding a couple of properties that can be used to set the alignment on the generated elements for each cell. For this you will need to add new class – ExtendedTextBoxColumn – that extends DataGridTextColumn and add to it a couple of properties – HorizontalAlignment and VerticalAlignment. Afterwards you must override the GenerateElement and GenerateEditingElement methods in order to intercept the creation of the corresponding cell elements and set the values accordingly to the ones specified when the DataGridColumn was created.

protected override FrameworkElement 
    GenerateElement(DataGridCell cell, object dataItem)
{
    var element = base.GenerateElement(cell, dataItem);

    element.HorizontalAlignment = HorizontalAlignment;
    element.VerticalAlignment = VerticalAlignment;

    return element;
}

protected override FrameworkElement 
    GenerateEditingElement(DataGridCell cell, object dataItem)
{
    var textBox = (TextBox)base.GenerateEditingElement(cell, dataItem);

    textBox.TextAlignment = GetTextAlignment();
    textBox.VerticalContentAlignment = VerticalAlignment;

    return textBox;
}

Please note that the GetTextAlignment method is just a helper method that maps a HorizontalAlignment to a TextAlignment. The only thing remaining is for you to replace the DataGridTextColumns by the newly created ExtendedTextColumn and remove its CellStyle:

<WpfToolkit:DataGridCheckBoxColumn
    Header="Enabled" Width=".5*"
    CellStyle="{StaticResource CenterAlignedCellStyle}"
    Binding="{Binding Path=IsEnabled}"/>
<Controls:ExtendedTextColumn
    Header="Player Name" Width="2*"
    HorizontalAlignment="Left" VerticalAlignment="Center"
    Binding="{Binding Path=Name}"/>
<Controls:ExtendedTextColumn
    Header="Age" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    Binding="{Binding Path=Age}"/>
<Controls:ExtendedTextColumn
    Header="Deviation" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    Binding="{Binding Path=Deviation}"/>
<WpfToolkit:DataGridComboBoxColumn
    Header="Category" Width="1*"
    ItemsSource="{DynamicResource Categories}"
    SelectedValueBinding="{Binding Path=Category}"
    TextBinding="{Binding Path=Category}" />
<Controls:ExtendedTextColumn
    Header="Deviation Chart" Width="1*"
    HorizontalAlignment="Center" VerticalAlignment="Center"
    HeaderStyle="{StaticResource CenterAlignedColumnHeaderStyle}"
    Binding="{Binding Path=DeviationPercentage}"/>

This is pretty basic stuff but replaces all of our styles when using text columns. It also solves the issue with the size of the Textbox for the EditingElement when in editing mode. On the left there is a screenshot of how a cell looked when in edit mode, on the right we have the cell being edited using ExtendedTextColumn:

WpfToolkitDataGrid-ss012

You can download a working sample of this code by following this link.

 

Enabling and disabling DataGridRows

Now let’s go and add some functionality to our DataGrid. The requirement is a simple one: whenever the user checks the CheckBox on the first column, the DataGridRow should be disabled, and he must not be able to edit the contents of any cell.

Although this is a simple requirement there is no direct way of doing it without creating a specific editing element style per column type. For each Player we have an IsEnabled field to which we bind our column in order to populate the CheckBoxes. We then have to bind each of the controls in the elements and disallow the editing on the cell based on the corresponding IsEnabled value.

To accomplish this, go ahead and add three new styles to the resources of your UserControl (these styles can be defined locally because they are specific and will probably make no sense outside this context), one for each type of control you use o your columns – TextBlock, TextBox and ComboBox.

<Style x:Key="BaseTextBlockCellStyle" TargetType="{x:Type TextBlock}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>
<Style x:Key="BaseTextBoxCellStyle" TargetType="{x:Type TextBox}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>
<Style x:Key="BaseComboBoxBoxCellStyle" TargetType="{x:Type ComboBox}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>

Having defined the styles, go through the DataGridColumn specifications and set their ElementStyle and EditingElementStyle to the corresponding resources:

(…)
<Controls:ExtendedTextColumn
    Header="Deviation" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    ElementStyle="{StaticResource BaseTextBlockCellStyle}"
    EditingElementStyle="{StaticResource BaseTextBoxCellStyle}"
    Binding="{Binding Path=Deviation}"/>
<WpfToolkit:DataGridComboBoxColumn
    Header="Category" Width="1*"
    ItemsSource="{DynamicResource Categories}"
    SelectedValueBinding="{Binding Path=Category}"
    ElementStyle="{StaticResource BaseComboBoxBoxCellStyle}"
    EditingElementStyle="{StaticResource BaseComboBoxBoxCellStyle}"
    TextBinding="{Binding Path=Category}" />
(…)

Now when we run the sample this is what we see when we try editing a value which we have disabled for editing:

WpfToolkitDataGrid-ss013

As you can see we have unchecked player “John Mufin” and the player’s name edit box is disabled.

You can download a working sample of this code by following this link.

 


Creating the LabelTextBoxColumn

If you run the sample and uncheck a row, you will not notice changes in the row until you select a field to edit, even though the TextBlock IsEnabled property was set to false. We could create a new style for TextBlock and change its foreground based on its enabled state. I have chosen another approach. Let’s change the cells element to be a Label instead of a TextBlock since the Label has already support for enable. When we set IsEnabled to false on a Label it will display a gray foreground.

Go ahead and add a new class that extends our ExtendedTextBoxColumn and name it LabelTextBoxColumn.

public class LabelTextBoxColumn : ExtendedTextBoxColumn
{
    private void ApplyStyle(bool isEditing, bool defaultToElementStyle, 
        FrameworkElement element)
    {
        var style = PickStyle(isEditing, defaultToElementStyle);
        if (style != null)
            element.Style = style;
    }

    private Style PickStyle(bool isEditing, bool defaultToElementStyle)
    {
        var style = isEditing ? EditingElementStyle : ElementStyle;
        if (isEditing && defaultToElementStyle && (style == null))
            style = ElementStyle;
        return style;
    }

    private void ApplyBinding(DependencyObject target, 
        DependencyProperty property)
    {
        var binding = Binding;
        if (binding != null)
            BindingOperations.SetBinding(target, property, binding);
        else
            BindingOperations.ClearBinding(target, property);
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell, 
        object dataItem)
    {
        var label = new Label
{
     HorizontalAlignment = this.HorizontalAlignment,
     VerticalAlignment = this.VerticalAlignment
}; ApplyStyle(false, false, label); ApplyBinding(label, ContentControl.ContentProperty); return label; } }

This class is of very simple implementation. In fact we are only replacing the GenerateElement method with a custom implementation that creates a Label instead of a TextBlock. The ApplyStyle, PickStyle and ApplyBinding methods are all part of WPF Toolkit, I had to copy them to this sample since WPF Toolkit does not expose them publically – they are either private or internal and part of DataGridTextBoxColumn.

Since you now have a new type of column a new style must be added to bind the LabelTextBoxColumn Label element to the IsEnabled property of the current player. In fact just change the style you have for the TextBlock so that it applies to Labels:

<Style x:Key="BaseLabelCellStyle" TargetType="{x:Type Label}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>

Now you have to change your DataGrid to use this new LabelTextBoxColumn. Just replace all the ExtendedTextBoxColumn by the LabelTextBoxColumn. You will also have to replace the ElementStyle by the new BaseLabelCellStyle:

<Controls:LabelTextBoxColumn
    Header="Age" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    ElementStyle="{StaticResource BaseLabelCellStyle}"
    EditingElementStyle="{StaticResource BaseTextBoxCellStyle}"
    Binding="{Binding Path=Age}"/>

After these changes when you uncheck the Enabled CheckBox the row will turn gray and you will have an obvious feedback that it is disabled:

WpfToolkitDataGrid-ss014

You can download a working sample of this code by following this link.

 

Creating an AutoCommitCheckBoxColumn

By default the DataGrid only allows you to check a CheckBox on a column when this column is in editing mode. Basically, in order for you to disable a row you will have to uncheck the CheckBox, move the focus out of the cell and only then will your change be committed.

What a user would expect of this column was for it to be effective on the first click. He is not expecting to have to click the CheckBox and only see the results of the commit after he clicks away from this control in order for it to lose focus. This is where the AutoCommitCheckBoxColumn comes into play.

We will be creating a type of column that is tightly bounded to the CheckBox it generates listening to its changes and committing them to the DataSource.

public class AutoCommitCheckBoxColumn : DataGridCheckBoxColumn
{
    private void checkBox_Unchecked(object sender, RoutedEventArgs e)
    {
        CommitCellEdit((FrameworkElement)sender);
    }

    private void checkBox_Checked(object sender, RoutedEventArgs e)
    {
        CommitCellEdit((FrameworkElement)sender);
    }

    protected override FrameworkElement GenerateEditingElement(
        DataGridCell cell, object dataItem)
    {
        var checkBox = (CheckBox)base.GenerateEditingElement(cell, dataItem);

        checkBox.Checked += checkBox_Checked;
        checkBox.Unchecked += checkBox_Unchecked;

        return checkBox;
    }
}

We just have to hook the Checked and Unchecked events and force a commit on the cell. Now whenever the user clicks the CheckBox he will have an instant response giving him a higher impression of feedback.

You can download a working sample of this code by following this link.

 


End of Part III

I hope you have enjoyed this new post on the WPF Toolkit DataGrid, it has been a pleasure writing it! In this part we have seen how to create custom DataGridColumns. Our main focus was on how to correctly display data and allow the user to interact with it. We have seen how to control each element rendered within a cell.

On part IV we will play with DataGridTemplateColumns and get our team grouping to work. Stay tuned Dear Reader!

Shout it kick it on DotNetKicks.com

WPF: Padding does not work on styled button

This is one of those things that can easily fall through the cracks when creating a new ControlTemplate for a WPF Button.

The problem

Let’s suppose you have the following code to render an OK and a Cancel button on the bottom of a window:

<Button Content="_OK" Margin="0,0,8,0" Padding="8,2"
        HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="_Cancel" Padding="8,2"
        HorizontalAlignment="Center" VerticalAlignment="Center" />

 

A nice padding was added to add some space between the content text and the outer borders of the button. This is how these buttons look:
Now you want to add a new ControlTemplate to your Button style and create a ContentPresenter within that template to accommodate the content the user of your style will provide, similar to what is achieved in this reference page at MSDN:
(…)
<ControlTemplate TargetType="Button">
    <Border x:Name="Border"
            CornerRadius="2" BorderThickness="1"
            Background="{StaticResource NormalBrush}"
            BorderBrush="{StaticResource NormalBorderBrush}">
        <ContentPresenter Margin="2"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          RecognizesAccessKey="True"/>
(…)
After applying this new style and running your application the subsequent picture shows how the buttons would look.
WpfTips0005

As you can see the nice space we had specified for our buttons with Padding is not visible anymore and the text almost touches the borders of our buttons. Basically the padding you are adding on your Button’s xaml is being ignored by the new style.

 

The solution

The solution to this problem is a very simple one. You just have to ensure that you bind your ContentPresenter’s margin to the TemplatedParent Padding property, as follows:

(…)
<ControlTemplate TargetType="Button">
    <Border x:Name="Border"
            CornerRadius="2" BorderThickness="1"
            Background="{StaticResource NormalBrush}"
            BorderBrush="{StaticResource NormalBorderBrush}">
        <ContentPresenter Margin="{TemplateBinding Padding}"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          RecognizesAccessKey="True"/>
(…)

This way a Padding will be applied when the Button’s content gets rendered. After this change this is how our would buttons look:

WpfTips0006

As you can see padding is now accounted for and our Buttons look much cooler!

Shout it kick it on DotNetKicks.com