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

时间:2010-09-30 18:12:56

标签: wpf datagrid wpfdatagrid

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

但是,它需要两次单击才能被选中,首次单击该单元格正在被选中,第二次单击该复选框将被选中。如何通过单击来选中/取消选中复选框。

我正在使用WPF 4.0。 DataGrid中的列是AutoGenerated。

11 个答案:

答案 0 :(得分:164)

对于单击DataGrid复选框,您可以将常规复选框控件放在DataGridTemplateColumn中并设置UpdateSourceTrigger=PropertyChanged

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

答案 1 :(得分:55)

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

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

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

答案 2 :(得分:20)

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

我几天前遇到了同样的问题,并遇到了一个令人惊讶的简短解决方案(见this blog)。基本上,您需要做的就是用以下内容替换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逻辑来减轻您的代码负担,并帮助您在MVVM狂热者的眼中保持您的状态;)。

答案 3 :(得分:16)

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

答案 4 :(得分:15)

要使Konstantin Salavatov's answerAutoGenerateColumns一起使用,请使用以下代码向DataGrid的{​​{1}}添加事件处理程序:

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

答案 5 :(得分:9)

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

我已创建自己的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 并切换它的值

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

虽然我们需要编写代码来执行此操作,但是令人失望。当WPF使用它将行置于编辑模式时,第一次鼠标单击(在DataGrid的CheckBox上)被“忽略”的解释可能听起来合乎逻辑,但在现实世界中,这与每个真实应用程序的工作方式不同。 / p>

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

答案 6 :(得分:8)

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

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

如果你使用DataGridCheckBoxColumn来实现,首先点击是焦点,第二次点击就是检查。

但使用DataGridTemplateColumn实现只需点击一下。

使用DataGridComboboxBoxColumnDataGridTemplateColumn实施的差异也很相似。

答案 7 :(得分:7)

我解决了这个问题:

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

单击时激活复选框!

答案 8 :(得分:6)

基于 Jim Adorno 的回答及对帖子的评论,这是Sub replaceCommas() Dim cell As Range Dim n As Long For Each cell In Selection.Cells For n = 1 To Len(cell.Value2) If cell.Characters(n, 1).Text = "," Then cell.Characters(n, 1).Text = vbLf Next n Next cell End Sub 的解决方案:

MultiTrigger

答案 9 :(得分:5)

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

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

答案 10 :(得分: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