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


143

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

但是,它需要两次单击才能被选中,对于第一次单击,就是选中了该单元格,对于第二次单击,就是选中了该复选框。如何通过单击使复选框处于选中状态/取消选中状态。

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


4
复制:stackoverflow.com/questions/1225836/…,但是这个标题更好
surfen 2011年

Answers:


189

对于单击DataGrid复选框,您只需将常规的复选框控件放入其中DataGridTemplateColumn并进行设置UpdateSourceTrigger=PropertyChanged

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

4
哇-我很高兴读到最后。这可以完美地工作,并且相当简单,IMO应该将此标记为答案。
Tod

2
这也适用于ComboBox。如:方式中,WAY比DataGridComboBoxColumn好。
user1454265 2014年

2
当我使用空格键来检查/取消选中和箭头移动到另一个单元格时,它不是。
Yola 2015年

1
我已经解释了您必须绑定“ IsSelected”的答案,但事实并非如此!你可以用DataGridTemplateColumn.CellTemplate自己的绑定,也将努力! @ weidian-huang 的回答帮助我理解了这一点,谢谢!
AstralisSomnium

62

我用以下样式解决了这个问题:

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

当然,可以针对特定的列对此进行进一步调整...


8
真好 我将其更改为MultiTrigger并添加了ReadOnly = False的条件,但是这种基本方法适用于我的键盘导航并不重要的简单情况。
MarcE 2011年

在使用ItemsSource时,将该样式添加到我的网格中会引发Operation异常。而是使用ItemsControl.ItemsSource访问和修改元素。
Alkampfer

1
到目前为止,这是我见过的最干净的方法!真好!(对于IsReadOnly =“ True”
MultiTrigger

2
此解决方案具有一些意外/不必要的行为。参见stackoverflow.com/q/39004317/2881450
jHilscher

2
为了使绑定
生效

27

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

几天前,我遇到了同样的问题,并且遇到了一个令人惊讶的简短解决方案(请参阅 此博客)。基本上,您所需要做的就是将DataGridCheckBoxColumnXAML中的定义替换为以下内容:

<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逻辑来负担代码返回的负担,并可以帮助您在MVVM狂热者的眼中保持状态;)。


1
这类似于康斯坦丁·萨拉瓦托夫的答案,而这个答案对我有用。+1包括他没有的代码示例。感谢您对一个老问题的好的回答。
唐·希律

1
问题在于,如果使用组合框列进行操作,则该列中的所有单元格始终会显示小下拉按钮。不只是单击单元格时。
user3690202 2015年

18

为了让康斯坦丁Salavatov的回答工作与AutoGenerateColumns,添加一个事件处理程序DataGridAutoGeneratingColumn使用下面的代码:

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的自动生成的复选框列都可以“单击”进行编辑。


感谢您填写自动生成的列方法,这很方便地指出了我合适的方向。
el2iot2 2014年

17

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

此解决方案最适合我。我绑定的ViewModel没有使用其他解决方案进行更新。
BrokeMyLegBiking 2012年

@surfen,如果我有很多包含datagrid的页面,我是否需要在每个页面及其代码中都放置上述样式和代码,是否可以在公共位置使用样式和代码而不是在其中创建每页
天使

为什么需要调度一个空的Action?
user3690202

@ user3690202就像Windows.Forms中的DoEvents。调用BeginEdit之后,您需要等待单元格真正进入编辑模式。
吉里·斯卡拉

@JiříSkála-我不记得在解决这个问题的过程中曾经需要这样做,但是我理解您在说什么-谢谢!
user3690202

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控件。如果是这样,那么我们将焦点设置到该CheckBox 并切换它的value

这似乎对我有用,并且是一个不错的,易于重用的解决方案。

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

如果用户在屏幕上看到一个复选框,则应该可以单击一次以选中/取消选中该复选框。故事结局。


1
谢谢,我尝试了很多“解决方案”,但这似乎是每次都能真正起作用的第一个解决方案。它非常适合我的应用程序体系结构。
古格

此解决方案导致更新绑定时出现问题,而此处的问题:wpf.codeplex.com/wikipage?title=Single-Click%20Editing不会。
贾斯汀·西蒙

2
太复杂。看我的答案。:)
康斯坦丁·萨拉瓦托夫

1
5年后,此代码仍然可以节省社交时间:)对于一些简单的要求,@ KonstantinSalavatov解决方案就足够了。就我而言,我将代码与Mike的解决方案混合在一起,以实现与处理程序的动态事件关联,我的网格具有动态的列数,在特定单元格中单击一次必须将更改存储在数据库中。
Fer R

8

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

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

如果您使用的DataGridCheckBoxColumn是工具,则首先单击即可确定焦点,其次单击将进行检查。

但是使用DataGridTemplateColumn工具只需单击一下即可。

使用DataGridComboboxBoxColumn和实现的区别DataGridTemplateColumn也相似。


给我很好的解释,并立即工作,谢谢!
AstralisSomnium

8

我解决了这个问题:

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

复选框在单击时处于活动状态!


2
我不需要将Checkbox包装在ViewBox中,但是这个答案对我有用。
JGeerWM '17

3
对我来说,这是比接受的答案更干净的解决方案。也不需要Viewbox。有趣的是,它比定义的Checkbox列更好地工作。
kenjara

6

根据吉姆·阿多诺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>

5

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

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

2
按空格键选中/取消选中将CheckBox从左侧移动到中间。在样式中添加<Setter Property =“ Horizo​​ntalAlignment” Value =“ Center” />将防止CheckBox移动。
YantingChen

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.