【问题标题】:Click and Drag to Multiselect Checkboxes in WPF/C#单击并拖动到 WPF/C# 中的多选复选框
【发布时间】:2012-09-24 09:21:37
【问题描述】:

问题:

我的应用程序要求用户能够通过一列复选框在数据网格中选择多个条目。所需的行为是,当您单击列中的复选框时,它的行为就像一个普通的复选框,但是如果您在鼠标左键按下时拖动它,它的选择状态将变为与之前相反的状态。

到目前为止我所做的尝试:

我已经尝试继承 CheckBox 并处理 OnMouseEnter,但单击的第一个复选框似乎捕获了鼠标,因此没有其他复选框触发 OnMouseEnter 事件。

我已经尝试实现拖放黑客,用户单击以选择一个复选框,然后将该复选框拖到其他复选框上,以便其他人收到 DragOver 事件并可以切换状态。此解决方案导致在拖放过程中光标不在另一个复选框上时显示为带有斜线的圆圈,这对于此应用程序是不可接受的。

我想要什么:

我想要一种方法来实现具有我描述的功能的复选框,理想情况下是我可以重用的 xaml 样式或子类,因为我的应用程序中的多个位置都需要此功能。

有没有优雅的方法来实现这个效果?

【问题讨论】:

    标签: c# wpf datagrid checkbox


    【解决方案1】:

    我在我的应用程序中这样做了,当您必须选择 30 个复选框时非常方便。
    为此,我自己处理了预览鼠标事件:PreviewMouseLeftButtonDown、PreviewMouseMove、PreviewMouseLeftButtonUp。

    在 PreviewMouseLeftButtonDown 中:我获取相对于控件的鼠标位置。
    在 PreviewMouseMove 中:如果我离 firstPoint 足够远,我会从开始到当前位置绘制一个矩形。然后我在 CheckBoxes 中进行迭代,查看它们是否与矩形相交,如果是,则突出显示它们(这样用户就知道 chexbox 会交换)
    在 PreviewMouseLeftButtonUp :我为相交的复选框进行交换。

    如果它可以帮助你,这是我使用的代码。它不是 MVVM (:-)) 但工作正常,它可能会给你一些想法。它是 vb.net 代码的自动翻译。

    要使其正常工作,您需要在 CheckBox 顶部放置一个 Canvas(例如,=在同一个网格单元内),并带有 IsHitTestVisible="False" 属性。
    在这个 Canvas 中,放置一个名为“SelectionRectangle”的矩形,它具有适当的填充和描边,但不透明度为 0.0。

    // '' <summary>
    // '' When Left Mouse button is pressed, remember where the mouse move start
    // '' </summary>
    private void EditedItems_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
        StartPoint = Mouse.GetPosition(this);
    }
    
    // '' <summary>
    // '' When mouse move, update the highlight of the selected items.
    // '' </summary>
    private void EditedItems_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e) {
        if ((StartPoint == null)) {
            return;
        }
        PointWhereMouseIs = Mouse.GetPosition(this);
        Rect SelectedRect = new Rect(StartPoint, PointWhereMouseIs);
        if (((SelectedRect.Width < 20) 
                    && (SelectedRect.Height < 20))) {
            return;
        }
        //  show the rectangle again
        Canvas.SetLeft(SelectionRectangle, Math.Min(StartPoint.X, PointWhereMouseIs.X));
        Canvas.SetTop(SelectionRectangle, Math.Min(StartPoint.Y, PointWhereMouseIs.Y));
        SelectionRectangle.Width = Math.Abs((PointWhereMouseIs.X - StartPoint.X));
        SelectionRectangle.Height = Math.Abs((PointWhereMouseIs.Y - StartPoint.Y));
        foreach (CheckBox ThisChkBox in EditedItems.Children) {
            object rectBounds = VisualTreeHelper.GetDescendantBounds(ThisChkBox);
            Vector vector = VisualTreeHelper.GetOffset(ThisChkBox);
            rectBounds.Offset(vector);
            if (rectBounds.IntersectsWith(SelectedRect)) {
                ((TextBlock)(ThisChkBox.Content)).Background = Brushes.LightGreen;
            }
            else {
                ((TextBlock)(ThisChkBox.Content)).Background = Brushes.Transparent;
            }
        }
    }
    
    // '' <summary>
    // '' When Left Mouse button is released, change all CheckBoxes values. (Or do nothing if it is a small move -->
    // '' click will be handled in a standard way.)
    // '' </summary>
    private void EditedItems_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) {
        PointWhereMouseIs = Mouse.GetPosition(this);
        Rect SelectedRect = new Rect(StartPoint, PointWhereMouseIs);
        StartPoint = null;
        SelectionRectangle.Opacity = 0;
        //  hide the rectangle again
        if (((SelectedRect.Width < 20) 
                    && (SelectedRect.Height < 20))) {
            return;
        }
        foreach (CheckBox ThisEditedItem in EditedItems.Children) {
            object rectBounds = VisualTreeHelper.GetDescendantBounds(ThisEditedItem);
            Vector vector = VisualTreeHelper.GetOffset(ThisEditedItem);
            rectBounds.Offset(vector);
            if (rectBounds.IntersectsWith(SelectedRect)) {
                ThisEditedItem.IsChecked = !ThisEditedItem.IsChecked;
            }
            ((TextBlock)(ThisEditedItem.Content)).Background = Brushes.Transparent;
        }
    }
    

    编辑:我在用户控件中使用了该代码。此控件将布尔值列表和字符串列表(标题)作为参数,并(使用 WrapPanel)构建具有正确标题的 CheckBox 数组。所以你可以用矩形选择/取消选择,还有两个按钮可以选中/取消选中所有。我还尝试保持良好的列/行比率以处理具有良好列/行平衡的 7 到 200 个布尔值的选择。

    【讨论】:

    • 感谢您的帮助。虽然这不是我一直在寻找的优雅解决方案,但它比我想出的任何方法都要好。
    【解决方案2】:

    这个话题有几个月的历史了,但我想我有你正在寻找的优雅答案。

    由于您将拖动和检查描述为两个单独的行为,因此首先设置您的数据网格 MouseDown 处理程序...

    yourdatagrid.MouseDown += DragCheck_MouseDownHandler;
    

    这将允许从数据网格背景开始拖动(但不是网格中的控件。)

        private void DragCheck_MouseDownHandler(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            Control dgrid = sender as Control;
            foreach (CheckBox box in dgrid.Controls.OfType<CheckBox>())
            {
                box.Tag = null;
            }
            dgrid.MouseMove += DragMove_MouseMoveHandler;
        }
    

    这使用checkbox.Tag 作为一个切换开关,只在鼠标按下时拖动。如果您将 CheckBox 标签用于其他用途,我相信您可以找到自己的方式来识别这些框。 datagrid.MouseMove 设置为下一个处理程序....

        private void DragMove_MouseMoveHandler(object sender, MouseEventArgs e)
        {
            Control dgrid = sender as Control;
    
            Point now = dgrid.PointToClient(Cursor.Position);
            if (e.Button == MouseButtons.Left)
            {
                Control under = dgrid.GetChildAtPoint(now);
                if (under != null && under.GetType() == typeof(CheckBox))
                {
                    //if the point has a valid CheckBox control under it
                    CheckBox box = under as CheckBox;
                    if (box.Tag == null)// not yet been swiped
                    {
                        box.Checked = !box.Checked;
                        box.Tag = true;
                    }
                }
            }
            else
            {
                //if MouseButtons no longer registers as left
                //remove the handler 
    
                dgrid.MouseMove -= DragMove_MouseMoveHandler;
    
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2015-04-08
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 2015-05-20
      • 2015-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多