嗨,所有这些听起来可能都是微不足道的,但我想停止在WPF
DataGrid
上拖动选择
我有一个像
这样的简单网格<DataGrid ItemsSource="{Binding Items}" SelectionMode="Extended">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding .}"/>
</DataGrid.Columns>
</DataGrid>
如何在单击拖动时停止多个选择,但按Shift和Ctrl进行多次选择。
感谢
答案 0 :(得分:1)
尝试破解:创建一个继承DataGrid
的类并覆盖OnMouseMove
而不调用base.OnMouseMove
答案 1 :(得分:0)
DataGrid
控件不是为自定义选择手势而设计的,无论如何都可以执行一些黑客攻击以达到目标。
首先,我们需要一个辅助类:
public static class ReflectionHelper
{
public static T GetPropertyValue<T>(object owner, string propertyName) where T : class
{
Type ownerType = owner.GetType();
PropertyInfo propertyInfo = ownerType.GetProperty(propertyName,
BindingFlags.Instance | BindingFlags.NonPublic);
while (propertyInfo == null)
{
ownerType = ownerType.BaseType;
propertyInfo = ownerType.GetProperty(propertyName,
BindingFlags.Instance | BindingFlags.NonPublic);
}
return propertyInfo.GetValue(owner, null) as T;
}
public static void Execute(object owner, string methodName, params object[] parameters)
{
Type ownerType = owner.GetType();
MethodInfo methodInfo = ownerType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
while (methodInfo == null)
{
ownerType = ownerType.BaseType;
methodInfo = ownerType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
}
methodInfo.Invoke(owner, parameters);
}
}
我不喜欢它,但需要反思。首先,我们需要创建自己的DataGridRowHeader
才能更改其OnClick
代码:
public class DataGridRowHeader : System.Windows.Controls.Primitives.DataGridRowHeader
{
protected override void OnClick()
{
if (Mouse.Captured == this)
{
base.ReleaseMouseCapture();
}
DataGrid dataGridOwner = ReflectionHelper.GetPropertyValue<DataGrid>(this, "DataGridOwner");
DataGridRow parentRow = ReflectionHelper.GetPropertyValue<DataGridRow>(this, "ParentRow");
if (dataGridOwner != null && parentRow != null)
{
ReflectionHelper.Execute(dataGridOwner, "HandleSelectionForRowHeaderAndDetailsInput", parentRow, false);
}
}
}
我们要做的就是将false(而不是true)作为方法HandleSelectionForRowHeaderAndDetailsInput
的第二个参数传递。
我们需要通过创建自己的DataGridCell来处理DataGrid的单元格:
public class DataGridCell : System.Windows.Controls.DataGridCell
{
static DataGridCell()
{
EventManager.RegisterClassHandler(typeof(DataGridCell),
UIElement.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(DataGridCell.OnAnyMouseLeftButtonDownThunk), true);
}
private static void OnAnyMouseLeftButtonDownThunk(object sender, MouseButtonEventArgs e)
{
((DataGridCell)sender).OnAnyMouseLeftButtonDown(e);
}
private void OnAnyMouseLeftButtonDown(MouseButtonEventArgs e)
{
bool isKeyboardFocusWithin = base.IsKeyboardFocusWithin;
bool flag = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
DataGrid dataGridOwner = ReflectionHelper.GetPropertyValue<DataGrid>(this, "DataGridOwner");
if (isKeyboardFocusWithin && !flag && !e.Handled && this.IsSelected)
{
if (dataGridOwner != null)
{
ReflectionHelper.Execute(dataGridOwner, "HandleSelectionForCellInput",
this, false, true, false);
if (!this.IsEditing && !this.IsReadOnly)
{
dataGridOwner.BeginEdit(e);
e.Handled = true;
return;
}
}
}
else if (!isKeyboardFocusWithin || !this.IsSelected || flag)
{
if (!isKeyboardFocusWithin)
{
base.Focus();
}
if (dataGridOwner != null)
{
ReflectionHelper.Execute(dataGridOwner, "HandleSelectionForCellInput",
this, Mouse.Captured == null && flag, true, false);
}
e.Handled = true;
}
}
}
还需要一个简单的DataGridCellsPresenter
:
public class DataGridCellsPresenter : System.Windows.Controls.Primitives.DataGridCellsPresenter
{
protected override DependencyObject GetContainerForItemOverride()
{
return new DataGridCell(); /* the one in our namespace */
}
}
它会告诉DataGrid
使用我们的DataGridCell
。
现在,当然,我们必须创建一个默认样式(它应该放在Window
资源中),以强制DataGrid
使用我们的东西:
<Style x:Key="{x:Type DataGridRow}" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRow}">
<Border Name="DGR_Border" Background="{TemplateBinding Control.Background}" BorderBrush="{TemplateBinding Control.BorderBrush}" BorderThickness="{TemplateBinding Control.BorderThickness}" SnapsToDevicePixels="True">
<SelectiveScrollingGrid>
<SelectiveScrollingGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</SelectiveScrollingGrid.ColumnDefinitions>
<SelectiveScrollingGrid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</SelectiveScrollingGrid.RowDefinitions>
<DataGridCellsPresenter Grid.Column="1" ItemsPanel="{TemplateBinding DataGridRow.ItemsPanel}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
<local:DataGridDetailsPresenter Grid.Column="1" Grid.Row="1" Visibility="{TemplateBinding DataGridRow.DetailsVisibility}" SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=AreRowDetailsFrozen, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}}" />
<local:DataGridRowHeader SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" Grid.RowSpan="2" Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Row}}" />
</SelectiveScrollingGrid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我希望它可以提供帮助。
答案 2 :(得分:0)
Mikolaytis's answer不完整。使用该解决方案,只要按下鼠标按钮,单击未选择的行就会选择该行与该行上方的第一行的所有行。这是DataGrid的副作用。_isDraggingSelection仍然为true,这是在其他鼠标事件驱动的操作中评估的。
Il Vic's answer,虽然我没有尝试过,但它比必要的要复杂得多。
相反,在对DataGrid.OnMouseMove()的覆盖(如Mikolaytis的回答中所做的那样)中,通过反射调用私有方法DataGrid.EndDragging():
public class MyDataGrid : DataGrid
{
private static readonly FieldInfo s_isDraggingSelectionField =
typeof(DataGrid).GetField("_isDraggingSelection", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly MethodInfo s_endDraggingMethod =
typeof(DataGrid).GetMethod("EndDragging", BindingFlags.Instance | BindingFlags.NonPublic);
// DataGrid.OnMouseMove() serves no other purpose than to execute click-drag-selection.
// Bypass that, and stop 'is dragging selection' mode for DataGrid
protected override void OnMouseMove(MouseEventArgs e)
{
if ((bool)(s_isDraggingSelectionField?.GetValue(this) ?? false))
s_endDraggingMethod.Invoke(this, new object[0]);
}
}
简单地说,在开始拖动选择后,下一个鼠标移动事件将其结束。