如何在 wpf 数据网格上停止拖动选择
本文关键字:拖动 选择 网格 数据网 wpf 数据 | 更新日期: 2023-09-27 17:55:08
嗨,所有这些听起来可能微不足道,但我想停止拖动选择WPF
DataGrid
我有一个简单的网格,例如
<DataGrid ItemsSource="{Binding Items}" SelectionMode="Extended">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding .}"/>
</DataGrid.Columns>
</DataGrid>
如何在单击拖动时停止多项选择,但通过 Shift 和 Ctrl 进行多项选择。
谢谢
Mikolaytis的答案是不完整的。 使用该解决方案,单击未选择的行将选择该行与其上方的第一个选定行之间的所有行,只要按下鼠标按钮即可。 这是DataGrid._isDraggingSelection仍然正确的副作用,在其他鼠标事件驱动的操作中对此进行了评估。
Il Vic的回答,虽然我没有尝试过,但比必要的要复杂得多。
相反,在 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]);
}
}
简而言之,在开始拖动选择后,下一个鼠标移动事件将结束它。
试试这个技巧:创建一个继承DataGrid
的类,并在不调用base.OnMouseMove
的情况下覆盖OnMouseMove
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>
我希望它能有所帮助。
我有用:
public partial class DataGridView : DataGrid
{
// ...
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && SelectionMode == DataGridSelectionMode.Extended)
{
// Do some stuff which you need for dragging
}
base.OnPreviewMouseMove(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
//base.OnMouseMove(e);
}
// ...
}