Jaime Rodriguez On Windows Phone, Windows Presentation Foundation, Silverlight and Windows 7
In part 1, I walked through some of the features in datagrid. The source for the series is here.
In this part, we will create a UI that looks like this:
I will mostly highlight the interesting parts [instead of going on a step by step]. The source is available and it would be repetitive if I go step-by-step. One thing to note is that (approximately) 90% of the work to customize it is in XAML. Some of it I did in code just to illustrate a point. DataGridTextColumn bindings were the simplest ones. You can assign a DataFieldBinding -- which is an actual Binding to the data you want. I liked the approach of passing the full binding [instead of a property name] because it allowed me to pass parameters like StringFormat (see below) and converters and other similar binding features. Nice!
<dg:DataGridTextColumnDataFieldBinding="{BindingDescription}"Header="Description"/> <dg:DataGridTextColumnDataFieldBinding="{BindingQuantity}"Header="Quantity" /> <dg:DataGridTextColumnDataFieldBinding="{BindingQuote,StringFormat={}{0:C}}"Header="Quote" />
<dg:DataGridHyperlinkColumn DataFieldBinding="{Binding SymbolUri}" ContentBinding="{Binding Symbol}" Header="Symbol" SortMemberPath="Symbol"/>
If you run the app, you can also see the "edit' behavior for DataGridHyperlinkColumn. You can edit the Uri, but not change the actual text (ContentBinding). You can manipulate it programmatically, but not from the editing experience.
Today's change column, I implemented as a DataGridTemplateColumn.
<dg:DataGridTemplateColumn CellTemplate="{StaticResource DailyPerformance}" Header="Today's Change" SortMemberPath="DailyDifference" />
A DatagridTemplateColumn is one where I can apply a CellTemplate so that it generates the UI. I first chose it for this column because I wanted to implement the behavior of highlighting gains ( >0 ) with Green and losses ( <0) as Red using a DataTemplate.Trigger, but that did not work so I ended up using a Converter. Hind-sight this is likely a better solution [more performant] any way.
<DataTemplate x:Key="DailyPerformance"> <TextBlock Text="{Binding DailyDifference, StringFormat={}{0:C}}" Foreground="{Binding '', Converter={StaticResource StockToBrushConverter}, ConverterParameter=IsDailyPositive }"> </TextBlock> </DataTemplate>
Notice that I was still able to use a SortMemberPath on the DataGridTemplateColumn. This is really nice because regardless of what my UI looks like I can still sort the data.Total Gain column uses the same technique than Today's change. Rating column is a little gaudy on purpose.
<dg:DataGridTemplateColumn CellTemplateSelector="{StaticResource StarsTemplateSelector}" Header="Rating" SortMemberPath="Rating"/>
Here I used a TemplateSelector just for illustration purposes. The selector is trivial. All it does is look for a template in a resource dictionary for the datagrid.
public class StarsTemplateSelector : DataTemplateSelector { public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) { StockXAction sac = item as StockXAction; FrameworkElement fe = container as FrameworkElement; if (sac != null && fe != null ) { string s = sac.Stars.ToString() + "StarsTemplate"; DataTemplate ret = fe.FindResource(s) as DataTemplate; return ret; } return base.SelectTemplate(item, container); } }
From the XAML, you can also notice the SortMemberPath again. The UI now has Star ratings on it, yet I can still sort and did not have to write any code !!
Separator Columns are empty 'dummy' columns I added just make empty space to separate the colums from autogenerated ones. See Autogenerated Columns below for why. Autogenerated columns I wanted to leave AutoGenerateColumns="true" so you could see how the 'raw' data turns into the view. It is also nice because you get to see some of the Column types I did not use for example the ComboBoxColum -- you can see it on the Autogenerated rating column. It is an enum, and it turns into a ComboBoxColumn.
Default data for new rows If you scroll to the bottom and a new row [functionality that comes out of box]. You will see this:
The NaN is a problem. What happens is here is it is trying to calculate Gain, but data has not been initialized.
The workaround is to handle the DataGrid's InitializeNewItem. This will be called as a new record is initalized.
this.BigKahuna.InitializingNewItem += new InitializingNewItemEventHandler(BigKahuna_InitializingNewItem);
void BigKahuna_InitializingNewItem(objectsender,
InitializingNewItemEventArgs e) { //cast e.NewItem to our type StockXAction sa = e.NewItem as StockXAction; if (sa != null) { //initialize sa.Symbol = "New data"; sa.Quantity = 0; sa.Quote = 0; sa.PurchasePrice = 0.0001; } }
Copying data on DataGridTemplateColumns
Another issue you would notice is that if you do a Copy (Ctrl-C) or right click into Context menu which I added, the TemplatedColumns are not copied by default. What I needed to handle in order for copying to work is to pass a binding to ClipboardContentBinding. So we can tweak the template we had earlier and I will be tricky and pass the enumerator (Stars).
<dg:DataGridTemplateColumn CellTemplateSelector="{StaticResource StarsTemplateSelector}" Header="Rating" SortMemberPath="Rating" ClipboardContentBinding="{Binding Stars}" />
Now when I copy paste, I do get the value generated from ToString() on the enumerator.
One more thing to mention around Copying is that Data*Column has a CopyingCellClipboardContent event. This is good for overriding the value if I did not have a binding; what I noticed on this build is that if there is no binding set on ClipboardContentBinding, the event is not firing. This will be fixed by RTM, interim just pass any binding (like {Binding}) and when the event fires you can override the value that will be cut & pasted from code.
OK, that covers most of the functionality. In part 3 we can take care of the styling.
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Jamie Rodriguez has this really nice series on getting started with the WPF DataGrid . Part1 is a brief
Well, their is a huge hype surrounding the release of .NET Framework 3.5 SP1 & Visual Studio 2008
In addition to releasing the .NET Framework 3.5 SP1 last week, which included a number of improvements
Finally WPF has its own Datagrid!
Great tutorial and sample. Is there a way to create a separator row so it doesn't contain the column type control? For example, I have a column type of DataGridCheckBoxColumn, but in my separator row I don't want a checkbox to appear, I just want a completely empty row. Is that possible?