【问题标题】:How to make WPF DataGridCell ReadOnly?如何使 WPF DataGridCell 只读?
【发布时间】:2011-04-20 02:09:56
【问题描述】:

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

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

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

【问题讨论】:

    标签: wpf readonly datagridcell


    【解决方案1】:

    经过大量搜索和实验,使用 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>
    

    【讨论】:

    • 哦……这是我找到的最干净的解决方案!
    • 很好的解决方案!
    • 这会产生问题的一个场景是:有一个包含两列的表,第一列是可编辑的,第二列是不可编辑的。当焦点位于第一列的一行时,单击第二列的另一行,您将看到焦点没有改变。新选择的行是蓝色的,但如果用户按键,之前选择的单元格将被编辑。
    • 多么优雅而简单的解决方案啊。干得好,那个人!
    【解决方案2】:

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

    思路是在两个模板之间动态切换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>
    

    然后定义一个带有额外ContentPresenter层的数据模板,并使用Trigger切换ContentPresenterContentTemplate,这样以上两个模板就可以通过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

    【讨论】:

    • Recle,这实际上也是我想出的。我唯一想念的是,一般来说,我想以不同的颜色显示只读单元格。所以,我接受你的。我希望微软可以在下一个版本中引入这个单元级属性。
    • @Recle 网格绑定的源数据类型是什么?如果您使用数据表将 DataView 类型填充为 DataGrid ItemSource,然后您想访问 DataTable 中具有 IsEditable 和 MyCellValue 属性的值级别的对象,您的解决方案将是什么样子?谢谢
    • @Recle 在我的情况下,我有网格源的集合,并且网格是根据源动态填充的(在 ScrollViewer 中 - 在 DataTemplate 属性中)。感谢您的建议
    【解决方案3】:

    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>
    

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

    我解决这个问题的方法是侦听 DataGrid 上的 PreviewExecutedEvent,然后有条件地将其标记为已处理。
    例如。通过将类似于此的代码添加到我的 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;
            }
        }
    }));
    

    通过这样做,单元格仍然可以聚焦和选择。
    但是除非您的模型项允许给定行,否则用户将无法进入编辑模式。
    使用 DataGridTemplateColumn 不会带来性能成本或复杂性。

    【讨论】:

    • 不错的解决方案。您还可以通过查看 dataGrid.CurrentCell.Column 使用此代码将特定的列-行组合设为只读。
    • 我最终遵循了这个,除了我在DataGrid 上使用了BeginningEdit 事件事件,它在参数中给了我RowColumn,所以我不必弄清楚.
    • 我真希望我能给你+100。我已经研究了我发现的所有其他建议,但它们都有副作用。这正是我需要的!!!!赞!!!!
    【解决方案4】:

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

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

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

    【讨论】:

    • 这似乎是个好主意。但是,根据我的测试,您的建议仅适用于 DataGridCheckBoxColumn,但不适用于 DataGridTextColumn 和 DataGridComboBoxColumn。知道如何解决吗?
    【解决方案5】:

    我的解决方案是使用转换器绑定到 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>
    

    和转换器:

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

    【讨论】:

      【解决方案6】:

      这有点晚了,但我也在研究这个问题,这些解决方案效果很好,但我需要一些不同的东西,我做了以下工作,它的工作原理与我想要的以及问题正在寻找什么完全一样。

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

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

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

      希望这会有所帮助。

      【讨论】:

      • 很好的解决方案!这解决了我的要求,即能够进入编辑模式以便选择和复制文本而不能更改它。我只使用了 部分,但似乎可以解决问题。
      【解决方案7】:

      基于@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;
          }
      }
      

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

      【讨论】:

        【解决方案8】:

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

        对于 CS 后端:

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

        【讨论】:

          【解决方案9】:

          就我而言,我使用的是 DataGridTextColumn。我在 Style 中的 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>
          

          【讨论】:

            【解决方案10】:

            您可以使用更简单的数据模板来做到这一点。

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

            ...

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

            【讨论】:

              【解决方案11】:

              对我来说,最简单的解决方案是在EditingElementStyle 中设置TextBox 的样式。

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-02-26
                • 1970-01-01
                • 1970-01-01
                • 2016-08-29
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多