Как сделать WPF DataGridCell ReadOnly? - PullRequest
11 голосов
/ 02 октября 2010

Я понимаю, что вы можете сделать всю DataGrid или весь столбец готовыми (IsReadOnly = true). Однако на уровне ячеек это свойство только готово. Но мне нужен этот уровень детализации. Есть блог о добавлении IsReadOnly в строку путем изменения исходного кода в старые времена, когда DataGrid был общественным достоянием, но сейчас у меня нет исходного кода для DataGrid. Какой обходной путь?

Отключение ячейки (IsEnabled = false) почти соответствует моим потребностям. Но проблема в том, что вы даже не можете щелкнуть отключенную ячейку, чтобы выбрать строку (у меня есть режим полного выбора строки).

РЕДАКТИРОВАТЬ: Поскольку никто не ответил на этот вопрос, поэтому я думаю, это не легко исправить. Вот возможный обходной путь: сделать ячейку недоступной для редактирования. Единственная проблема заключается в том, что нажатие на ячейку не выделяет строку. Я только что заметил, что событие MouseDown или MouseUp в DataGrid по-прежнему вызывается при нажатии на отключенную ячейку. В этом обработчике событий, если бы я мог выяснить строку, по которой он щелкнул, я мог бы выбрать строку программно. Однако я не мог понять, как найти базовую строку из DataGrid.InputHitTest. Может кто-нибудь дать мне совет?

Ответы [ 9 ]

11 голосов
/ 09 сентября 2015

После долгих поисков и экспериментов использование 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>
10 голосов
/ 19 августа 2011

Я столкнулся с той же проблемой, ячейка должна быть доступна только для чтения в некоторых строках, но не в других.Вот обходное решение:

Идея состоит в том, чтобы динамически переключать 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 ContentPresenter, поэтому два вышеуказанных шаблона можно динамически переключать с помощью привязки 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

8 голосов
/ 02 октября 2011

На 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.

То, как я решил эту проблему, было прослушиванием 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;
        }
    }
}));

Делая так, ячейки все еще могут фокусироваться и выбираться.
Но пользователь не сможет войти в режим редактирования, если элементы модели не позволяют его для данной строки.
И вы не будете страдать от затрат на производительность или сложностей при использовании DataGridTemplateColumn.

2 голосов
/ 18 ноября 2010

Я решил эту проблему в своем приложении, установив базовый объект в ячейке (например, CheckBox) - IsHitTestVisible = false; Фокусируемый = ложный;

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

«строка» - это DataGridRow. IsHitTestVisible = false, означает, что вы не можете щелкать / выбирать / манипулировать нижележащим объектом с помощью мыши, но вы все равно можете выбрать DataGridCell. Focusable = false, означает, что вы не можете выбирать / манипулировать базовым объектом с помощью клавиатуры. Это создает иллюзию ячейки ReadOnly, но вы все равно можете выбрать ячейку, и я уверен, что если DataGrid установлен на SelectionMode = FullRow , то щелчок ячейки «только для чтения» выделит всю строку .

1 голос
/ 10 августа 2018

Исходя из комментария @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;
    }
}

Вы можете использовать его позже как поведение присоединенного свойства.

1 голос
/ 10 ноября 2014

Мое решение состоит в том, чтобы использовать привязку к 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;
        }
    }
0 голосов
/ 22 сентября 2018

В моем случае я использовал DataGridTextColumn.Я установил свойство IsEnabled в ContentPresenter в стиле следующим образом, и оно отлично работает -

     <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>
0 голосов
/ 24 марта 2015

Это немного поздно, но, я тоже смотрел на это, эти решения работают хорошо, но мне нужно что-то немного другое, я сделал следующее, и это работает точно так, как я хотел, и то, что ищет вопрос. 1001 *

По сути, я хотел иметь возможность войти в режим редактирования для ячейки и иметь все те же другие шаблоны и логику команд, не имея возможности редактировать ячейку.

Решение для всего этого состоит в том, чтобы установить для свойства TextBox.IsReadOnly значение true в стиле DataGridCell и обработать начальное событие 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;
    }
}

Надеюсь, это полезно.

0 голосов
/ 21 июня 2013

Один из способов получения выбираемых текстовых ячеек, доступных только для чтения, для 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();
    }
...