如何使WPF DataGridCell成为只读?

如何使WPF DataGridCell成为只读?,wpf,readonly,datagridcell,Wpf,Readonly,Datagridcell,我知道您可以轻松地创建整个DataGrid或整个列(IsReadOnly=true)。但是,在单元格级别,此属性仅可用。但我确实需要这种粒度。有一个博客是关于在DataGrid是公共域的旧时代通过更改源代码将IsReadOnly添加到一行的,但现在我没有DataGrid的源代码。解决办法是什么 使单元格禁用(IsEnabled=false)几乎满足了我的需要。但问题是,您甚至不能单击禁用的单元格来选择行(我有完整的行选择模式) 编辑:因为没有人回答这个问题,所以我想这不是一个容易解决的问题。这里

我知道您可以轻松地创建整个DataGrid或整个列(IsReadOnly=true)。但是,在单元格级别,此属性仅可用。但我确实需要这种粒度。有一个博客是关于在DataGrid是公共域的旧时代通过更改源代码将IsReadOnly添加到一行的,但现在我没有DataGrid的源代码。解决办法是什么

使单元格禁用(IsEnabled=false)几乎满足了我的需要。但问题是,您甚至不能单击禁用的单元格来选择行(我有完整的行选择模式)


编辑:因为没有人回答这个问题,所以我想这不是一个容易解决的问题。这里有一个可能的解决方法:使单元格不可编辑。唯一的问题是单击单元格不会选择行。我刚刚注意到,当单击禁用的单元格时,DataGrid的MouseDown或MouseUp事件仍然会被触发。在这个事件处理程序中,如果我能够找出它单击的行,我就可以通过编程方式选择该行。但是,我不知道如何从
DataGrid.InputHitTest
中找到底层行。有人能给我一些提示吗?

在我的应用程序中,我通过在单元格中设置基础对象(例如复选框)-ishitestvisible=false解决了这个问题;聚焦=假

var cb = this.dataGrid.Columns[1].GetCellContent(row) as CheckBox;
cb.IsHitTestVisible = false;
cb.Focusable = false;

“行”是DataGridRow。IshittesVisible=false,表示不能通过鼠标单击/选择/操作基础对象,但仍可以选择DataGridCell。Focusable=false,表示无法使用键盘选择/操作底层对象。这给人一种只读单元格的错觉,但您仍然可以选择该单元格,我确信如果DataGrid设置为SelectionMode=FullRow,那么单击“只读”单元格将选择整行。

我在应用程序中通过在单元格中设置基础对象(例如复选框)-IshittesVisible=false解决了这个问题;聚焦=假

var cb = this.dataGrid.Columns[1].GetCellContent(row) as CheckBox;
cb.IsHitTestVisible = false;
cb.Focusable = false;

“行”是DataGridRow。IshittesVisible=false,表示不能通过鼠标单击/选择/操作基础对象,但仍可以选择DataGridCell。Focusable=false,表示无法使用键盘选择/操作底层对象。这给人一种只读单元格的错觉,但您仍然可以选择该单元格,我确信如果DataGrid设置为SelectionMode=FullRow,那么单击“只读”单元格将选择整行。

我遇到了同样的问题,该单元格在某些行中应为只读,而在其他行中则不应为只读。以下是一个变通解决方案:

其思想是在两个模板之间动态切换
CellEditingTemplate
,一个与
CellTemplate
中的模板相同,另一个用于编辑。这使得编辑模式与非编辑单元的行为完全相同,尽管它处于编辑模式

下面是执行此操作的一些示例代码,请注意,此方法需要
DataGridTemplateColumn

首先,为只读单元格和编辑单元格定义两个模板:

<DataGrid>
  <DataGrid.Resources>
    <!-- the non-editing cell -->
    <DataTemplate x:Key="ReadonlyCellTemplate">
      <TextBlock Text="{Binding MyCellValue}" />
    </DataTemplate>

    <!-- the editing cell -->
    <DataTemplate x:Key="EditableCellTemplate">
      <TextBox Text="{Binding MyCellValue}" />
    </DataTemplate>
  </DataGrid.Resources>
</DataGrid>

HTH

我遇到了同样的问题,单元格在某些行中应该是只读的,但在其他行中不应该是只读的。以下是一个变通解决方案:

其思想是在两个模板之间动态切换
CellEditingTemplate
,一个与
CellTemplate
中的模板相同,另一个用于编辑。这使得编辑模式与非编辑单元的行为完全相同,尽管它处于编辑模式

下面是执行此操作的一些示例代码,请注意,此方法需要
DataGridTemplateColumn

首先,为只读单元格和编辑单元格定义两个模板:

<DataGrid>
  <DataGrid.Resources>
    <!-- the non-editing cell -->
    <DataTemplate x:Key="ReadonlyCellTemplate">
      <TextBlock Text="{Binding MyCellValue}" />
    </DataTemplate>

    <!-- the editing cell -->
    <DataTemplate x:Key="EditableCellTemplate">
      <TextBox Text="{Binding MyCellValue}" />
    </DataTemplate>
  </DataGrid.Resources>
</DataGrid>

HTH

DataGridCell.IsReadOnly上有一个属性,您可能认为可以绑定到该属性,
e、 g.像这样使用XAML:

<DataGrid>
<DataGrid.CellStyle>
    <Style TargetType="{x:Type DataGridCell}">                                        
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridCell}">
                    <Border Padding="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                         <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        <TextBox BorderThickness="0" MouseDoubleClick="DataGrid_TextBox_MouseDoubleClick" IsReadOnly="True" Padding="5" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.CellStyle>

不幸的是,这将无法工作,因为此属性不可写。
接下来,您可能会尝试拦截和停止鼠标事件,但这不会阻止用户使用F2键进入编辑模式

我解决这个问题的方法是在DataGrid上侦听
PreviewExecutedEvent
,然后有条件地将其标记为已处理。
例如,通过向my Window或UserControl(或其他更合适的位置)的构造函数添加类似的代码:

myDataGrid.AddHandler(CommandManager.PreviewExecutedEvent,
(ExecutedRoutedEventHandler)((发送方,参数)=>
{
if(args.Command==DataGrid.BeginEditCommand)
{
DataGrid DataGrid=(DataGrid)发送方;
DependencyObject focusScope=FocusManager.GetFocusScope(数据网格);
FrameworkElement focusedElement=(FrameworkElement)FocusManager.GetFocusedElement(focusScope);
MyRowItemModel=(MyRowItemModel)focusedElement.DataContext;
if(仅适用于型号MyIsReadOnly)
{
args.Handled=true;
}
}
}));
通过这样做,单元格仍然可以聚焦和选择。
但用户将无法进入编辑模式,除非您的模型项允许该模式用于给定行。

使用DataGridTemplateColumn不会带来性能成本或复杂性。

DataGridCell.IsReadOnly上有一个您可能认为可以绑定的属性,
e、 g.像这样使用XAML:

<DataGrid>
<DataGrid.CellStyle>
    <Style TargetType="{x:Type DataGridCell}">                                        
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridCell}">
                    <Border Padding="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                         <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        <TextBox BorderThickness="0" MouseDoubleClick="DataGrid_TextBox_MouseDoubleClick" IsReadOnly="True" Padding="5" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.CellStyle>

不幸的是,这将无法工作,因为此属性不可写。
接下来,您可能会尝试拦截和停止鼠标事件,但这不会阻止用户使用F2键进入编辑模式

我解决这个问题的方法是在DataGrid上侦听
PreviewExecutedEvent
,然后有条件地将其标记为已处理。
例如,通过向my Window或UserControl(或其他更合适的位置)的构造函数添加类似的代码:

myDataGrid.AddHandler(CommandManager.PreviewExecutedEvent
class isReadOnlyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        try
        {
            return !(bool)value;
        }
        catch (Exception)
        {
            return false;
        }
    }
<Style TargetType="DataGridCell">
    <Setter Property="TextBox.IsReadOnly" Value="True"/>
    <EventSetter Event="PreviewKeyDown" Handler="cell_PreviewKeyDown"/>
</Style>
protected void cell_PreviewKeyDown(object sender, KeyEventArgs e)
{
    DataGridCell cell = sender as DataGridCell;
    if (cell.IsEditing == false && 
        ((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control)) //So that Ctrl+C keeps working
    {
        cell.IsEditing = true;
        e.Handled = true;
    }
}
<DataGridTextColumn Header="My Column" Binding="{Binding Path=MyColumnValue}">
    <DataGridTextColumn.CellStyle>
        <Style TargetType="DataGridCell">                                    
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=ReadOnly}" Value="True">                                                    
                    <Setter Property="IsTabStop" Value="False"></Setter>
                    <Setter Property="Focusable" Value="False"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>
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;
    }
}
     <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>
<DataGrid.Resources>
    <DataTemplate x:Key="MyTemplate" DataType="MyRowDataType">
        <TextBox Text="{Binding Value}" IsReadOnly="{Binding IsReadOnly}" />
    </DataTemplate>
</DataGrid.Resources>
<DataGridTemplateColumn CellTemplate="{StaticResource MyTemplate}" />