Monday, December 22, 2008

WPF - data templating

Well, I found the weird facts about WPF (at least I think that they are weird) during resolving the first question from the list of the prior post:

How to hide the column headers for WPF list view controls?

I found no way to disable the column headers for WPF ListView control and no way to disallow the resizing of the columns. However I learned WPF feature called "data templating" which is new for me. Data templating allows you to define the appearance of the items in ListBox and ListView control. Data templating allows you to define the relation between the visual components properties and data source. To achieve my needs (show the data using multicolumn grid view but do not show the column headers) I should use ListBox control specifying the data template for the ListBox items. MSDN shows the examples of the data templates using XAML syntax only, i.e.:
<ListBox.ItemTemplate>
 <DataTemplate>
  <StackPanel>
   <TextBlock Text="{Binding Path=Name}" />
   <TextBlock Text="{Binding Path=Value}"/>
  </StackPanel>
 </DataTemplate>
</ListBox.ItemTemplate>


But creating the Data Template programmatically topic looks like somewhat classified. Well, let’s look at this topic closer. ListBox and ListView types have ItemTemplate property which is inherited from ItemsControl class which represents a control that can be used to present a collection of items. The type of ItemTemplate property is DataTemplate class. DataTemplate overview in MSDN also has only XAML syntax bases examples. DataTemplate class has VisualTree property which should be set to define the appearance of the items. Actually this is not that explicitly stated in MSDN. Then the type of VisualTree property of DataTemplate class is FrameworkElementFactory class. The description of this class says:

"This class is a deprecated way to programmatically create templates…not all of the template functionality is available when you create a template using this class. The recommended way to programmatically create a template is to load XAML from a string or a memory stream"

So, guys in MS believe that creating the data templates programmatically is a deprecated way! It sounds like terrific fact for me. However, I tried to find the limitations that exist for creating the data templates programmatically and instantaneously faced the problem. First of all I tried to create the visual tree for the data template that is shown above (stack panel has 2 child textblocks, the 1st textblock is bound to Name property of data source item, the 2nd textblock is bound to Value property). Operating on FrameWorkElement factory is weird itself. Look at the code:
private static FrameworkElementFactory CreateDataTemplate()
{
   var txtNode = new FrameworkElementFactory(typeof(TextBlock));
   txtNode.SetBinding(TextBlock.TextProperty, new Binding("Name"));
   var valueNode = new FrameworkElementFactory(typeof(TextBlock));
   valueNode.SetBinding(TextBlock.TextProperty, new Binding("Value"));
   var stackPanelNode = new FrameworkElementFactory(typeof (StackPanel));
   stackPanelNode.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
   stackPanelNode.AppendChild(txtNode);
   stackPanelNode.AppendChild(valueNode);
   return stackPanelNode;
}

So, to create the visual tree for the data template programmatically I need to manually replicate the corresponding XAML nodes tree in the code and this way has limitations.
Let’s return to the goal I wanted to achieve:
Show the data using multicolumn grid view but do not show the column headers.
Basically I need to show the data in ListBox control and define the data template with visual tree that is Grid control:
<Grid>
 <Grid.ColumnDefinitions>
  <ColumnDefinition Width="100" />
  <ColumnDefinition Width="50" />
 </Grid.ColumnDefinitions>
 <Grid.RowDefinitions>
  <RowDefinition />
 </Grid.RowDefinitions>
 <TextBlock Text="{Binding Path=Name}" Grid.Column="0" Grid.Row="0" />
 <TextBlock Text="{Binding Path=Value}" Grid.Column="1" Grid.Row="0" />
</Grid>

Well, I tried to create this visual tree programmatically:

private static FrameworkElementFactory CreateDataTemplate()
{
   var txtNode = new FrameworkElementFactory(typeof(TextBlock));
   txtNode.SetBinding(TextBlock.TextProperty, new Binding("Name"));
   txtNode.SetValue(Grid.RowProperty, 0);
   txtNode.SetValue(Grid.ColumnProperty, 0);
   var valueNode = new FrameworkElementFactory(typeof (TextBlock));
   valueNode.SetBinding(TextBlock.TextProperty, new Binding("Value"));
   valueNode.SetValue(Grid.RowProperty, 0);
   valueNode.SetValue(Grid.ColumnProperty, 1);
   var rowDefNode = new FrameworkElementFactory(typeof (RowDefinition));
   var col1DefNode = new FrameworkElementFactory(typeof (ColumnDefinition));
   var col2DefNode = new FrameworkElementFactory(typeof(ColumnDefinition));
   var rowsDefNode = new FrameworkElementFactory(typeof (RowDefinitionCollection));
   rowsDefNode.AppendChild(rowDefNode);
   var colsDefNode = new FrameworkElementFactory(typeof (ColumnDefinitionCollection));
   colsDefNode.AppendChild(col1DefNode);
   colsDefNode.AppendChild(col2DefNode);
   var gridNode = new FrameworkElementFactory(typeof (Grid));
   gridNode.AppendChild(rowsDefNode);
   gridNode.AppendChild(colsDefNode);
   gridNode.AppendChild(txtNode);
   gridNode.AppendChild(valueNode);
}

Executing this code throws the following exception:
"'RowDefinitionCollection' type must derive from FrameworkElement, FrameworkContentElement, or Visual3D."
Yep! You can’t create the visual tree nodes for types which are not derived from the listed types! This is the main limitation of creating data template programmatically.
When I faced that I thought that I made some mistake, I thought – well, probably I can load the data template from pre-defined XAML visual tree that is based on Grid definition and look how VisialTree property value is constructed? Nope. If you load the data template from XAML content, then VisualTree property of DataTemplate instance is null.
So, the only way to construct the data template programmatically is load it from XAML fragments. I think that this is very weird fact.
Indeed I can be asked – why do you need to create the data templates at runtime? Do you know any live use-case of that? Yes, I know.
The first idea that came to my mind when I learned the data templates is we can create the simple reporting engine using WPF capabilities. The reporting engine that seems like ActiveReports would be created very easy. End users would specify the data they want to show and the data item template exactly like ActiveReports designer. Then the engine would construct the data template programmatically and show the data in WPF ListBox control. With the current way of operating on Data Templates programmatically this is not possible.
Well, once again returning to the problem that I tried to resolve – I made it by using Grid based data template that is loaded from XAML resource file. The code is trivial.

Sunday, December 14, 2008

WPF - the power of data binding

During last 2 weeks I was distracted from VS.NET 2010 learning. We prepare the next release of Data Dynamics Reports and I am totally occupied by coding, code reviewing and testing.

However, one of the tasks I am working on turned me onto looking at WPF and I would like to write about it here.

Well, one of the common tasks in Windows Applications development is presentation of the data. You might want to show SQL Server table data in the DataGrid control or show the list of items that come from GData sources.

I worked on the code which is intended for show the collection of the business objects in such a way that the presentation is multi-column grid view where every column shows the certain business object property value. The collection of business objects may change dynamically – the end user can add the new items, delete the existing items and change the object property values. The presentation has to reflect these changes immediately. The presentation should not provide edit capabilities itself. The rest of UI provides end-users with tools that allow editing the collection.

.NET 2.0 Windows Forms include ListView control which is exactly that I needed – it shows the list of the items and every item can consist of sub-items and every sub-item can be shown in the new column.

However, .NET 2.0 ListView has the great hole – it doesn’t allow data binding. That means:

  • I need to initialize ListView items collection by going through the business object collection, creating ListViewSubItem object for every property value, then creating ListViewItem for every object, then adding the new items in ListView.
  • I need to handle the business object collection changes and re-initialize ListView items.
  • I need to handle the business object properties values changes and reflect them in ListView control.

All these stuff doesn’t seem straightforward, yep? However, .NET 2.0 can’t offer any other solution except of DataGrid control which isn’t suitable in my situation by the number of reasons.

Well, I thought – “How did they change the situation in WPF?” I have heard that WPF has the super-awesome-cool data binding capabilities. And I looked at how I can implement my task using WPF. I was impressed. ListView control in WPF supports data binding as any other control. To bind the collection of business object to list view control you just need to ensure that the collection class implements INotifyCollectionChanged interface in order to reflect the changes in the collection immediately and INotifyPropertyChanged interface in order to reflect the changes in the object properties values. .NET 3.5 provides ObservableCollection<(Of <(T>)>) Class which implements both of interfaces. The code to bind the data to ListView is pretty straightforward. Say I have the following business objects class:

To create the collection that is bound to ListView control I use the following code:

And to bind this collection to list view I use the following code:

That’s all! Looks very cool, doesn’t it?

After I tried this, the new question came to my mind. I didn't find the obvious solution for them.
  • How to hide the column headers for WPF list view controls?
  • How to highlight the list view item when the mouse is over it?
  • How to set the background for the selected item?
  • How to disable the columns resizing capability?
Well, I will try to figure it out and write about any interesting:)

Thursday, December 4, 2008

VS.NET 2010 - the illusion of the built-in TDD support.

TDD means Test Driven Development. Every person involved in the software development used it or at least heard about that. VS.NET 2010 "what's new" documentation has the "TDD support" item in the list of the new features. What would it mean? How did they make the support of software development philosophy in IDE? I tried to figure it out and was totally disappointed...
Well, there is the new Project Template in VS.NET 2010. It is called "Test Project" and it's description is "The project that contains the test":) In reality this is the Class Library Project that has the reference to the new MS assembly that contains classic "TestFixture", "Test" attributes, assertion methods and so on. Also, the newly created project contains the class with the test methods and text file with the brief description of TDD concept.
So, once you start the development keeping TDD in mind you should start with writing the new test. The test typically consists of creating the new instance of some type, invoking this type methods and compare the real results of something with the expected result. You write the code that creates the new instance of non-existing type. Visual Studio IDE reports about the error. You click on the smart tag near the code that creates the instance of the non-existing type and IDE creates the new type for you. Then you can run the tests, see if they fail, fix the code, run the tests again and so on.
So, what actually MS did for "TDD support"?
  • They created VS.NET add-in that allows to automatically create the new classes, methods, properties, indexers and so on by clicking on the smart tags near the piece of the code. Frankly, it can be done for a couple of days by every programmer who intimate with VS add-in development.
  • They created the new assembly that actually copies NUnut.Framework assembly. Yeah, I bet no one will want to re-write tons of unit tests created using NUnit or MBUnit using this new library.
  • They added the tools that allow to run the unit tests that created using the MS unit tests library.(Resharper has the tools that allow to run NUnit tests from IDE:))
  • They added the new Project Template which is actually not new.
  • They called all these stuff "built-in TDD support".
And I am ready to hear the words like "TDD makes your business flow easier and VS.NET 2010 has the TDD support!" in MS sessions.
So, what is conclusion? The conclusion is - MS developed the pretty simple and useless feature, packed it to the nice wrappers and called it "TDD support!"
Everyone knows what would be result if pack the useless feature to the nice wrappers.