在WPF/C#中单击并拖动多选复选框

xpcnnkqh  于 2023-10-22  发布在  C#
关注(0)|答案(3)|浏览(211)
    • 问题:**

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

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

我尝试过将CheckBox子类化并处理OnMouseEnter,但单击的第一个复选框似乎捕获了鼠标,因此没有其他复选框触发OnMouseEnter事件。
我尝试过实现一个拖放技巧,用户单击选中一个复选框,然后将该复选框拖动到其他复选框上,这样其他复选框就会收到一个DragOver事件并可以切换状态。这种解决方案会导致光标在拖放过程中不在另一个复选框上时显示为一个带斜线的圆圈,这对于这个应用程序是不可接受的。

    • 我想要的:**

我想一个方法来实现一个复选框,具有我所描述的功能,理想情况下在一个xaml风格或子类,我可以重用,因为这个功能是需要在我的应用程序中的多个地方。

    • 是否有一个优雅的方式来实现这种效果?**
mfuanj7w

mfuanj7w1#

我在我的应用程序中这样做了,当你必须选择,比如说,30个复选框时,非常方便。
为此,我自己处理了预览鼠标事件:PreviewMouseLeftButtonDown、PreviewMouseMove、PreviewMouseLeftButtonUp。
在PreviewMouseLeftButtonDown中:我得到鼠标相对于控件的位置。
在PreviewMouseMove中:如果我离firstPoint足够远,我会画一个从起点到当前位置的矩形。然后我在CheckBox中搜索,看看它们是否与矩形相交,如果是,则突出显示它们(以便用户知道哪个复选框将交换)
在PreviewMouseLeftButtonUp中:我对交叉的CheckBox进行交换。
如果它可以帮助你,这是我使用的代码。它不是MVVM(:-)),但工作正常,它可能会给你给予想法。它是从www.example.com代码自动翻译而来vb.net。
要使其工作,您需要在CheckBox顶部(例如,在同一网格单元格中)使用属性IsHitTestVisible=“False”的Canvas。
在这个画布,把一个矩形命名为“选择矩形”适当的填充和中风,但与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个布尔值的选择,并保持良好的列/行平衡。

smdnsysy

smdnsysy2#

这个主题是几个月前的事了,但我想我有你想要的优雅的答案。
由于您将拖动和检查描述为两个独立的行为,因此首先将datagrid MouseDown处理程序设置为.

yourdatagrid.MouseDown += DragCheck_MouseDownHandler;

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

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;

        }
    }
fdx2calv

fdx2calv3#

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag to Select Checkboxes</title>
    <style>
        .checkbox-container {
            display: flex;
            align-items: center;
        }

        .checkbox {
            width: 20px;
            height: 20px;
            margin: 5px;
            border: 1px solid #ccc;
        }

        .number {
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="checkboxContainer" class="checkbox-container">
        <div class="checkbox-item">
            <div class="number">1</div>
            <input type="checkbox" class="checkbox">
        </div>
        <div class="checkbox-item">
            <div class="number">2</div>
            <input type="checkbox" class="checkbox">
        </div>
        <div class="checkbox-item">
            <div class="number">3</div>
            <input type="checkbox" class="checkbox">
        </div>
        <!-- Add more checkbox items here as needed -->
    </div>
    <script>
        let isDragging = false;
        let selectedCheckboxes = [];

        const checkboxItems = document.querySelectorAll('.checkbox-item');

        checkboxItems.forEach(checkboxItem => {
            const checkbox = checkboxItem.querySelector('.checkbox');
            const number = checkboxItem.querySelector('.number');

            number.addEventListener('mousedown', () => {
                isDragging = true;
                selectedCheckboxes.push(checkbox);
                checkbox.checked = !checkbox.checked;
            });

            number.addEventListener('mouseup', () => {
                isDragging = false;
            });

            number.addEventListener('mouseover', () => {
                if (isDragging) {
                    selectedCheckboxes.push(checkbox);
                    checkbox.checked = !checkbox.checked;
                }
            });
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });
    </script>
</body>
</html>

相关问题