如何在WPF DataGrid中执行单击复选框选择?

问题描述 投票:0回答:13

我有一个 DataGrid,第一列作为文本列,第二列作为复选框列。我想要的是,如果我单击复选框。应该检查一下。

但是,需要两次单击才能被选中,第一次单击将选中单元格,第二次单击将选中复选框。如何通过单击使复选框选中/取消选中。

我正在使用 WPF 4.0。 DataGrid 中的列是自动生成的。

wpf datagrid wpfdatagrid
13个回答
231
投票

对于单击 DataGrid 复选框,您只需将常规复选框控件放入

DataGridTemplateColumn
中并设置
UpdateSourceTrigger=PropertyChanged

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

69
投票

我用以下风格解决了这个问题:

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

当然可以针对特定列进一步调整...


35
投票

首先,我知道这是一个很老的问题,但我仍然认为我会尝试回答它。

几天前我遇到了同样的问题,并发现了一个令人惊讶的简短解决方案(请参阅此博客)。基本上,您需要做的就是将 XAML 中的

DataGridCheckBoxColumn
定义替换为以下内容:

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

此解决方案的优点是显而易见的 - 它仅适用于 XAML;因此,它有效地避免了额外的 UI 逻辑给隐藏代码带来的负担。


23
投票

要使 Konstantin Salavatov 的答案

AutoGenerateColumns
一起使用,请使用以下代码将事件处理程序添加到
DataGrid
AutoGeneratingColumn

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

这将使所有

DataGrid
自动生成的复选框列都可以“单击”编辑。


19
投票

基于 Goblin 的答案中引用的博客,但经过修改以在 .NET 4.0 和行选择模式下工作。

请注意,它还可以通过进入编辑模式并在单击或文本输入时显示下拉菜单来加快 DataGridComboBoxColumn 编辑速度。

XAML:

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

后台代码:

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

13
投票

另一个简单的解决方案是将此样式添加到 DataGridColumn 中。样式的主体可以为空。

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

12
投票

这里有一个更简单的解决方案。

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

如果使用

DataGridCheckBoxColumn
来实现,第一次点击是聚焦,第二次点击是检查。

但使用

DataGridTemplateColumn
来实现只需要一键点击即可。

使用

DataGridComboboxBoxColumn
和通过
DataGridTemplateColumn
实现的区别也类似。


11
投票

基于 Jim Adorno 对他的帖子的回答和评论,这是

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>

10
投票

我已经尝试过这些建议,以及我在其他网站上找到的很多其他建议,但没有一个对我有用。 最后,我创建了以下解决方案。

我创建了自己的 DataGrid 继承控件,并简单地向其中添加了以下代码:

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

这一切有什么作用?

嗯,每次我们单击 DataGrid 中的任何单元格时,我们都会查看该单元格中是否包含 CheckBox 控件。 如果它,那么我们将把焦点设置到该复选框并切换它的值

这似乎对我有用,并且是一个很好的、易于重复使用的解决方案。

令人失望的是,我们需要编写代码来做到这一点。 第一次鼠标单击(在 DataGrid 的复选框上)被“忽略”,因为 WPF 使用它将该行置于编辑模式,这一解释可能听起来合乎逻辑,但在现实世界中,这与每个实际应用程序的工作方式背道而驰。

如果用户在屏幕上看到一个复选框,他们应该能够单击一次来勾选/取消勾选它。 故事结束。


8
投票

我用这个解决了:

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

单击即可激活复选框!


1
投票
<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
投票

这是一种具有自己的列类的方法,该方法基于默认的 DataGridCheckBoxColumn 类,并且可以像普通方法一样使用。只需复制/粘贴即可。

public class DataGridCheckBoxColumn : System.Windows.Controls.DataGridCheckBoxColumn
{
    private static Style _noFocusEditElementStyle;

    static DataGridCheckBoxColumn()
    {
        ElementStyleProperty.OverrideMetadata(typeof(DataGridCheckBoxColumn), new FrameworkPropertyMetadata(NoFocusEditElementStyle));
        EditingElementStyleProperty.OverrideMetadata(typeof(DataGridCheckBoxColumn), new FrameworkPropertyMetadata(NoFocusEditElementStyle));
    }


    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);

        if (e.Property.Name == nameof(IsReadOnly))
        {
            ElementStyle = IsReadOnly ? DefaultElementStyle : NoFocusEditElementStyle;
            EditingElementStyle = IsReadOnly ? DefaultElementStyle : NoFocusEditElementStyle;
        }
    }

    public static Style NoFocusEditElementStyle
    {
        get
        {
            if (_noFocusEditElementStyle == null)
            {
                Style style = new Style(typeof(System.Windows.Controls.CheckBox));

                // When not in edit mode, the end-user should not be able to toggle the state
                style.Setters.Add(new Setter(UIElement.FocusableProperty, false));
                style.Setters.Add(new Setter(System.Windows.Controls.CheckBox.HorizontalAlignmentProperty, HorizontalAlignment.Center));
                style.Setters.Add(new Setter(System.Windows.Controls.CheckBox.VerticalAlignmentProperty, VerticalAlignment.Top));

                style.Seal();
                _noFocusEditElementStyle = style;
            }

            return _noFocusEditElementStyle;
        }
    }
}

与读/写属性一起使用:

<myNamespace:DataGridCheckBoxColumn Header="Name"
                    Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

与只读属性一起使用:

<myNamespace:DataGridCheckBoxColumn Header="Name"
                    IsReadOnly="True"
                    Binding="{Binding Name, Mode=OneWay}" />

说明:

  • 默认的列类有两个样式属性,一个用于编辑模式,一个用于视图。
  • 在只读的情况下,我们在两种情况下都使用视图样式
  • 另一种情况是在编辑模式下我们设置编辑样式
  • 我更喜欢复选框没有获得焦点的样式(NoFocusEditElementStyle),而不是 EditElementStyle,因为这种行为看起来有点奇怪

0
投票

我在代码后面这样做:

    private void DataGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        DataGridCell obj = (DataGridCell)Keyboard.FocusedElement;
        if (obj == null) { return; }
        if (obj.Content == null) { return; }
        if (obj.Content.GetType() == typeof(CheckBox))
        {
            CheckBox cb = (CheckBox)obj.Content;
            Point x = Mouse.GetPosition(cb);
            if (x.X < cb.ActualWidth && x.Y < cb.ActualHeight)
            {
                cb.IsChecked = (bool)cb.IsChecked ? false : true;
            }
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.