Hoe kan ik een enkele klik aankruisvak selecteren in WPF DataGrid?

Ik heb een DataGrid met de eerste kolom als tekstkolom en de tweede kolom als CheckBox-kolom. Wat ik wil is, als ik op het selectievakje klik. Het moet worden gecontroleerd.

Maar er zijn twee klikken nodig om geselecteerd te worden, voor de eerste klik wordt de cel geselecteerd, voor de tweede keer klikken wordt het selectievakje aangevinkt. Hoe u het selectievakje kunt aan-/uitvinken met een enkele klik.

Ik gebruik WPF 4.0. Kolommen in de DataGrid worden automatisch gegenereerd.


Antwoord 1, autoriteit 100%

Voor een DataGrid-aanvinkvakje met één klik, kunt u gewoon een gewoon selectievakje in DataGridTemplateColumnplaatsen en UpdateSourceTrigger=PropertyChangedinstellen.

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

Antwoord 2, autoriteit 33%

Ik heb dit opgelost met de volgende stijl:

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

Het is natuurlijk mogelijk om dit verder aan te passen voor specifieke kolommen …


Antwoord 3, autoriteit 15%

Ten eerste, ik weet dat dit een vrij oude vraag is, maar ik dacht toch dat ik het zou proberen te beantwoorden.

Ik had een paar dagen geleden hetzelfde probleem en kwam er een verrassend korte oplossing voor tegen (zie deze blog). In principe hoeft u alleen de DataGridCheckBoxColumn-definitie in uw XAML te vervangen door het volgende:

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

Het voordeel van deze oplossing is duidelijk: het is alleen voor XAML; dus het weerhoudt je er effectief van om je achterliggende code te belasten met extra UI-logica.


Antwoord 4, autoriteit 9%

Om Konstantin Salavatovs antwoordte laten werken met AutoGenerateColumns, voegt u een gebeurtenishandler toe aan de DataGrid‘s AutoGeneratingColumnmet de volgende code:

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

Hierdoor worden alle automatisch gegenereerde selectievakjes van DataGridmet één klik bewerkbaar.


Antwoord 5, autoriteit 9%

Gebaseerd op blog waarnaar wordt verwezen in het antwoord van Goblin, maar aangepast om te werken in .NET 4.0 en met rijselectiemodus.

Merk op dat het ook het bewerken van DataGridComboBoxColumn versnelt – door naar de bewerkingsmodus te gaan en een vervolgkeuzelijst weer te geven met een enkele klik of tekstinvoer.

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

Antwoord 6, autoriteit 5%

Ik heb deze suggesties geprobeerd, en vele andere die ik op andere sites heb gevonden, maar geen enkele werkte echt voor mij. Uiteindelijk heb ik de volgende oplossing gemaakt.

Ik heb mijn eigen door DataGrid geërfde besturingselement gemaakt en heb er simpelweg deze code aan toegevoegd:

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

Wat doet dit allemaal?

Tja, elke keer dat we op een cel in ons DataGrid klikken, zien we of de cel een CheckBox-besturingselement bevat. Als het doet, dan zullen we de focus op die CheckBox zetten en de waarde ervan veranderen.

Dit lijkt voor mij te werken en is een mooie, gemakkelijk herbruikbare oplossing.

Het is echter teleurstellend dat we nodigcode moeten schrijven om dit te doen. De verklaring dat de eerste muisklik (op de CheckBox van een DataGrid) wordt “genegeerd” omdat WPF deze gebruikt om de rij in de bewerkingsmodus te zetten, klinkt misschien logisch, maar in de echte wereld gaat dit in tegen de manier waarop elke echte toepassing werkt.

Als een gebruiker een selectievakje op zijn scherm ziet, moet hij er één keer op kunnen klikken om het aan of uit te vinken. Einde verhaal.


Antwoord 7, autoriteit 4%

Er is hier een veel eenvoudigere oplossing.

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

Als u DataGridCheckBoxColumngebruikt om te implementeren, is de eerste klik om te focussen, de tweede klik om te controleren.

Maar het gebruik van DataGridTemplateColumnom te implementeren heeft slechts één klik nodig.

Het verschil tussen het gebruik van DataGridComboboxBoxColumnen de implementatie door DataGridTemplateColumnis ook vergelijkbaar.


Antwoord 8, autoriteit 4%

Gebaseerd op Jim Adornoantwoord en commentaar op zijn bericht, dit is een oplossing met 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>

Antwoord 9, autoriteit 4%

Ik heb dit opgelost:

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

Het selectievakje is actief bij een enkele klik!


Antwoord 10, autoriteit 4%

Nog een andere eenvoudige oplossing is om deze stijl aan uw DataGridColumn toe te voegen. De hoofdtekst van uw stijl mag leeg zijn.

<DataGridCheckBoxColumn>
     <DataGridCheckBoxColumn.ElementStyle>
          <Style TargetType="CheckBox">
           </Style>
     </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

Antwoord 11

<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

Other episodes