2010-10-01 22 views
9

Entiendo que puede hacer que todo el DataGrid o una columna completa estén listos solo (IsReadOnly = true). Sin embargo, a nivel celular esta propiedad solo está lista. Pero sí necesito este nivel de granularidad. Hay un blog sobre cómo agregar IsReadOnly a una fila cambiando el código fuente en días pasados ​​cuando DataGrid era de dominio público, pero ahora no tengo el código fuente de DataGrid. ¿Qué es la solución?¿Cómo hacer WPF DataGridCell ReadOnly?

Haciendo que la celda esté deshabilitada (IsEnabled = false) casi satisface mis necesidades. Pero el problema es que ni siquiera puede hacer clic en la celda deshabilitada para seleccionar la fila (tengo el modo de selección de fila completa).

EDIT: Dado que nadie ha respondido a esta pregunta, supongo que no es una solución fácil. Aquí hay una posible solución alternativa: hacer que la celda no sea editable. El único problema es que al hacer clic en la celda no se selecciona la fila. Me acabo de dar cuenta de que el evento MouseDown o MouseUp del DataGrid todavía se activa cuando se hace clic en la celda deshabilitada. En este controlador de eventos, si pudiera averiguar la fila en la que hizo clic, podría seleccionar la fila mediante programación. Sin embargo, no pude encontrar la manera de encontrar la fila subyacente en DataGrid.InputHitTest. ¿Puede alguien darme algún consejo?

Respuesta

9

He encontrado el mismo problema, la celda debe ser de solo lectura en algunas filas pero no en las otras. Aquí hay una solución alternativa:

La idea es cambiar dinámicamente el CellEditingTemplate entre dos plantillas, una es la misma que la del CellTemplate, la otra es para editar. Esto hace que el modo de edición actúe exactamente igual que la celda sin edición aunque esté en modo de edición.

El siguiente es un ejemplo de código para hacer esto, observe que este enfoque requiere DataGridTemplateColumn:

En primer lugar, definir dos plantillas para las células de sólo lectura y edición:

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

a continuación, definir una plantilla de datos con capa adicional ContentPresenter y use Trigger para cambiar ContentTemplate del ContentPresenter, por lo que las dos plantillas anteriores se pueden cambiar dinámicamente por el enlace 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

+0

Recle, esto fue en realidad lo que yo también inventé. Lo único que echo de menos es que, en general, me gustaría mostrar celdas de solo lectura en un color diferente. Entonces, estoy aceptando el tuyo. Espero que Microsoft pueda presentar esta propiedad de nivel celular en la próxima versión. – newman

+0

@Recle ¿cuál es su tipo de datos fuente para el enlace de la red?En caso de que esté utilizando datatable para llenar el tipo de DataView como DataGrid ItemSource y luego desee acceder al objeto en el nivel de valor en DataTable, que tiene las propiedades IsEditable y MyCellValue, ¿cómo se verá su solución? Gracias – stenly

+0

@Recle En mi caso tengo la colección de fuentes de cuadrícula y las cuadrículas se rellenan dinámicamente en función de la fuente (en ScrollViewer - dentro de la propiedad DataTemplate). Gracias por su asesoramiento – stenly

1

He resuelto este problema en mi aplicación estableciendo el objeto subyacente en la celda (por ejemplo, CheckBox) - IsHitTestVisible = false; Enfocable = falso;

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

"fila" es un DataGridRow. IsHitTestVisible = false, significa que no puede hacer clic/seleccionar/manipular el objeto subyacente mediante el mouse, pero puede seleccionar DataGridCell. Enfocable = falso, significa que no puede seleccionar/manipular el objeto subyacente con el teclado. Esto da la ilusión de una celda ReadOnly, pero aún puede seleccionar la celda y estoy seguro de que si DataGrid está configurado en SelectionMode = FullRow y luego hacer clic en la celda "solo lectura" seleccionará toda la fila.

+0

Esto parece ser una buena idea. Sin embargo, según mis pruebas, su sugerencia solo funciona para DataGridCheckBoxColumn, pero no para DataGridTextColumn y DataGridComboBoxColumn. ¿Alguna de idea de cómo arreglarlo? – newman

7

Hay una propiedad en DataGridCell.IsReadOnly que se podría pensar que puede unirse a, por ejemplo,
usando XAML como esto:

<!-- 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 esto no funcionará porque esta propiedad no se puede escribir.
A continuación, puede intentar interceptar y detener los eventos del mouse, pero esto no evitará que el usuario ingrese al modo de edición con la tecla F2.

La forma en que subí esto fue escuchando el PreviewExecutedEvent en el DataGrid y luego marcándolo condicionalmente como manejado.
P. ej. mediante la adición de código similar al siguiente al constructor de mi ventana o control de usuario (o en otro lugar más adecuado):

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

Al hacerlo de esta manera las células siguen siendo enfocable y seleccionable.
Pero el usuario no podrá ingresar al modo de edición a menos que los elementos de su modelo lo permitan para la fila dada.
Y no sufrirá los costos o complejidades de rendimiento mediante el uso de DataGridTemplateColumn.

+2

Buena solución. También puede hacer combinaciones específicas de columna-fila de solo lectura con este código mirando * dataGrid.CurrentCell.Column *. –

+1

Terminé siguiendo esto, excepto que usé el evento de evento 'BeginningEdit' en' DataGrid' que me da 'Row' y' Column' en los argumentos, así que no tuve que resolverlo. – sohum

+0

Realmente me gustaría poder darte +100 por esto. He revisado todas las otras sugerencias que encontré, pero todas vienen con efectos secundarios. Esto fue EXACTAMENTE lo que necesitaba !!!! ¡¡¡¡Prestigio!!!! – MegaMark

0

Una forma de conseguir seleccionable, de sólo lectura celdas de texto para la cuadrícula de datos es el uso de la plantilla y el estilo de esta manera:

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

Y para CS backend:

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

Mi La solución es usar el enlace a DataGridTemplateColumn con el convertidor.

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

y el convertidor:

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

Esto es un poco tarde, pero, yo estaba buscando en esto así, estas soluciones funcionan bien, pero necesitaba algo un poco diferente, hice lo siguiente y funciona exactamente como yo quería y lo que la pregunta está buscando.

Básicamente, quería poder ingresar al modo de edición de la celda y tener todas las demás plantillas y lógica de comandos de la misma manera sin poder editar la celda.

La solución para todo esto es para establecer la propiedad TextBox.IsReadOnly en true en el estilo DataGridCell y para controlar el evento KeyDown inicial

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

y el siguiente código detrás de detener la edición 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 esto sea útil.

5

Después de mucha búsqueda y experimentación usando IsTabStop = False y Focusable = False funciona mejor para mí.

<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 ... ¡esta es la solución más limpia que he encontrado! – Enhakiel

Cuestiones relacionadas