3

问题:

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

到目前为止我已经尝试过:

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

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

我想要什么:

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

有没有一种优雅的方式来实现这种效果?

4

2 回答 2

3

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

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

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

要使其工作,您需要在 CheckBoxes 顶部有一个 Canvas(例如,=在同一个网格单元内),其属性为 IsHitTestVisible="False" 。
在这个画布中,放置一个名为“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 个布尔值的选择。

在窗口中使用 BooleanEdit 用户控件的示例

于 2012-10-03T13:35:42.327 回答
0

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

由于您将拖动和检查描述为两个单独的行为,因此首先设置您的数据网格 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;

        }
    }
于 2013-07-18T07:54:58.713 回答