Introduction
This is something you would think would be pretty simple, but it isn't, unless I'm missing something. I thought I'd write a little step by step of how I arrived at the solution I came up with in case I took a more difficult path than I needed to and someone can point out where I took a wrong turn.
Background
Here is the basic XAML for a DataGrid
and what it looks like in the browser. You have sorting, validation, and two way binding, which is pretty nice:
The dilemma
All this works fine and until someone says they want the first two columns read-only and they want the third column with a yellow background so the user can easily tell which columns are editable. "No problem" you say, whereupon you become perplexed at the curious lack of a Background
or a BackgroundColor
property in the text column:
You, being an industrious person not easily discouraged by such trifles, decide to turn to the templated cell to gain more control over what is going on, making a template for the read only view (a TextBlock
) and for the edit mode (a TextBox
). The TextBox
does have a Background
property, sweet success! But the TextBlock
doesn't have a Background
property, crushing defeat. To add insult to injury, now sorting and validation don't work either. But, we do have a different background color in edit mode, so that is progress of a sort.
Utilizing your finely honed Google-Fu, you correct the two issues just created, with the following:
And then, if the TextBlock
doesn't have a Background
property, let's just throw something behind it that does, like a Border
, and we should be all done, right?
Well, that's pretty close, but now, as we mouse over the rows, we don't see the nice highlight effect that was there, and it just, generally, doesn't look that great. It would be nice if we could still have the nice UI cues that were there in the first place, but just, well, with a different background color. At this point, you may start pondering a dive into the visual state manager and animations, but as you watch everyone else leaving the office, you start wondering why you've been spending all day messing with the background color of a column.
Ignoring this irrelevant piece of information, you sense inspiration, and think, "Ah! if the background were only partially visible, the existing animation might be more visible, and no additional work would be needed!"
Which is kinda true... Except now, the font is slightly transparent as well, and no amount of font bolding or manual setting of opacity seems to help:
Cursing the stars and heavens while simultaneously pounding your keyboard with your elbows, you accidentally stumble into this, which does work! Eureka, you have done it!
Except... You feel kind of dirty because in order to change the *&#$^*&^$% background color, you have exploded one line of XAML into 15, which seems excessive. Furthermore, there is great redundancy in the name of the column, the background color used, and you are going to have to copy and paste this multiple times for multiple columns. You briefly consider using a style, but that will only get you so far since you'd still have to specify the two templates for each column using this method.
A voice in the back of your head is telling you to set fire to this DataGrid
and look into vendor grids, but no, you won't be undone by these minor issues.. You will find a way to beat this issue into submission. Shortly before running out of curse words and keyboards, you arrive at the final solution which looks and acts exactly like how you would expect it to:
public class DataGridTextColumnExtended : DataGridTemplateColumn
{
public string Background { get; set; }
public string BindingProperty
{
set
{
if (value == null)
throw new ArgumentNullException("BindingProperty");
base.SortMemberPath = value.ToString();
CellTemplate = (DataTemplate)XamlReader.Load(@"
<DataTemplate
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<Border Background='" + Background + @"' Opacity='.5' />
<TextBlock Text='{Binding Path=" + value + @"}' Margin='4' />
</DataTemplate>
");
CellEditingTemplate = (DataTemplate)XamlReader.Load(@"
<DataTemplate
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<TextBox Background='" + Background + @"' Text='{Binding
NotifyOnValidationError=True, ValidatesOnExceptions=True,
Path=" + value + @", Mode=TwoWay}' />
</DataTemplate>
");
_BindingProperty = value;
}
get
{
return _BindingProperty;
}
}
private string _BindingProperty = null;
}
Enjoy!
Points of Interest
No actual keyboards or desks were harmed in the making of this article.
Also, if you have a cleaner way of creating the custom column with the code being in a separate XAML file instead of C# (while keeping the usage XAML the same), I'd love to hear about it.
History
- 2-Jan-2010 - Initial version.