Ich möchte, dass der Benutzer die Zelle in den Bearbeitungsmodus versetzen und die Zeile, in der sich die Zelle befindet, mit einem einzigen Klick markieren kann. Standardmäßig ist dies ein Doppelklick.
Wie überschreibe oder implementiere ich dies?
Ich möchte, dass der Benutzer die Zelle in den Bearbeitungsmodus versetzen und die Zeile, in der sich die Zelle befindet, mit einem einzigen Klick markieren kann. Standardmäßig ist dies ein Doppelklick.
Wie überschreibe oder implementiere ich dies?
Antworten:
So habe ich dieses Problem behoben:
<DataGrid DataGridCell.Selected="DataGridCell_Selected"
ItemsSource="{Binding Source={StaticResource itemView}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Nom" Binding="{Binding Path=Name}"/>
<DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"/>
</DataGrid.Columns>
</DataGrid>
Dieses DataGrid ist an eine CollectionViewSource (mit Dummy- Personenobjekten ) gebunden .
Dort passiert die Magie: DataGridCell.Selected = "DataGridCell_Selected" .
Ich hänge einfach das ausgewählte Ereignis der DataGrid-Zelle ein und rufe BeginEdit () im DataGrid auf.
Hier ist der Code für den Event-Handler:
private void DataGridCell_Selected(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
}
}
SelectionUnit
Zeilenproblem umgehen, indem Sie die Eigenschaft im DataGrid auf festlegen Cell
.
grd.BeginEdit(e)
, möchte ich, dass die TextBox in dieser Zelle den Fokus hat. Wie kann ich das machen? Ich habe versucht, FindName("txtBox")
sowohl DataGridCell als auch DataGrid aufzurufen, aber es gibt für mich null zurück.
Die Antwort von Micael Bergeron war ein guter Anfang für mich, um eine Lösung zu finden, die für mich funktioniert. Um die Bearbeitung mit einem Klick auch für Zellen in derselben Zeile zu ermöglichen, die sich bereits im Bearbeitungsmodus befindet, musste ich sie etwas anpassen. Die Verwendung von SelectionUnit Cell war für mich keine Option.
Anstatt das DataGridCell.Selected-Ereignis zu verwenden, das nur zum ersten Mal ausgelöst wird, wenn auf die Zelle einer Zeile geklickt wird, habe ich das DataGridCell.GotFocus-Ereignis verwendet.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Wenn Sie dies tun, haben Sie immer die richtige Zelle fokussiert und im Bearbeitungsmodus, aber es wird keine Steuerung in der Zelle fokussiert. Dies habe ich so gelöst
private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
if (control != null)
{
control.Focus();
}
}
}
private T GetFirstChildByType<T>(DependencyObject prop) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(prop); i++)
{
DependencyObject child = VisualTreeHelper.GetChild((prop), i) as DependencyObject;
if (child == null)
continue;
T castedProp = child as T;
if (castedProp != null)
return castedProp;
castedProp = GetFirstChildByType<T>(child);
if (castedProp != null)
return castedProp;
}
return null;
}
Von: http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing
XAML:
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type dg:DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
CODE-HINTER:
//
// SINGLE CLICK EDITING
//
private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
{
if (!cell.IsFocused)
{
cell.Focus();
}
DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
if (dataGrid != null)
{
if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
{
if (!cell.IsSelected)
cell.IsSelected = true;
}
else
{
DataGridRow row = FindVisualParent<DataGridRow>(cell);
if (row != null && !row.IsSelected)
{
row.IsSelected = true;
}
}
}
}
}
static T FindVisualParent<T>(UIElement element) where T : UIElement
{
UIElement parent = element;
while (parent != null)
{
T correctlyTyped = parent as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
Die Lösung von http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing hat bei mir hervorragend funktioniert, aber ich habe sie für jedes DataGrid mithilfe eines in einem ResourceDictionary definierten Stils aktiviert. Um Handler in Ressourcenwörterbüchern zu verwenden, müssen Sie eine CodeBehind-Datei hinzufügen. So geht's:
Dies ist ein DataGridStyles.xaml- Ressourcenwörterbuch:
<ResourceDictionary x:Class="YourNamespace.DataGridStyles"
x:ClassModifier="public"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="DataGrid">
<!-- Your DataGrid style definition goes here -->
<!-- Cell style -->
<Setter Property="CellStyle">
<Setter.Value>
<Style TargetType="DataGridCell">
<!-- Your DataGrid Cell style definition goes here -->
<!-- Single Click Editing -->
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="DataGridCell_PreviewMouseLeftButtonDown" />
</Style>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Beachten Sie das Attribut x: Class im Stammelement. Erstellen Sie eine Klassendatei. In diesem Beispiel wäre es DataGridStyles.xaml.cs . Geben Sie diesen Code ein:
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
namespace YourNamespace
{
partial class DataGridStyles : ResourceDictionary
{
public DataGridStyles()
{
InitializeComponent();
}
// The code from the myermian's answer goes here.
}
Ich bevorzuge diesen Weg basierend auf dem Vorschlag von Dušan Knežević. du klickst auf das war's))
<DataGrid.Resources>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsReadOnly"
Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="IsEditing"
Value="True" />
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
Ich habe es gelöst, indem ich einen Trigger hinzugefügt habe, der die IsEditing-Eigenschaft der DataGridCell auf True setzt, wenn sich die Maus darüber befindet. Es hat die meisten meiner Probleme gelöst. Es funktioniert auch mit Comboboxen.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Ich suche nach einer Bearbeitungszelle mit einem Klick in MVVM und dies ist eine andere Möglichkeit, dies zu tun.
Verhalten in xaml hinzufügen
<UserControl xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:myBehavior="clr-namespace:My.Namespace.To.Behavior">
<DataGrid>
<i:Interaction.Behaviors>
<myBehavior:EditCellOnSingleClickBehavior/>
</i:Interaction.Behaviors>
</DataGrid>
</UserControl>
Die EditCellOnSingleClickBehavior-Klasse erweitert System.Windows.Interactivity.Behavior.
public class EditCellOnSingleClick : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.LoadingRow += this.OnLoadingRow;
this.AssociatedObject.UnloadingRow += this.OnUnloading;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.LoadingRow -= this.OnLoadingRow;
this.AssociatedObject.UnloadingRow -= this.OnUnloading;
}
private void OnLoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.GotFocus += this.OnGotFocus;
}
private void OnUnloading(object sender, DataGridRowEventArgs e)
{
e.Row.GotFocus -= this.OnGotFocus;
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
this.AssociatedObject.BeginEdit(e);
}
}
Voila!
Es gibt zwei Probleme mit der Antwort von user2134678. Einer ist sehr gering und hat keine funktionelle Wirkung. Der andere ist ziemlich bedeutsam.
Das erste Problem ist, dass der GotFocus tatsächlich gegen das DataGrid aufgerufen wird, in der Praxis nicht gegen das DataGridCell. Das DataGridCell-Qualifikationsmerkmal in der XAML ist redundant.
Das Hauptproblem, das ich bei der Antwort festgestellt habe, ist, dass das Verhalten der Eingabetaste fehlerhaft ist. Die Eingabetaste sollte Sie im normalen DataGrid-Verhalten zur nächsten Zelle unterhalb der aktuellen Zelle bewegen. Was jedoch tatsächlich hinter den Kulissen passiert, ist, dass das GotFocus-Ereignis zweimal aufgerufen wird. Einmal, wenn die aktuelle Zelle den Fokus verliert und einmal, wenn die neue Zelle den Fokus gewinnt. Solange BeginEdit für diese erste Zelle aufgerufen wird, wird die nächste Zelle niemals aktiviert. Das Ergebnis ist, dass Sie mit einem Klick bearbeiten können, aber jeder, der nicht buchstäblich auf das Raster klickt, wird belästigt, und ein Benutzeroberflächendesigner sollte nicht davon ausgehen, dass alle Benutzer Mäuse verwenden. (Tastaturbenutzer können dies mithilfe von Tab umgehen, aber das bedeutet immer noch, dass sie durch Reifen springen, die sie nicht benötigen sollten.)
Also die Lösung für dieses Problem? Behandeln Sie das Ereignis KeyDown für die Zelle. Wenn der Schlüssel die Eingabetaste ist, setzen Sie ein Flag, das verhindert, dass BeginEdit in der ersten Zelle ausgelöst wird. Jetzt verhält sich die Eingabetaste wie es sollte.
Fügen Sie Ihrem DataGrid zunächst den folgenden Stil hinzu:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Wenden Sie diesen Stil auf die Eigenschaft "CellStyle" der Spalten an, für die Sie einen Klick aktivieren möchten.
Dann haben Sie im Code dahinter Folgendes in Ihrem GotFocus-Handler (beachten Sie, dass ich hier VB verwende, weil dies unser "One-Click-Data-Grid-Requesting" -Client als Entwicklungssprache wollte):
Private _endEditing As Boolean = False
Private Sub DataGrid_GotFocus(ByVal sender As Object, ByVal e As RoutedEventArgs)
If Me._endEditing Then
Me._endEditing = False
Return
End If
Dim cell = TryCast(e.OriginalSource, DataGridCell)
If cell Is Nothing Then
Return
End If
If cell.IsReadOnly Then
Return
End If
DirectCast(sender, DataGrid).BeginEdit(e)
.
.
.
Anschließend fügen Sie Ihren Handler für das KeyDown-Ereignis hinzu:
Private Sub DataGridCell_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.Key = Key.Enter Then
Me._endEditing = True
End If
End Sub
Jetzt haben Sie ein DataGrid, das kein grundlegendes Verhalten der sofort einsatzbereiten Implementierung geändert hat und dennoch die Bearbeitung mit nur einem Klick unterstützt.
Ich weiß, dass ich etwas spät zur Party komme, aber ich hatte das gleiche Problem und fand eine andere Lösung:
public class DataGridTextBoxColumn : DataGridBoundColumn
{
public DataGridTextBoxColumn():base()
{
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
throw new NotImplementedException("Should not be used.");
}
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var control = new TextBox();
control.Style = (Style)Application.Current.TryFindResource("textBoxStyle");
control.FontSize = 14;
control.VerticalContentAlignment = VerticalAlignment.Center;
BindingOperations.SetBinding(control, TextBox.TextProperty, Binding);
control.IsReadOnly = IsReadOnly;
return control;
}
}
<DataGrid Grid.Row="1" x:Name="exportData" Margin="15" VerticalAlignment="Stretch" ItemsSource="{Binding CSVExportData}" Style="{StaticResource dataGridStyle}">
<DataGrid.Columns >
<local:DataGridTextBoxColumn Header="Sample ID" Binding="{Binding SampleID}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Analysis Date" Binding="{Binding Date}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Test" Binding="{Binding Test}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Comment" Binding="{Binding Comment}"></local:DataGridTextBoxColumn>
</DataGrid.Columns>
</DataGrid>
Wie Sie sehen, habe ich meine eigene DataGridTextColumn geschrieben, die alles von der DataGridBoundColumn erbt. Durch Überschreiben der GenerateElement-Methode und Zurückgeben eines Textbox-Steuerelements wird die Methode zum Generieren des Bearbeitungselements niemals aufgerufen. In einem anderen Projekt habe ich damit eine Datepicker-Spalte implementiert, sodass dies auch für Kontrollkästchen und Comboboxen funktionieren sollte.
Dies scheint keinen Einfluss auf das restliche Verhalten der Datagrids zu haben. Zumindest habe ich bisher keine Nebenwirkungen bemerkt und auch keine negativen Rückmeldungen erhalten.
Eine einfache Lösung, wenn Sie damit einverstanden sind, dass Ihre Zelle ein Textfeld bleibt (keine Unterscheidung zwischen Bearbeitungsmodus und Nichtbearbeitungsmodus). Auf diese Weise funktioniert die Bearbeitung mit einem Klick sofort. Dies funktioniert auch mit anderen Elementen wie Combobox und Schaltflächen. Verwenden Sie andernfalls die Lösung unter dem Update.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Ich habe alles ausprobiert, was ich hier und bei Google gefunden habe, und sogar versucht, meine eigenen Versionen zu erstellen. Aber jede Antwort / Lösung funktionierte hauptsächlich für Textfeldspalten, funktionierte jedoch nicht mit allen anderen Elementen (Kontrollkästchen, Kombinationsfelder, Schaltflächenspalten) oder brach sogar diese anderen Elementspalten oder hatte andere Nebenwirkungen. Vielen Dank an Microsoft, dass Datagrid sich so hässlich verhält und uns zwingt, diese Hacks zu erstellen. Aus diesem Grund habe ich beschlossen, eine Version zu erstellen, die mit einem Stil direkt auf eine Textfeldspalte angewendet werden kann, ohne andere Spalten zu beeinflussen.
Ich habe diese Lösung und die Antwort von @ my verwendet und sie so geändert, dass sie ein angehängtes Verhalten sind. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Fügen Sie diesen Stil hinzu. Das BasedOn
ist wichtig , wenn Sie ein paar verwenden ausgefallene Styles für Ihre Datagrid und Sie nicht sie wollen zu verlieren.
<Window.Resources>
<Style x:Key="SingleClickEditStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="local:DataGridTextBoxSingleClickEditBehavior.Enable" Value="True" />
</Style>
</Window.Resources>
Wenden Sie den Stil mit CellStyle
auf jeden Ihrer DataGridTextColumns
wie folgt an:
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
Fügen Sie diese Klasse jetzt demselben Namespace wie Ihrem MainViewModel hinzu (oder einem anderen Namespace. Dann müssen Sie jedoch ein anderes Namespace-Präfix als verwenden local
). Willkommen in der hässlichen Code-Welt der angehängten Verhaltensweisen.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace YourMainViewModelNameSpace
{
public static class DataGridTextBoxSingleClickEditBehavior
{
public static readonly DependencyProperty EnableProperty = DependencyProperty.RegisterAttached(
"Enable",
typeof(bool),
typeof(DataGridTextBoxSingleClickEditBehavior),
new FrameworkPropertyMetadata(false, OnEnableChanged));
public static bool GetEnable(FrameworkElement frameworkElement)
{
return (bool) frameworkElement.GetValue(EnableProperty);
}
public static void SetEnable(FrameworkElement frameworkElement, bool value)
{
frameworkElement.SetValue(EnableProperty, value);
}
private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DataGridCell dataGridCell)
dataGridCell.PreviewMouseLeftButtonDown += DataGridCell_PreviewMouseLeftButtonDown;
}
private static void DataGridCell_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
EditCell(sender as DataGridCell, e);
}
private static void EditCell(DataGridCell dataGridCell, RoutedEventArgs e)
{
if (dataGridCell == null || dataGridCell.IsEditing || dataGridCell.IsReadOnly)
return;
if (dataGridCell.IsFocused == false)
dataGridCell.Focus();
var dataGrid = FindVisualParent<DataGrid>(dataGridCell);
dataGrid?.BeginEdit(e);
}
private static T FindVisualParent<T>(UIElement element) where T : UIElement
{
var parent = VisualTreeHelper.GetParent(element) as UIElement;
while (parent != null)
{
if (parent is T parentWithCorrectType)
return parentWithCorrectType;
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
}
}
<DataGridComboBoxColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="cal:Message.Attach"
Value="[Event MouseLeftButtonUp] = [Action ReachThisMethod($source)]"/>
</Style>
</DataGridComboBoxColumn.CellStyle>
public void ReachThisMethod(object sender)
{
((System.Windows.Controls.DataGridCell)(sender)).IsEditing = true;
}