Pergunta

Entendo que você pode tornar o DataGrid inteiro ou uma coluna inteira somente pronto (IsReadOnly = true).No entanto, no nível da célula, esta propriedade está apenas pronta.Mas eu preciso desse nível de granularidade.Há um blog sobre como adicionar IsReadOnly a uma linha alterando o código-fonte nos velhos tempos, quando o DataGrid era de domínio público, mas agora não tenho o código-fonte do DataGrid.Qual é a solução alternativa?

Desativar a célula (IsEnabled = false) quase atende à minha necessidade.Mas o problema é que você não consegue nem clicar na célula desativada para selecionar a linha (tenho o modo de seleção de linha completa).

EDITAR:Como ninguém respondeu a essa pergunta, acho que não é uma solução fácil.Aqui está uma solução possível:Torne a célula não editável.O único problema é que clicar na célula não seleciona a linha.Acabei de notar que o evento MouseDown ou MouseUp do DataGrid ainda é acionado quando a célula desabilitada é clicada.Neste manipulador de eventos, se eu conseguisse descobrir a linha em que ele clicou, poderia selecionar a linha programaticamente.No entanto, não consegui descobrir como encontrar a linha subjacente de DataGrid.InputHitTest.Alguém pode me dar alguma dica?

Foi útil?

Solução

Eu encontrei o mesmo problema, a célula deve ser somente leitura em algumas linhas, mas não nos outros. Aqui está uma solução alternativa:

A idéia é mudar dinamicamente o CellEditingTemplate Entre dois modelos, um é o mesmo que o no CellTemplate, o outro é para edição. Isso faz com que o modo de edição age exatamente o mesmo que a célula sem edição, embora esteja no modo de edição.

A seguir, é apresentado algum código de amostra para fazer isso, observe que essa abordagem requer DataGridTemplateColumn:

Primeiro, defina dois modelos para células somente leitura e edição:

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

Em seguida, defina um modelo de dados com adicional ContentPresenter camada e uso Trigger Para mudar o ContentTemplate do ContentPresenter, então os dois modelos acima podem ser alterados dinamicamente pelo IsEditable vinculativo:

<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

Outras dicas

Depois de muita pesquisa e experimentação usando o ISTABSTOP = FALSE e FOCUSLABLE = FALSE FUNCIONA MELHORES PARA MIM.

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

Existe uma propriedade em DataGridCell.IsReadOnly que você pode pensar que pode se ligar,
por exemplo, usando xaml como este:

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

Infelizmente, isso não funcionará porque essa propriedade não é gravada.
Em seguida, você pode tentar interceptar e interromper os eventos do mouse, mas isso não impedirá que o usuário digite o modo de edição usando a tecla F2.

A maneira como eu abriu isso foi ouvindo o PreviewExecutedEvent no datagrid e, em seguida, sinalizando -o condicionalmente como tratado.
Por exemplo, adicionando código semelhante a esse construtor da minha janela ou UserControl (ou outro local mais adequado):

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

Ao fazer assim, as células ainda são focadas e selecionáveis.
Mas o usuário não poderá inserir o modo de edição, a menos que seus itens de modelo permitam a linha fornecida.
E você não sofrerá os custos ou complexidades de desempenho usando o DataGridTemplateColumn.

Eu resolvi esse problema em meu aplicativo, configurando o objeto subjacente na célula (por exemplo, caixa de seleção) - ishittestVisible = false; Foco = false;

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

"Row" é um datagridrow. IshittestVisible = false, significa que você não pode clicar/selecionar/manipular o objeto subjacente via mouse, mas ainda pode selecionar o DataGridCell. Focusable = false, significa que você não pode selecionar/manipular o objeto subjacente com o teclado. Isso dá a ilusão de uma célula leitura, mas você ainda pode selecionar a célula e tenho certeza SelectionMode = Fullrow Em seguida, clicar na célula "Lead Somente" selecionará a linha inteira.

Minha solução é usar a ligação ao datagridTemplatecolumn com o conversor.

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

e o conversor:

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

Com base no comentário @Sohum, aqui você pode usar a versão simplificada da resposta marcada como resposta.

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

Você pode usá -lo mais tarde como comportamento da propriedade anexada.

Uma maneira de obter células de texto selecionáveis ​​e somente leitura para DataGrid é usar modelo e estilo como este:

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

E para back-end CS:

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

Isso é um pouco tarde, mas eu também estava investigando isso, essas soluções funcionam bem, mas eu precisava de algo um pouco diferente, fiz o seguinte e funciona exatamente como eu queria e o que a pergunta está procurando.

Essencialmente, eu queria entrar no modo de edição para a célula e ter todos esses outros modelos e comandar a lógica da mesma forma, sem conseguir editar a célula.

A solução para tudo isso é definir a propriedade TextBox.

<Style TargetType="DataGridCell">
    <Setter Property="TextBox.IsReadOnly" Value="True"/>
    <EventSetter Event="PreviewKeyDown" Handler="cell_PreviewKeyDown"/>
</Style>

e o seguinte código para trás para interromper a edição inicial

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

Espero que isso seja útil.

No meu caso, eu estava usando DataGridTextColumn. Defino a propriedade IseNabled no ContentPresenter em estilo da seguinte maneira e funciona bem -

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

Você pode fazer isso com um modelo de dados mais simples.

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

...

<DataGridTemplateColumn CellTemplate="{StaticResource MyTemplate}" />
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top