如何制作WPF DataGridCell ReadOnly?

时间:2010-10-01 22:01:52

标签: wpf readonly datagridcell

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

禁用单元格(IsEnabled = false)几乎符合我的需要。但问题是你甚至无法点击禁用的单元格来选择行(我有完整的行选择模式)。

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

10 个答案:

答案 0 :(得分:10)

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

我们的想法是在两个模板之间动态切换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切换ContentTemplate的{​​{1}},以便上述两个模板可以动态切换ContentPresenter绑定:

IsEditable

HTH

答案 1 :(得分:10)

经过大量的搜索和实验,使用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 :(得分:8)

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,您不会遇到性能成本或复杂性。

答案 3 :(得分:2)

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

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

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:0)

为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();
    }

答案 7 :(得分:0)

这有点晚了但是,我也在研究这个问题,这些解决方案效果很好,但我需要一些不同的东西,我做了以下工作,它的工作方式与我想要的完全一样,问题是什么。< / p>

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

所有这一切的解决方案是在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;
    }
}

希望这很有帮助。

答案 8 :(得分:0)

就我而言,我使用的是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>

答案 9 :(得分:0)

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

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

...

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