问题:
我的应用程序要求用户能够通过一列复选框在一个数据集中选择多个条目。所需的行为是,当您单击列中的复选框时,它的行为与普通复选框一样,但如果在鼠标左键关闭时拖动复选框,则其选择状态将更改为与以前相反的状态。
到目前为止我尝试过的:
我尝试过子类化CheckBox和处理OnMouseEnter,但是单击的第一个复选框似乎捕获了鼠标,因此没有其他复选框触发OnMouseEnter事件。
我尝试实现一个拖放黑客,用户点击选择一个复选框,然后将该复选框拖到其他复选框上,以便其他人接收一个DragOver事件并切换状态。此解决方案使游标在拖放期间不超过另一个复选框时显示为带有斜杠的圆圈,这对于此应用程序是不可接受的。
我想要的:
我希望有一种方法来实现具有我描述的功能的复选框,最好是使用我可以重用的xaml样式或子类,因为这个功能需要在我的应用程序中的多个地方使用。
有一个优雅的方法来达到这个效果吗?
发布于 2012-10-03 05:35:42
我在我的应用程序中这样做,当您必须选择,比方说,30 checkBoxes时,非常方便。
为此,我亲自处理了预览鼠标事件: PreviewMouseLeftButtonDown、PreviewMouseMove、PreviewMouseLeftButtonUp。
在PreviewMouseLeftButtonDown中:我得到鼠标相对于控件的位置。
在PreviewMouseMove中:如果距离firstPoint足够远,我会从开始到当前位置绘制一个矩形。然后我在CheckBoxes中迭代,查看它们是否与矩形相交,如果是,则突出显示它们(这样用户就知道chexboxes将交换)。
在PreviewMouseLeftButtonUp中:我为交叉的CheckBoxes做交换。
如果它能帮助你,这是我使用的代码。它不是MVVM (:-),但是工作得很好,它可能会给您带来一些想法。它是vb.net代码的自动翻译。
要使其工作,您需要在CheckBoxes (例如,在相同的网格单元格内)之上设置一个画布,并带有属性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)具有正确标题的CheckBoxes数组。因此,您可以使用矩形选择/取消选择,还有两个按钮来检查all/uncheck all。我还努力保持良好的列/行比,以处理7到200个布尔值的选择,并具有良好的列/行平衡。
发布于 2013-07-17 23:54:58
这个话题已经有了几个月的历史了,但我想我已经有了你们正在寻找的优雅的答案。
由于您将拖动和检查描述为两种不同的行为,所以首先设置数据处理程序.
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;
}
}
https://stackoverflow.com/questions/12708645
复制