Как сделать WPF DataGridCell ReadOnly?
-
27-09-2019 - |
Вопрос
Я понимаю, что вы можете сделать весь DataGrid или весь столбец готовым только (IsReadOnly = true).Однако, на уровне ячейки это свойство готово только.Но мне нужен такой уровень детализации.Существует блог о добавлении IsReadOnly в строку путем изменения исходного кода в старые времена, когда DataGrid был общедоступным, но сейчас у меня нет исходного кода для DataGrid.Какой обходной путь?
Отключение ячейки (IsEnabled=false) почти удовлетворяет мои потребности.Но проблема в том, что вы даже не можете щелкнуть отключенную ячейку, чтобы выбрать строку (у меня есть режим полного выбора строки).
РЕДАКТИРОВАТЬ:Поскольку никто не ответил на этот вопрос, я думаю, это непростое решение.Вот возможное решение:Сделайте ячейку нередактируемой.Единственная проблема заключается в том, что щелчок по ячейке не выделяет строку.Я только что заметил, что событие MouseDown или MouseUp DataGrid по-прежнему запускается при нажатии на отключенную ячейку.В этом обработчике событий, если бы я мог определить строку, по которой он щелкнул, я мог бы выбрать ее программно.Однако я не мог понять, как найти базовую строку из DataGrid.InputHitTest
.Может кто-нибудь, пожалуйста, дайте мне совет?
Решение
Я столкнулся с такой же проблемой, клетку следует читать только в рядах, но не в других. Вот обходное решение:
Идея - динамически переключать 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>
Емкость
Другие советы
После многих поисковых и экспериментов с использованием ISTABSTOP = FALSE и FOCETALE = 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>
Есть недвижимость на 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, а затем условно помещают его как обработанные.
Например, добавив код, аналогичный этому конструктору моего окна или 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.
Я решил эту проблему в своем приложении, установив базовый объект в ячейке (например.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 затем щелчок по ячейке «только для чтения» выделит всю строку.
Мое решение состоит в том, чтобы использовать привязку к DataGridTemPlateColumn Converter.
<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;
}
}
Основываясь на комментарии @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;
}
}
Вы можете использовать его позже в качестве прилагаемого поведения свойства.
Один из способов получения выбора, только для чтения текстовые ячейки для 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 Backend:
private void DataGrid_TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
(sender as TextBox).SelectAll();
}
Это немного поздно, но я тоже смотрел в это, эти решения хорошо работают хорошо, но мне нужно что-то немного другое, я сделал следующее, и это работает точно так же, как я хотел, и что ищет вопрос.
По сути, я хотел, чтобы иметь возможность вводить режим редактирования для ячейки и иметь все другие шаблоны и логику команды то же самое, не способные редактировать ячейку.
Решение для всего этого состоит в том, чтобы установить свойство 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;
}
}
Надеюсь, это полезно.
В моем случае я использовал dataGridtextColumn. Я установил свойство изгибаемой недвижимости на 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>
Вы можете сделать это с более простыми шаблонами данных.
<DataGrid.Resources>
<DataTemplate x:Key="MyTemplate" DataType="MyRowDataType">
<TextBox Text="{Binding Value}" IsReadOnly="{Binding IsReadOnly}" />
</DataTemplate>
</DataGrid.Resources>
...
<DataGridTemplateColumn CellTemplate="{StaticResource MyTemplate}" />