问题:
我的应用程序要求用户能够通过一列复选框在数据网格中选择多个条目。所需的行为是,当您单击列中的复选框时,它的行为类似于普通复选框,但如果您在鼠标左键按下时拖动它,则其选择状态将更改为与之前相反的状态。 / p>
到目前为止我尝试了什么:
我尝试过继承CheckBox并处理OnMouseEnter,但是点击的第一个复选框似乎捕获了鼠标,因此没有其他复选框触发OnMouseEnter事件。
我尝试过实施拖放式黑客攻击,用户单击以选中复选框,然后将该复选框拖到其他复选框上,以便其他人接收DragOver事件并切换状态。此解决方案会导致光标在拖放过程中显示为带斜杠的圆圈而不是另一个复选框,这对于此应用程序是不可接受的。
我想要的是什么:
我想要一个方法来实现一个具有我描述的功能的复选框,理想情况是我可以重用的xaml样式或子类,因为我的应用程序中的多个位置需要此功能。
有没有一种优雅的方法来实现这种效果?
答案 0 :(得分:3)
我在我的应用程序中这样做了,当你必须选择30个checkBox时非常方便 为此,我自己处理了预览鼠标事件:PreviewMouseLeftButtonDown,PreviewMouseMove,PreviewMouseLeftButtonUp。
在PreviewMouseLeftButtonDown中:我得到相对于控件的鼠标位置 在PreviewMouseMove中:如果我离firstPoint足够远,我会从开始到当前位置绘制一个矩形。然后我在CheckBoxes中迭代,看看它们是否与矩形相交,如果是这样则突出显示它们(这样用户就知道chexboxes会交换) 在PreviewMouseLeftButtonUp中:我执行相交CheckBoxes的交换。
如果它可以帮到你,这是我使用的代码。它不是MVVM(:-))但工作正常,它可能会给你一些想法。它是来自vb.net代码的自动翻译。
要使其工作,您需要在CheckBoxes顶部使用Canvas(例如,在同一个网格单元格内),其属性为IsHitTestVisible =“False”。
在这个Canvas中,将一个Rectangle nammed“SelectionRectangle”放入适当的填充和描边,但是使用0.0 Opacity。
// '' <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数组。因此,您可以使用矩形选择/取消选择,还有两个按钮可以全部检查/取消选中全部。我还试图保持良好的列/行比率,以处理7到200个布线的选择,具有良好的列/行平衡。
答案 1 :(得分:0)
这个话题已经有几个月了,但我想我有你想要的优雅答案。
由于您将拖动和检查描述为两个单独的行为,首先设置您的datagrid 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;
}
}