web-dev-qa-db-fra.com

Modification en un seul clic dans WPF DataGrid

Je veux que l'utilisateur puisse mettre la cellule en mode édition et mettre en surbrillance la ligne dans laquelle la cellule est contenue en un seul clic. Par défaut, il s'agit d'un double clic.

Comment remplacer ou implémenter cela?

86
Austin

Voici comment j'ai résolu ce problème:

<DataGrid DataGridCell.Selected="DataGrid_GotFocus" 
          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é à une CollectionViewSource (contenant des objets factices Person).

La magie s'y produit: DataGridCell.Selected = "DataGrid_GotFocus".

Je raccorde simplement l'événement sélectionné de la cellule DataGrid et j'appelle BeginEdit () sur le DataGrid.

Voici le code du gestionnaire d'événements:

private void DataGrid_GotFocus(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);
    }
}
72
Micael Bergeron

La réponse de Micael Bergeron a été un bon début pour moi de trouver une solution qui fonctionne pour moi. Pour permettre l'édition en un seul clic également pour les cellules de la même ligne, c'est déjà en mode édition, j'ai dû l'ajuster un peu. L'utilisation de 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 où la cellule d'une ligne est cliquée, 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;
}
38
user2134678

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>

DERRIÈRE LE CODE:

//
// 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;
}
8
m-y

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 ResourceDictionary. Pour utiliser des gestionnaires dans les dictionnaires de ressources, vous devez y ajouter un fichier code-behind. Voici comment procéder:

Il s'agit d'un DataGridStyles.xaml dictionnaire de ressources:

<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.
}
6
Andikki

Je cherche à éditer une cellule sur simple clic dans MVVM et c'est une autre façon de le faire.

  1. Ajout d'un comportement dans 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>
    
  2. 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!

2
Jouan Antoine

je préfère cette façon basée sur la suggestion de Dušan Knežević. vous cliquez sur 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>
2
René Hankel

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

<Style TargetType="DataGridCell">
     <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
             <Setter Property="IsEditing" Value="True" />
         </Trigger>
     </Style.Triggers>
 </Style>
1
Dušan Knežević

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

Le premier problème est que le GotFocus est en fait appelé contre le DataGrid, pas le DataGridCell dans la 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é. La touche Entrée doit vous déplacer vers la cellule suivante sous la cellule actuelle dans le 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 que la cellule actuelle perd le focus, et une fois que la nouvelle cellule gagne le focus. 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 gêné, et un concepteur d'interface utilisateur ne devrait pas supposer que tous les utilisateurs utilisent des souris. (Les utilisateurs de clavier peuvent en quelque sorte se déplacer 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 néanmoins en charge l'édition en un seul clic.

1
GrantA

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 héritant de tout le DataGridBoundColumn. En remplaçant la méthode GenerateElement et en renvoyant un contrôle Textbox 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é cela pour implémenter une colonne Datepicker, donc cela devrait également fonctionner pour les cases à cocher et les zones de liste déroulante.

Cela ne semble pas avoir d'incidence sur le reste des comportements des datagrids .. Au moins, je n'ai pas remarqué d'effets secondaires ni reçu de commentaires négatifs jusqu'à présent.

0
Troillius