web-dev-qa-db-fra.com

Comment effectuer la sélection de case à cocher en un seul clic dans WPF DataGrid?

J'ai un DataGrid avec la première colonne en tant que colonne de texte et la deuxième colonne en tant que colonne CheckBox. Ce que je veux, c'est si je coche la case. Il devrait être vérifié.

Mais, il faut deux clics pour être sélectionné. Pour le premier clic, la cellule est sélectionnée, pour le second, la case à cocher est cochée. Comment faire la case à cocher pour être coché/décoché en un seul clic.

J'utilise WPF 4.0. Les colonnes du DataGrid sont générées automatiquement.

128
Prince Ashitaka

Pour la case à cocher DataGrid en un seul clic, vous pouvez simplement mettre un contrôle de case à cocher régulier dans DataGridTemplateColumn et définir UpdateSourceTrigger=PropertyChanged.

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>
171

J'ai résolu ceci avec le style suivant:

<Style TargetType="DataGridCell">
     <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
             <Setter Property="IsEditing" Value="True" />
         </Trigger>
     </Style.Triggers>
 </Style>

Il est bien sûr possible d’adapter cela à des colonnes spécifiques ...

56
Jim Adorno

Premièrement, je sais que la question est assez ancienne, mais je pensais toujours que j'essaierais d'y répondre.

J'ai eu le même problème il y a quelques jours et je suis tombé sur une solution étonnamment courte (voir ce blog ). En gros, tout ce que vous avez à faire est de remplacer la définition de DataGridCheckBoxColumn dans votre code XAML par ce qui suit:

<DataGridTemplateColumn Header="MyCheckBoxColumnHeader">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

L’avantage de cette solution est évident: il s’agit uniquement de XAML; ainsi, il vous évite de surcharger votre code-back avec une logique d’interface utilisateur supplémentaire et vous aide à conserver votre statut aux yeux des fanatiques de MVVM;).

21
Priidu Neemre

Basé sur le blog référencé dans la réponse de Goblin, mais modifié pour fonctionner en .NET 4.0 et avec le mode de sélection de ligne.

Notez que cela accélère également l'édition de DataGridComboBoxColumn - en entrant en mode édition et en affichant une liste déroulante en un seul clic ou en entrée de texte.

XAML:

        <Style TargetType="{x:Type DataGridCell}">
            <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
            <EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
        </Style>

Code-behind:

    private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private void DataGridCell_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private static void GridColumnFastEdit(DataGridCell cell, RoutedEventArgs e)
    {
        if (cell == null || cell.IsEditing || cell.IsReadOnly)
            return;

        DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
        if (dataGrid == null)
            return;

        if (!cell.IsFocused)
        {
            cell.Focus();
        }

        if (cell.Content is CheckBox)
        {
            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;
                }
            }
        }
        else
        {
            ComboBox cb = cell.Content as ComboBox;
            if (cb != null)
            {
                //DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                dataGrid.BeginEdit(e);
                cell.Dispatcher.Invoke(
                 DispatcherPriority.Background,
                 new Action(delegate { }));
                cb.IsDropDownOpen = true;
            }
        }
    }


    private 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;
    }
16
surfen

Pour que réponse de Konstantin Salavatov travaille avec AutoGenerateColumns, ajoutez un gestionnaire d'événements au DataGrid de AutoGeneratingColumn avec le code suivant:

if (e.Column is DataGridCheckBoxColumn && !e.Column.IsReadOnly)
{
    var checkboxFactory = new FrameworkElementFactory(typeof(CheckBox));
    checkboxFactory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
    checkboxFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
    checkboxFactory.SetBinding(ToggleButton.IsCheckedProperty, new Binding(e.PropertyName) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

    e.Column = new DataGridTemplateColumn
        {
            Header = e.Column.Header,
            CellTemplate = new DataTemplate { VisualTree = checkboxFactory },
            SortMemberPath = e.Column.SortMemberPath
        };
}

Cela rendra toutes les colonnes de case à cocher générées automatiquement de DataGrid modifiables en un seul clic.

15
Allon Guralnek

J'ai essayé ces suggestions et beaucoup d'autres que j'ai trouvées sur d'autres sites, mais aucune d'entre elles n'a vraiment fonctionné pour moi. Au final, j'ai créé la solution suivante.

J'ai créé mon propre contrôle DataGrid-inherited et y ai simplement ajouté ce code:

public class DataGridWithNavigation : Microsoft.Windows.Controls.DataGrid
{
    public DataGridWithNavigation()
    {
        EventManager.RegisterClassHandler(typeof(DataGridCell), 
            DataGridCell.PreviewMouseLeftButtonDownEvent,
            new RoutedEventHandler(this.OnPreviewMouseLeftButtonDown));
    }


    private void OnPreviewMouseLeftButtonDown(object sender, RoutedEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
        {
          DependencyObject obj = FindFirstControlInChildren(cell, "CheckBox");
            if (obj != null)
            {
                System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)obj;
                cb.Focus();
                cb.IsChecked = !cb.IsChecked;
            }
        }
    }

    public DependencyObject FindFirstControlInChildren(DependencyObject obj, string controlType)
    {
        if (obj == null)
            return null;

        // Get a list of all occurrences of a particular type of control (eg "CheckBox") 
        IEnumerable<DependencyObject> ctrls = FindInVisualTreeDown(obj, controlType);
        if (ctrls.Count() == 0)
            return null;

        return ctrls.First();
    }

    public IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, string type)
    {
        if (obj != null)
        {
            if (obj.GetType().ToString().EndsWith(type))
            {
                yield return obj;
            }

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
                {
                    if (child != null)
                    {
                        yield return child;
                    }
                }
            }
        }
        yield break;
    }
}

Qu'est-ce que tout cela fait?

Eh bien, chaque fois que nous cliquons sur une cellule de notre DataGrid, nous voyons si la cellule contient un contrôle CheckBox. Si le fait , alors nous allons définir le focus sur ce CheckBox et basculer sa valeur .

Cela semble fonctionner pour moi et constitue une solution agréable et facilement réutilisable.

Il est décevant que nous ayons besoin d’écrire du code pour le faire. L'explication selon laquelle le premier clic de souris (sur la case à cocher d'un contrôle DataGrid) est "ignorée" car WPF l'utilise pour mettre la ligne en mode Edition peut sembler logique, mais dans la réalité, cela va à l'encontre du fonctionnement de chaque application réelle.

Si un utilisateur voit une case à cocher sur son écran, il devrait pouvoir cliquer dessus une fois pour la cocher/la décocher. Fin de l'histoire.

9
Mike Gledhill

Il existe une solution beaucoup plus simple ici.

          <DataGridTemplateColumn MinWidth="20" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

Si vous utilisez DataGridCheckBoxColumn pour l'implémenter, le premier clic est pour le focus, le deuxième clic est pour vérifier.

Mais utiliser DataGridTemplateColumn pour l'implémenter nécessite un seul clic.

La différence entre DataGridComboboxBoxColumn et la mise en œuvre par DataGridTemplateColumn est également similaire.

8
Weidian Huang

J'ai résolu avec ceci:

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Viewbox Height="25">
                <CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </Viewbox>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

La case à cocher active sur simple clic!

8
Darlan Dieterich

Base sur Jim Adorno réponse et commentaires sur son post, voici la solution avec MultiTrigger:

<Style TargetType="DataGridCell">
  <Style.Triggers>
    <MultiTrigger>
      <MultiTrigger.Conditions>
    <Condition Property="IsReadOnly" Value="False" />
    <Condition Property="IsMouseOver" Value="True" />
      </MultiTrigger.Conditions>
      <Setter Property="IsEditing" Value="True" />
    </MultiTrigger>
  </Style.Triggers>
</Style>
6
Rafal Spacjer

Une autre solution simple consiste à ajouter ce style à votre DataGridColumn.Le corps de votre style peut être vide.

<DataGridCheckBoxColumn>
     <DataGridCheckBoxColumn.ElementStyle>
          <Style TargetType="CheckBox">
           </Style>
     </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
5
AmirHossein Rezaei
<Style x:Key="StilCelula" TargetType="DataGridCell"> 
<Style.Triggers>
 <Trigger Property="IsMouseOver" Value="True">
   <Setter Property="IsEditing" 
     Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
     Converter={StaticResource CheckBoxColumnToEditingConvertor}}" />
 </Trigger>
</Style.Triggers>
<Style>
Imports System.Globalization
Public Class CheckBoxColumnToEditingConvertor
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
        Try

            Return TypeOf TryCast(value, DataGridCell).Column Is DataGridCheckBoxColumn
        Catch ex As Exception
            Return Visibility.Collapsed
        End Try
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class
1
TotPeRo