2010-10-01 15 views
9

Capisco che è possibile rendere pronto l'intero DataGrid o un'intera colonna (IsReadOnly = true). Tuttavia, a livello di cella questa proprietà è solo pronta. Ma ho bisogno di questo livello di granularità. C'è un blog sull'aggiunta di IsReadOnly a una riga modificando il codice sorgente nei vecchi giorni in cui DataGrid era di dominio pubblico, ma ora non ho codice sorgente per DataGrid. Cos'è una soluzione alternativa?Come rendere WPF DataGridCell ReadOnly?

Rendere la cella disabilitata (IsEnabled = false) soddisfa quasi il mio bisogno. Ma il problema è che non puoi nemmeno cliccare sulla cella disabilitata per selezionare la riga (ho una modalità di selezione riga completa).

EDIT: Poiché nessuno ha risposto a questa domanda, quindi credo che non sia una soluzione facile. Ecco una possibile soluzione: rendere la cella non modificabile. L'unico problema è che facendo clic sulla cella non si seleziona la riga. Ho appena notato che l'evento MouseDown o MouseUp di DataGrid viene ancora attivato quando si fa clic sulla cella disabilitata. In questo gestore di eventi, se potessi capire la riga su cui ha fatto clic, potrei selezionare la riga a livello di codice. Tuttavia, non sono riuscito a capire come trovare la riga sottostante da DataGrid.InputHitTest. Qualcuno può darmi qualche consiglio?

risposta

9

Ho riscontrato lo stesso problema, la cella dovrebbe essere di sola lettura in alcune righe ma non negli altri. Ecco una soluzione alternativa:

L'idea è di passare dinamicamente il CellEditingTemplate tra due modelli, uno è lo stesso di quello nel CellTemplate, l'altro è per la modifica. Ciò rende la modalità di modifica esattamente uguale alla cella di non modifica anche se è in modalità di modifica.

Il seguente è alcuni esempi di codice per fare questo, si noti che questo approccio richiede DataGridTemplateColumn:

In primo luogo, definire due modelli per le celle di sola lettura e la modifica:

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

Poi definire un modello di dati con lo strato ContentPresenter aggiuntivo e utilizzare Trigger per passare lo ContentTemplate dello ContentPresenter, in modo che i due modelli precedenti possano essere modificati dinamicamente dal binding 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, questo è stato effettivamente quello che sono venuto anche io. L'unica cosa che mi manca è che, in generale, mi piacerebbe mostrare celle di sola lettura di un colore diverso. Quindi, sto accettando il tuo. Spero che Microsoft possa introdurre questa proprietà a livello di cella nella prossima versione. – newman

+0

@Recle qual è il tipo di dati sorgente per il collegamento a griglia?Nel caso in cui si usi datatable per riempire il tipo DataView come DataGrid ItemSource e quindi si desideri accedere all'oggetto a livello di valore in DataTable, che ha proprietà IsEditable e MyCellValue, come sarà la tua soluzione? Grazie – stenly

+0

@Recle Nel mio caso ho la raccolta di fonti di rete e le griglie sono popolate dinamicamente in base all'origine (in ScrollViewer - all'interno della proprietà DataTemplate). Grazie per il consiglio – stenly

1

Ho risolto questo problema nella mia applicazione impostando l'oggetto sottostante nella cella (ad esempio CheckBox) - IsHitTestVisible = false; Focusable = false;

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

"row" è un oggetto DataGridRow. IsHitTestVisible = false, significa che non puoi fare clic/selezionare/manipolare l'oggetto sottostante tramite il mouse, ma puoi ancora selezionare DataGridCell. Focusable = false, significa che non puoi selezionare/manipolare l'oggetto sottostante con la tastiera. Questo dà l'illusione di una cella ReadOnly, ma è ancora possibile selezionare la cella e sono sicuro che se DataGrid è impostato su SelectionMode = FullRow quindi fare clic sulla cella "sola lettura" selezionerà l'intera riga.

+0

Questa sembra essere una buona idea. Tuttavia, in base ai miei test, il tuo suggerimento funziona solo per DataGridCheckBoxColumn, ma non per DataGridTextColumn e DataGridComboBoxColumn. hai qualche idea su come aggiustarlo? – newman

7

C'è una proprietà su DataGridCell.IsReadOnly che si potrebbe pensare è possibile associare a,
esempio utilizzando XAML come questo:

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

Purtroppo questo non funzionerà perché questa proprietà non è scrivibile.
Successivamente è possibile tentare di intercettare e arrestare gli eventi del mouse, ma ciò non impedirà all'utente di accedere alla modalità di modifica utilizzando il tasto F2.

Il modo in cui l'ho spostato era ascoltando PreviewExecutedEvent sul DataGrid e quindi contrassegnandolo condizionatamente come gestito.
E.g. con l'aggiunta di codice simile a questo per il costruttore della mia finestra o UserControl (o in altro luogo più adatto):

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

Facendo in questo modo le cellule sono ancora attivabile e selezionabile.
Ma l'utente non potrà accedere alla modalità di modifica a meno che gli elementi del modello non lo consentano per la riga specificata.
E non soffrirete i costi o le complessità delle prestazioni utilizzando DataGridTemplateColumn.

+2

Bella soluzione. È anche possibile creare combinazioni di righe di colonne specifiche in modo leggibile con questo codice guardando * dataGrid.CurrentCell.Column *. –

+1

Ho finito per seguire questo, tranne che ho usato l'evento evento 'BeginningEdit' su' DataGrid' che mi da 'Row' e' Column' negli args quindi non ho dovuto capirlo. – sohum

+0

Vorrei davvero poterti dare +100 per questo. Ho esaminato tutti gli altri suggerimenti che ho trovato, ma sono venuti tutti con effetti collaterali. Questo era ESATTAMENTE quello di cui avevo bisogno !!!! Complimenti !!!! – MegaMark

0

Un modo di ottenere selezionabili, di sola lettura celle di testo per DataGrid è quello di utilizzare template e stile come questo:

<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 per CS backend:

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

mio soluzione consiste nell'utilizzare l'associazione al DataGridTemplateColumn con il convertitore.

<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 il convertitore:

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

Questo è un po 'tardi, ma, stavo cercando in questo pure, queste soluzioni funzionano bene, ma ho bisogno di qualcosa di un po' diverso, ho fatto quanto segue e funziona esattamente come volevo e qual è la domanda che sta cercando.

In sostanza, volevo essere in grado di immettere la modalità di modifica per la cella e avere tutti gli altri modelli e la stessa logica di comando senza poter modificare la cella.

La soluzione per tutto questo è quello di impostare la proprietà TextBox.IsReadOnly su true nel Style DataGridCell e per gestire l'evento keydown iniziale

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

e il seguente codice dietro per fermare la modifica iniziale

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

Speriamo che questo sia utile.

5

Dopo molte ricerche e sperimentazioni con IsTabStop = False e Focusable = False funziona meglio per me.

<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 ... questa è la soluzione più pulita che abbia trovato! – Enhakiel