2010-10-01 3 views
9

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

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

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

ответ

9

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

Идея состоит в том, чтобы динамически переключать 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> 

НТН

+0

Рекл, на самом деле это то, что я придумал. Единственное, что я пропустил, это то, что в целом я хотел бы показывать ячейки только для чтения в другом цвете. Итак, я принимаю твое. Я надеюсь, что Microsoft может представить это свойство уровня ячейки в следующей версии. – newman

+0

@Recle, каков ваш исходный тип данных для привязки сетки?В случае, если вы используете datatable для заполнения типа DataView как DataGrid ItemSource, а затем вы хотите получить доступ к объекту на уровне значения в DataTable, который имеет свойства IsEditable и MyCellValue, как будет выглядеть ваше решение? Спасибо – stenly

+0

@Recle В моем случае у меня есть коллекция источников сетки, и сетки динамически заполняются на основе источника (в ScrollViewer - в свойстве DataTemplate). Спасибо за внимание – stenly

1

Я решил эту проблему в своем приложении, установив базовый объект в ячейке (например, 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, тогда щелчок по ячейке «только для чтения» выберет всю строку.

+0

Это, кажется, хорошая идея. Однако, основываясь на моем тестировании, ваше предложение работает только для DataGridCheckBoxColumn, но не для DataGridTextColumn и DataGridComboBoxColumn. Любая идея, как это исправить? – newman

7

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

То, как я это делал, это прослушивание PreviewExecutedEvent на DataGrid, а затем условное обозначение его как обрабатываемого.
E.g. добавив код, подобный этому конструктору моего окна или 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

Хорошее решение. Вы также можете делать определенные комбинации столбцов и строк с помощью этого кода, просматривая * dataGrid.CurrentCell.Column *. –

+1

Я закончил это, за исключением того, что я использовал событие события «BeginningEdit» в «DataGrid», которое дает мне строки «Row» и «Column» в args, поэтому мне не приходилось это выяснять. – sohum

+0

Мне очень жаль, что я не смог дать вам +100. Я проработал все другие предложения, которые я нашел, но все они сопровождались побочными эффектами. Это было ТОЧНО, что мне было нужно !!!! Престижность !!!! – MegaMark

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

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

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

решения для все это установить свойство TextBox.IsReadOnly истину в 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; 
    } 
} 

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

5

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

oh ... это самое чистое решение, которое я нашел! – Enhakiel

Смежные вопросы