How to make WPF DataGridCell ReadOnly?

Viewed 37682

I understand you can make the whole DataGrid or a whole column readyonly (IsReadOnly = true). However, at cell level this property is ready only. But I do need this level of granularity. There is blog about adding IsReadOnly to a row by changing the source code in old days when DataGrid was public domain, but now I don't have source code for DataGrid. What's workaround?

Making cell disabled (IsEnabled=false) almost meets my need. But the problem is that you can't even click the disabled cell to select the row (I have full row selection mode).

EDIT: Since nobody has responded to this question, so I guess it's not an easy fix. Here is a possible workaround: Make the cell uneditable. The only problem is that clicking the cell doesn't select the row. I just noticed that MouseDown or MouseUp event of the DataGrid is still fired when the disabled cell is clicked. In this event handler, if I could figure out the row it clicked, I could select the row programmatically. However, I couldn't figure out how to find the underlying row from DataGrid.InputHitTest. Can somebody please give me some tip?

11 Answers

Based on @sohum comment, here you can use simplified version of the response marked as answer.

dataGrid.BeginningEdit += DataGrid_BeginningEdit;

(...)

private static void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
    //Actual content of the DataGridCell
    FrameworkElement content = e.Column.GetCellContent(e.Row);
    MyObject myObject = (MyObject)content.DataContext;

    if (!myObject.CanEdit)
    {
        e.Cancel = true;
    }
}

You can use it later as Attached Property Behaviour.

In my case I was using DataGridTextColumn. I set the IsEnabled property on ContentPresenter in Style as follows and it works fine

     <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
        <DataGrid.Resources>
            <Style TargetType="{x:Type DataGridCell}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type DataGridCell}">
                            <Grid Background="{TemplateBinding Background}" >
                                <ContentPresenter IsEnabled="{Binding Path=IsEditable}"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.Resources>
        <DataGridTextColumn Header="A" 
                            Binding="{Binding Path=A}"/>
    </DataGrid>

You can do this with a simpler data template.

<DataGrid.Resources>
    <DataTemplate x:Key="MyTemplate" DataType="MyRowDataType">
        <TextBox Text="{Binding Value}" IsReadOnly="{Binding IsReadOnly}" />
    </DataTemplate>
</DataGrid.Resources>

...

<DataGridTemplateColumn CellTemplate="{StaticResource MyTemplate}" />

For me, the most simple solution was to style the TextBox inside the EditingElementStyle.

<DataGridTextColumn Binding="{Binding MyValueProperty}">
    <DataGridTextColumn.EditingElementStyle>
        <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding MyReadonlyProperty}" Value="True">
                    <Setter Property="IsReadOnly" Value="True"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
Related