如何使 WPF DataGridCell 只读?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3843331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:11:14  来源:igfitidea点击:

How to make WPF DataGridCell ReadOnly?

wpfreadonlydatagridcell

提问by newman

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?

我知道您可以使整个 DataGrid 或一整列 readyonly (IsReadOnly = true)。但是,在单元级别,此属性仅准备就绪。但我确实需要这种级别的粒度。在过去,DataGrid 是公共领域时,有一篇关于通过更改源代码将 IsReadOnly 添加到一行的博客,但现在我没有 DataGrid 的源代码。有什么解决办法?

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).

禁用单元格(IsEnabled=false)几乎可以满足我的需要。但问题是您甚至无法单击禁用的单元格来选择行(我有全行选择模式)。

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?

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

采纳答案by Recle

I've encountered the same problem, the cell should be read-only in some rows but not in the others. Here is a workaround solution:

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

The idea is to dynamically switch the CellEditingTemplatebetween two templates, one is the same as the one in the CellTemplate, the other is for editing. This makes the edit mode acts exactly the same as the non-editing cell although it is in edit mode.

思路是动态切换CellEditingTemplate两个模板,一个和里面的一样CellTemplate,另一个是编辑。这使得编辑模式的行为与非编辑单元格完全相同,尽管它处于编辑模式。

The following is some sample code for doing this, notice that this approach requires DataGridTemplateColumn:

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

First, define two templates for read-only and editing cells:

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

<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>

Then define a data template with additional ContentPresenterlayer and use Triggerto switch the ContentTemplateof the ContentPresenter, so the above two templates can be switched dynamically by the IsEditablebinding:

然后定义一个带附加ContentPresenter层的数据模板,Trigger用于切换ContentTemplateContentPresenter,这样就可以通过IsEditable绑定动态切换上述两个模板:

<DataGridTemplateColumn CellTemplate="{StaticResource ReadonlyCellTemplate}">
  <DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
      <!-- the additional layer of content presenter -->
      <ContentPresenter x:Name="Presenter" Content="{Binding}" ContentTemplate="{StaticResource ReadonlyCellTemplate}" />
      <DataTemplate.Triggers>
        <!-- dynamically switch the content template by IsEditable binding -->
        <DataTrigger Binding="{Binding IsEditable}" Value="True">
          <Setter TargetName="Presenter" Property="ContentTemplate" Value="{StaticResource EditableCellTemplate}" />
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>
  </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

HTH

HTH

回答by apc

After much searching and experimentation using IsTabStop = False and Focusable = False works best for me.

在使用 IsTabStop = False 和 Focusable = False 进行大量搜索和实验后,对我来说效果最好。

<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>

回答by SteffenSH

There is a property on DataGridCell.IsReadOnlythat you might think you can bind to,
e.g. using XAML like this:

有一个DataGridCell.IsReadOnly您可能认为可以绑定的属性,
例如使用这样的 XAML:

<!-- Won't work -->
<DataGrid Name="myDataGrid" ItemsSource="{Binding MyItems}">
    <DataGrid.Resources>
        <Style TargetType="DataGridCell">
            <Setter Property="IsReadOnly" Value="{Binding MyIsReadOnly}" />
        </Style>
    </DataGrid.Resources>
    <!-- Column definitions... -->
</DataGrid>

Unfortunantly this won't work because this property is not writable.
Next you might attempt to intercept and stop mouse events, but this won't prevent the user from entering edit mode using the F2 key.

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

The way I sloved this was by listening for the PreviewExecutedEventon the DataGrid and then conditionally flagging it as handled.
E.g. by adding code similar to this to the constructor of my Window or UserControl (or another more suitable place):

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

myDataGrid.AddHandler(CommandManager.PreviewExecutedEvent,
    (ExecutedRoutedEventHandler)((sender, args) =>
{
    if (args.Command == DataGrid.BeginEditCommand)
    {
        DataGrid dataGrid = (DataGrid) sender;
        DependencyObject focusScope = FocusManager.GetFocusScope(dataGrid);
        FrameworkElement focusedElement = (FrameworkElement) FocusManager.GetFocusedElement(focusScope);
        MyRowItemModel model = (MyRowItemModel) focusedElement.DataContext;
        if (model.MyIsReadOnly)
        {
            args.Handled = true;
        }
    }
}));

By doing it like this the cells are still focusable and selectable.
But the user will not be able to enter edit mode unless your model items allow it for the given row.
And you will not suffer the performance costs or complexities by using the DataGridTemplateColumn.

通过这样做,单元格仍然是可聚焦和可选择的。
但是用户将无法进入编辑模式,除非您的模型项目允许给定行。
并且您不会因为使用 DataGridTemplateColumn 而遭受性能成本或复杂性的影响。

回答by Marko

I've solved this problem in my application by setting the underlying object in the cell (eg. CheckBox) - IsHitTestVisible = false; Focusable = false;

我通过在单元格中设置底层对象(例如 CheckBox) - IsHitTestVisible = false; 在我的应用程序中解决了这个问题;可聚焦 = 假;

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

"row" is a DataGridRow. IsHitTestVisible=false, means that you cant click/select/manipulate the underlying object via mouse, but you can still select the DataGridCell. Focusable=false, means that you can't select/manipulate the underlying object with the keyboard. This gives the illusion of a ReadOnly cell, but you can still select the cell and I'm sure if the DataGrid is set up to SelectionMode=FullRowthen clicking the "read only" cell will select the entire row.

“行”是一个 DataGridRow。IsHitTestVisible=false,表示您无法通过鼠标单击/选择/操作底层对象,但您仍然可以选择DataGridCell。Focusable=false,表示您无法使用键盘选择/操作底层对象。这给人一种只读单元格的错觉,但您仍然可以选择该单元格,并且我确定 DataGrid 是否设置为SelectionMode=FullRow然后单击“只读”单元格将选择整行。

回答by Mateusz My?lak

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

基于@sohum 评论,在这里您可以使用标记为答案的响应的简化版本。

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.

您可以稍后将其用作附加属性行为。

回答by Yechiel

My solution is to use binding to the DataGridTemplateColumn with converter.

我的解决方案是使用转换器绑定到 DataGridTemplateColumn。

<UserControl.Resources>
    <c:isReadOnlyConverter x:Key="isRead"/>
</UserControl.Resources>

   <DataGridTemplateColumn x:Name="exampleTemplate" Header="example:" Width="120" IsReadOnly="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                            <CheckBox x:Name="exampleCheckBox" VerticalAlignment="Center" IsEnabled="{Binding ElementName=exmpleTemplate, Path=IsReadOnly, Converter={StaticResource isRead}}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

and the converter:

和转换器:

class isReadOnlyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        try
        {
            return !(bool)value;
        }
        catch (Exception)
        {
            return false;
        }
    }

回答by Bilal Bashir

This is a bit late but, I was looking into this as well, these solutions work well but I needed something a little different, I did the following and it works exactly like I wanted and what the question is looking for.

这有点晚了,但是,我也在研究这个问题,这些解决方案运行良好,但我需要一些不同的东西,我做了以下操作,它的工作方式完全符合我的要求以及问题所在。

I essentially I wanted to be able to enter edit mode for the cell and have all that other templates and command logic the same while not being able to edit the cell.

我基本上希望能够进入单元格的编辑模式,并使所有其他模板和命令逻辑相同,而不能编辑单元格。

The solution for all this is to set the TextBox.IsReadOnly property to true in the DataGridCell Style and to handle the initial keydown event

所有这些的解决方案是在 DataGridCell 样式中将 TextBox.IsReadOnly 属性设置为 true 并处理初始 keydown 事件

<Style TargetType="DataGridCell">
    <Setter Property="TextBox.IsReadOnly" Value="True"/>
    <EventSetter Event="PreviewKeyDown" Handler="cell_PreviewKeyDown"/>
</Style>

and the following code behind to stop the initial edit

和后面的代码停止初始编辑

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;
    }
}

Hopefully this is helpful.

希望这是有帮助的。

回答by Ppp

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

就我而言,我使用的是 DataGridTextColumn。我按如下方式在 ContentPresenter 上设置了 IsEnabled 属性,它工作正常 -

     <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>

回答by Denise Skidmore

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}" />

回答by Evalds Urtans

One way of getting selectable, read-only text cells for DataGrid is to use template and style like this:

为 DataGrid 获取可选的只读文本单元格的一种方法是使用模板和样式,如下所示:

<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>

And for CS backend:

对于 CS 后端:

private void DataGrid_TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        (sender as TextBox).SelectAll();
    }