Je veux que l'utilisateur puisse mettre la cellule en mode d'édition et mettre en évidence la ligne dans laquelle la cellule est contenue en un seul clic. Par défaut, il s'agit d'un double clic.
Comment puis-je remplacer ou mettre en œuvre cela?
Je veux que l'utilisateur puisse mettre la cellule en mode d'édition et mettre en évidence la ligne dans laquelle la cellule est contenue en un seul clic. Par défaut, il s'agit d'un double clic.
Comment puis-je remplacer ou mettre en œuvre cela?
Réponses:
Voici comment j'ai résolu ce problème:
<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>
Ce DataGrid est lié à un CollectionViewSource (contenant des objets Personne factice ).
La magie opère ici: DataGridCell.Selected = "DataGridCell_Selected" .
J'accroche simplement l'événement sélectionné de la cellule DataGrid et j'appelle BeginEdit () sur le DataGrid.
Voici le code derrière le gestionnaire d'événements:
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
propriété sur le DataGrid sur Cell
.
grd.BeginEdit(e)
, je veux que la zone de texte de cette cellule ait le focus. Comment puis je faire ça? J'ai essayé d'appeler FindName("txtBox")
à la fois le DataGridCell et le DataGrid, mais il renvoie null pour moi.
La réponse de Micael Bergeron a été un bon début pour moi pour trouver une solution qui fonctionne pour moi. Pour permettre l'édition en un seul clic également pour les cellules de la même ligne qui était déjà en mode d'édition, j'ai dû l'ajuster un peu. Utiliser SelectionUnit Cell n'était pas une option pour moi.
Au lieu d'utiliser l'événement DataGridCell.Selected qui n'est déclenché que pour la première fois que l'utilisateur clique sur la cellule d'une ligne, j'ai utilisé l'événement DataGridCell.GotFocus.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Si vous le faites, vous aurez toujours la bonne cellule focalisée et en mode édition, mais aucun contrôle dans la cellule ne sera focalisé, ce que j'ai résolu comme ceci
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;
}
De: 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-DERRIÈRE:
//
// 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;
}
La solution de http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing a très bien fonctionné pour moi, mais je l'ai activée pour chaque DataGrid en utilisant un style défini dans un ResourceDictionary. Pour utiliser des gestionnaires dans des dictionnaires de ressources, vous devez y ajouter un fichier code-behind. Voici comment procéder:
Ceci est un dictionnaire de ressources DataGridStyles.xaml :
<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>
Notez l'attribut x: Class dans l'élément racine. Créez un fichier de classe. Dans cet exemple, ce serait DataGridStyles.xaml.cs . Mettez ce code à l'intérieur:
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.
}
je préfère cette méthode basée sur la suggestion de Dušan Knežević. vous cliquez et c'est tout))
<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>
Je l'ai résolu en ajoutant un déclencheur qui définit la propriété IsEditing de DataGridCell sur True lorsque la souris est dessus. Cela a résolu la plupart de mes problèmes. Cela fonctionne aussi avec les comboboxes.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Je recherche une cellule d'édition en un seul clic dans MVVM et c'est une autre façon de le faire.
Ajout d'un comportement en xaml
<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>
La classe EditCellOnSingleClickBehavior étend 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!
Il y a deux problèmes avec la réponse de user2134678. L'un est très mineur et n'a aucun effet fonctionnel. L'autre est assez significatif.
Le premier problème est que le GotFocus est en fait appelé par rapport au DataGrid, et non au DataGridCell en pratique. Le qualificatif DataGridCell dans le XAML est redondant.
Le principal problème que j'ai trouvé avec la réponse est que le comportement de la touche Entrée est cassé. Entrée devrait vous déplacer vers la cellule suivante sous la cellule actuelle dans un comportement DataGrid normal. Cependant, ce qui se passe réellement dans les coulisses, c'est que l'événement GotFocus sera appelé deux fois. Une fois sur la cellule actuelle perdant la focalisation, et une fois sur la nouvelle cellule gagnant la concentration. Mais tant que BeginEdit est appelé sur cette première cellule, la cellule suivante ne sera jamais activée. Le résultat est que vous avez une modification en un clic, mais quiconque ne clique pas littéralement sur la grille sera incommodé, et un concepteur d'interface utilisateur ne doit pas supposer que tous les utilisateurs utilisent des souris. (Les utilisateurs de clavier peuvent en quelque sorte le contourner en utilisant Tab, mais cela signifie toujours qu'ils sautent à travers des cerceaux dont ils ne devraient pas avoir besoin.)
Alors, la solution à ce problème? Gérez l'événement KeyDown pour la cellule et si la clé est la clé Entrée, définissez un indicateur qui empêche BeginEdit de se déclencher sur la première cellule. Maintenant, la touche Entrée se comporte comme il se doit.
Pour commencer, ajoutez le style suivant à votre DataGrid:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Appliquez ce style à la propriété "CellStyle" des colonnes pour lesquelles vous souhaitez activer en un clic.
Ensuite, dans le code derrière vous avez ce qui suit dans votre gestionnaire GotFocus (notez que j'utilise VB ici parce que c'est ce que notre client "demande de grille de données en un clic" voulait comme langage de développement):
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)
.
.
.
Ensuite, vous ajoutez votre gestionnaire pour l'événement KeyDown:
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
Vous disposez maintenant d'un DataGrid qui n'a modifié aucun comportement fondamental de l'implémentation prête à l'emploi et qui prend en charge l'édition en un seul clic.
Je sais que je suis un peu en retard à la fête mais j'ai eu le même problème et j'ai trouvé une solution différente:
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>
Comme vous pouvez le voir, j'ai écrit mon propre DataGridTextColumn en héritant de tout ce qui est le DataGridBoundColumn. En remplaçant la méthode GenerateElement et en retournant un contrôle Textbox juste là, la méthode de génération de l'élément d'édition n'est jamais appelée. Dans un autre projet, j'ai utilisé ceci pour implémenter une colonne Datepicker, donc cela devrait également fonctionner pour les cases à cocher et les comboboxes.
Cela ne semble pas avoir d'impact sur le reste des comportements des datagrids. Au moins, je n'ai pas remarqué d'effets secondaires ni de commentaires négatifs jusqu'à présent.
Une solution simple si vous êtes d'accord que votre cellule reste une zone de texte (pas de distinction entre le mode édition et non-édition). De cette façon, l'édition en un seul clic fonctionne immédiatement. Cela fonctionne également avec d'autres éléments comme la liste déroulante et les boutons. Sinon, utilisez la solution ci-dessous la mise à jour.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
J'ai essayé tout ce que j'ai trouvé ici et sur Google et j'ai même essayé de créer mes propres versions. Mais chaque réponse / solution fonctionnait principalement pour les colonnes de zone de texte mais ne fonctionnait pas avec tous les autres éléments (cases à cocher, comboboxes, colonnes de boutons), ou même cassait ces autres colonnes d'éléments ou avait d'autres effets secondaires. Merci Microsoft d'avoir fait en sorte que datagrid se comporte de cette manière moche et nous force à créer ces hacks. Pour cette raison, j'ai décidé de créer une version qui peut être appliquée avec un style à une colonne de zone de texte directement sans affecter les autres colonnes.
J'ai utilisé cette solution et la réponse de @ my et les ai modifiées pour qu'elles soient un comportement attaché. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Ajoutez ce style. Le BasedOn
est important lorsque vous utilisez des styles sophistiqués pour votre grille de données et que vous ne voulez pas les perdre.
<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>
Appliquez le style avec CellStyle
à chacun de vos DataGridTextColumns
comme ceci:
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
Et maintenant, ajoutez cette classe au même espace de noms que votre MainViewModel (ou à un autre espace de noms. Mais vous devrez alors utiliser un autre préfixe d'espace de noms que local
). Bienvenue dans le monde du code passe-partout laid des comportements attachés.
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;
}