2010-10-01 19 views
9

Ich verstehe, dass Sie das gesamte DataGrid oder eine ganze Spalte bereit machen können (IsReadOnly = true). Auf Zellenebene ist diese Eigenschaft jedoch nur bereit. Aber ich brauche diese Granularität. Es gibt einen Blog zum Hinzufügen von IsReadOnly zu einer Zeile, indem der Quellcode in früheren Tagen geändert wurde, als DataGrid eine öffentliche Domäne war, aber jetzt habe ich keinen Quellcode für DataGrid. Was ist eine Problemumgehung?Wie mache ich WPF DataGridCell ReadOnly?

Die Deaktivierung der Zelle (IsEnabled = false) entspricht fast meinem Bedarf. Das Problem ist jedoch, dass Sie nicht einmal auf die deaktivierte Zelle klicken können, um die Zeile auszuwählen (ich habe den vollständigen Zeilenauswahlmodus).

EDIT: Da niemand auf diese Frage geantwortet hat, denke ich, es ist keine einfache Lösung. Hier ist ein möglicher Workaround: Machen Sie die Zelle nicht editierbar. Das einzige Problem ist, dass beim Klicken auf die Zelle die Zeile nicht ausgewählt wird. Mir ist gerade aufgefallen, dass das MouseDown- oder MouseUp-Ereignis des DataGrid weiterhin ausgelöst wird, wenn auf die deaktivierte Zelle geklickt wird. Wenn ich in diesem Ereignishandler die angeklickte Zeile herausfinden könnte, könnte ich die Zeile programmgesteuert auswählen. Ich konnte jedoch nicht herausfinden, wie die zugrunde liegende Zeile von DataGrid.InputHitTest zu finden ist. Kann mir bitte jemand ein Trinkgeld geben?

Antwort

9

Ich habe das gleiche Problem, die Zelle sollte in einigen Zeilen schreibgeschützt sein, aber nicht in den anderen. Hier ist eine Umgehungslösung:

Die Idee ist, die CellEditingTemplate zwischen zwei Vorlagen dynamisch zu wechseln, eine ist die gleiche wie die in der CellTemplate, die andere ist für die Bearbeitung. Dadurch verhält sich der Bearbeitungsmodus genau wie die nicht bearbeitbare Zelle, obwohl er sich im Bearbeitungsmodus befindet.

Hier finden Sie einige Beispiel-Code, dies zu tun, beachten Sie, dass dieser Ansatz erfordert DataGridTemplateColumn:

Zunächst definieren zwei Vorlagen für schreibgeschützte und Bearbeiten von Zellen:

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

Dann eine Datenvorlage definieren mit zusätzlichen ContentPresenter Schicht und Trigger verwenden, um die ContentTemplate der ContentPresenter zu wechseln, so dass die oben genannten zwei Vorlagen dynamisch durch die IsEditable Bindung:

geschaltet werden können
<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, das war tatsächlich was ich auch gekommen bin. Das einzige, was ich vermisse, ist, dass ich im Allgemeinen schreibgeschützte Zellen in einer anderen Farbe zeigen möchte. Also, ich akzeptiere deins. Ich hoffe, dass Microsoft diese Eigenschaft auf Zellenebene in der nächsten Version einführen könnte. – newman

+0

@Recre Was ist der Quelldatentyp für die Gitterbindung?Falls Sie Datentabelle verwenden, um den DataView-Typ als DataGrid-ItemSource zu füllen, und dann in DataTable auf Value-Ebene zugreifen möchten, die über die Eigenschaften IsEditable und MyCellValue verfügt, wie wird Ihre Lösung aussehen? Danke – stenly

+0

@Recle In meinem Fall habe ich die Sammlung von Grid-Quellen und Grids dynamisch auf der Quelle (in ScrollViewer - innerhalb der DataTemplate-Eigenschaft) gefüllt. Danke für den Rat – stenly

1

Ich habe dieses Problem in meiner Anwendung gelöst, indem ich das zugrunde liegende Objekt in der Zelle (z. B. CheckBox) gesetzt habe - IsHitTestVisible = false; Fokussierbar = falsch;

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

"Zeile" ist ein DataGridRow. IsHitTestVisible = false bedeutet, dass Sie das zugrundeliegende Objekt nicht mit der Maus anklicken/auswählen/bearbeiten können, aber Sie können immer noch das DataGridCell auswählen. Focusable = false bedeutet, dass Sie das zugrunde liegende Objekt nicht mit der Tastatur auswählen/manipulieren können. Dies gibt die Illusion einer ReadOnly-Zelle, aber Sie können immer noch die Zelle auswählen und ich bin sicher, wenn das DataGrid auf SelectionMode = FullRow eingerichtet ist, klicken Sie dann auf die Zelle "schreibgeschützt" wird die gesamte Zeile auswählen.

+0

Das scheint eine gute Idee zu sein. Basierend auf meinen Tests funktioniert Ihr Vorschlag jedoch nur für DataGridCheckBoxColumn, nicht jedoch für DataGridTextColumn und DataGridComboBoxColumn. Irgendeine Idee, wie man es repariert? – newman

7

Es ist eine Eigenschaft auf DataGridCell.IsReadOnly, die Sie vielleicht denken Sie binden können,
z.B. mit XAML wie folgt:

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

Leider wird dies nicht funktionieren, weil diese Eigenschaft nicht beschreibbar ist.
Als nächstes könnten Sie versuchen, Mausereignisse abzufangen und zu stoppen, aber dies hindert den Benutzer nicht daran, mit der Taste F2 in den Bearbeitungsmodus zu gelangen.

Die Art, wie ich das geliebt habe, war, indem ich auf das PreviewExecutedEvent auf dem DataGrid lauschte und es dann bedingungslos als behandelt markierte.
Zum Beispiel durch Hinzufügen von Code ähnlich wie diese an den Konstruktor von meinem Fenster oder Usercontrol (oder einem anderen geeigneteren Ort):

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

Durch es, wie dies zu tun sind die Zellen noch fokussierbar und wählbar.
Der Benutzer kann jedoch nicht in den Bearbeitungsmodus wechseln, es sei denn, Ihre Modellelemente erlauben dies für die angegebene Zeile.
Und Sie werden die Leistungskosten oder Komplexität nicht durch die DataGridTemplateColumn leiden.

+2

Schöne Lösung. Sie können auch spezifische Spalten-Zeilen-Kombinationen mit diesem Code lesen, indem Sie * dataGrid.CurrentCell.Column * betrachten. –

+1

Ich endete damit, außer dass ich das Event Event 'StartingEdit' auf' DataGrid' benutzte, das mir die 'Row' und 'Column' in den Argumenten gibt, so dass ich es nicht herausfinden musste. – sohum

+0

Ich wünschte wirklich, ich könnte Ihnen +100 dafür geben. Ich habe alle anderen Vorschläge durchgearbeitet, die ich gefunden habe, aber sie haben alle Nebenwirkungen mit sich gebracht. Das war genau das, was ich brauchte !!!! Kudos !!!! – MegaMark

0

Ein Weg, um wählbar, schreibgeschützte Textzellen für Datagrid ist wie diese Vorlage und Stil zu verwenden:

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

Und für CS-Backend:

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

Meine Lösung ist die Bindung an die DataGridTemplateColumn mit Konverter zu verwenden.

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

und der Wandler:

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

Das ist ein bisschen spät, aber ich wurde in diese Suche und arbeiten diese Lösungen gut, aber ich brauchte etwas ein wenig anders, ich habe die folgenden Hinweise und es funktioniert genau so, wie ich es wollte und wonach die Frage sucht.

Ich im Wesentlichen wollte ich in der Lage sein, Bearbeitungsmodus für die Zelle zu betreten und alle anderen Vorlagen und Befehlslogik die gleiche, während nicht in der Lage, die Zelle zu bearbeiten.

Die Lösung für all dies ist die TextBox.IsReadOnly Eigenschaft auf true in der Datagridcell Stil festlegen und dem anfänglichen keydown Ereignis

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

und den folgenden Code hinter behandeln die anfängliche bearbeiten

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

Hoffentlich ist das hilfreich.

5

Nach viel Suchen und Experimentieren mit IsTabStop = False und Focusable = False funktioniert am besten für mich.

<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 ... das ist die sauberste Lösung, die ich gefunden habe! – Enhakiel