ReadOnly Rows and Cells in a DataGrid
A common requirement for a DataGrid control is to have cells, or entire rows and/or columns that are read-only, or in other words non-editable. My requirements for this are as follows:
- ReadOnly can be applied to the entire grid, a column, a row, or an individual cell.
- The cell(s) must not allow the cell value to be modified.
- The cell(s) must be highlighted in some manner (e.g. background colour) to indicate that they are different to the editable cells.
- ReadOnly columns require only single direction data-binding (i.e. Mode=OneWay to read-only properties).
The Microsoft WPF DataGrid meets these requirements via:
- DataGrid.IsReadOnly property
<toolkit:DataGrid IsReadOnly="True"
- DataColumn.IsReadOnly property
<toolkit:DataGridTextColumn Binding="{Binding FullName,Mode=OneWay}" IsReadOnly="True"/>
We can use a simple style targeting all DataGridCells to change the background colour.
<Style TargetType="{x:Type toolkit:DataGridCell}"> <Style.Triggers> <Trigger Property="IsReadOnly" Value="True"> <Setter Property="Background" Value="LightGray"/> </Trigger> </Style.Triggers> </Style>
So the only thing that’s really missing here is the ability to mark an entire row as read-only. In my experience this is a common requirement – we have a list of records displayed in the grid some of which are locked/completed/secured, whilst others can be edited.
One solution is to override the OnBeginningEdit method of the DataGrid. The following example assumes that I have an attached property ControlSupport.IsReadOnly.
protected override void OnBeginningEdit( DataGridBeginningEditEventArgs e ) { base.OnBeginningEdit( e ); bool isReadOnlyRow = ControlSupport.GetIsReadOnly( e.Row ); if ( isReadOnlyRow ) e.Cancel = true; }
However, since I’ve got used to “tweaking” some of the DataGrid code I decided to instead to simply add an IsReadOnly property to the DataGridRow class.
public bool IsReadOnly { get { return (bool) GetValue( IsReadOnlyProperty ); } set { SetValue( IsReadOnlyProperty, value ); } } // Using a DependencyProperty as the backing store for IsReadOnly. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register( "IsReadOnly", typeof( bool ), typeof( DataGridRow ),
new FrameworkPropertyMetadata( false, OnNotifyRowAndCellsPropertyChanged ) );
private static void OnNotifyRowAndCellsPropertyChanged( DependencyObject d,
DependencyPropertyChangedEventArgs e ) { ( d as DataGridRow ).NotifyPropertyChanged( d, e, NotificationTarget.Rows | NotificationTarget.Cells ); }
Then I just needed to make sure that the read-only property DataGridCell.IsReadOnly would correctly return the right value when its row was marked as read-only.
private static object OnCoerceIsReadOnly(DependencyObject d, object baseValue) { var cell = d as DataGridCell; var column = cell.Column; var row = cell.RowOwner; var dataGrid = cell.DataGridOwner; return DataGridHelper.GetCoercedTransferPropertyValue( cell, baseValue, IsReadOnlyProperty, row, DataGridRow.IsReadOnlyProperty, column, DataGridColumn.IsReadOnlyProperty, dataGrid, DataGrid.IsReadOnlyProperty); }
There’s a fairly complex series of notification propagation calls going on within the DataGrid classes. For this solution to work it requires that the DataGridRow.IsReadOnly property changing flows down and causes the read-only DataGridCell.IsReadOnly property to be re-evaulated (via the coerce method above). I had to add another GetCoervedTransferPropertyValue method that took another pair of object/property parameters, and also tweaked the DataGridCell.NotifyPropertyChanged as follows:
else if (e.Property == DataGrid.IsReadOnlyProperty ||
e.Property == DataGridColumn.IsReadOnlyProperty ||
e.Property == DataGridRow.IsReadOnlyProperty ||
e.Property == IsReadOnlyProperty) { DataGridHelper.TransferProperty(this, IsReadOnlyProperty); }
Now in my XAML I can do the following (assuming I have an IsReadOnly property on my business objects):
<Style TargetType="{x:Type toolkit:DataGridRow}"> <Setter Property="IsReadOnly" Value="{Binding IsReadOnly}"/> </Style>



Where do I write the code suggested do could you perhaps email mail me a solution of the full cs file?
This was exactly what I wanted to do.
Reply to this
I'm like Oliver, this is exactly what I would like to do too. Could you please send let me know how to get the source code for this sample?
Many thanks
Reply to this
Here is a sample solution that demonstrates customising the WPF Toolkit DataGrid - including allowing ReadOnly rows. The solution contains two projects - my sample application and a modified version of the WPFToolkit. It's based on the March 2009 version of the WPF Toolkit - there aren't that many changes - you should be able to use BeyondCompare/WifDiff etc. to work out the changes I made.
Note that to make these changes I modified the original source so you will need to recompile the updated WPF Toolkit. I'm using source control so its easy to track the changes I make and re-apply the "patches" to new versions when Microsoft release them (like the just released June WPF Toolkit). Having said that it would have been nice to achieve the desired fixes by subclassing or using attached properties, events but I think that would have become very "messy" given the number of changes.
From memory some of the other changes were - removing the ridiculous "Show Calendar" text that displays in the DatePicker, preserving CharacterCasing on grid TextBox... hmm... I can't remember the rest. Still you should see all the changes by comparing my source against the March 2009 source.
Reply to this
want this code.
Reply to this