我想以下列方式修改DataGrid的选择行为。通常,如果选择了多个行,然后单击其中一个已选择的项,则选择将重置为仅单击的项。我想更改它,以便如果单击其中一个多选行而没有任何键盘修饰符,则不会修改选择。这样做的目的是允许多项拖放。
我注意到,当激活上述默认行为时,调用堆栈包括:
at System.Windows.Controls.DataGrid.OnSelectionChanged(SelectionChangedEventArgs e)
at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
at System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo info, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)
at System.Windows.Controls.DataGrid.HandleSelectionForCellInput(DataGridCell cell, Boolean startDragging, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)
at System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDown(MouseButtonEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
at System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
因此看起来我应该可以通过重写DataGridCell.OnMouseLeftButtonDown来修改行为,如下所示:
class MultiDragDataGridCell : DataGridCell
{
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
// This allows users to click-and-drag a multi-selection by handling the event before
// the default behavior (deselecting everything but the clicked cell) kicks in.
if (IsSelected && Keyboard.Modifiers == ModifierKeys.None)
{
e.Handled = true;
}
base.OnMouseLeftButtonDown(e);
}
}
但是,我无法让DataGrid创建MultiDragDataGridCell而不是普通的DataGridCell,因为实例化DataGridCell的类是内部的。任何人都知道如何实现这一目标,或者是否有其他方法可以实现我想要的行为?
我试过的其他事情:
答案 0 :(得分:3)
注意:此答案仅尝试为问题中提到的以下问题提供解决方案;而不是如何覆盖网格的选择行为。我希望一旦你有一个自定义DataGridCell
,它就可以成为你想要做的事情的一个很好的起点。
但是,我无法让DataGrid创建MultiDragDataGridCell而不是普通的DataGridCell,因为实例化DataGridCell的类是内部的。任何人都知道如何实现这一点..
解决方案:为了确保DataGrid
使用您的自定义DataGridCell
,您需要重新模板DataGridRow
以使用扩展版本DataGridCellsPresenter
反过来会提供您的自定义DataGridCell
。
请参阅以下示例代码:
扩展DataGrid控件
public class ExtendedDataGrid : DataGrid
{
protected override DependencyObject GetContainerForItemOverride()
{
//This provides the DataGrid with a customized version for DataGridRow
return new ExtendedDataGridRow();
}
}
public class ExtendedDataGridRow : DataGridRow { }
public class ExtendedDataGridCellsPresenter : System.Windows.Controls.Primitives.DataGridCellsPresenter
{
protected override DependencyObject GetContainerForItemOverride()
{
//This provides the DataGrid with a customized version for DataGridCell
return new ExtendedDataGridCell();
}
}
public class ExtendedDataGridCell : DataGridCell
{
// Your custom/overridden implementation can be added here
}
在XAML中重新模板化DataGridRow (更全面的template can be found at this link - 为了便于阅读,我只使用了它的淡化版本。)
<Style TargetType="{x:Type local:ExtendedDataGridRow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ExtendedDataGridRow}">
<Border x:Name="DGR_Border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<SelectiveScrollingGrid>
<SelectiveScrollingGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</SelectiveScrollingGrid.ColumnDefinitions>
<SelectiveScrollingGrid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</SelectiveScrollingGrid.RowDefinitions>
<!-- Make sure to register your custom DataGridCellsPresenter here as following -->
<local:ExtendedDataGridCellsPresenter Grid.Column="1"
ItemsPanel="{TemplateBinding ItemsPanel}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<DataGridDetailsPresenter Grid.Column="1"
Grid.Row="1"
Visibility="{TemplateBinding DetailsVisibility}"
SelectiveScrollingGrid.SelectiveScrollingOrientation=
"{Binding AreRowDetailsFrozen,
ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical},
Converter={x:Static DataGrid.RowDetailsScrollingConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<DataGridRowHeader Grid.RowSpan="2"
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
Visibility="{Binding HeadersVisibility,
ConverterParameter={x:Static DataGridHeadersVisibility.Row},
Converter={x:Static DataGrid.HeadersVisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
</SelectiveScrollingGrid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
而且,您的扩展DataGrid
可视树具有自定义数据网格单元格:
此外,请注意,扩展DataGrid
或DataGridRow
并非强制要求提供自定义DataGridCell
- 只需扩展DataGridCellsPresenter
即可获得相同的结果(并且,更新DataGridRow
的控制模板以使用扩展版本)
答案 1 :(得分:1)
我唯一能想到的就像是一个大黑客,所以最好不要按原样使用它。但它可能是找到自己的解决方案的起点。
基本想法:
EventManager.RegisterClassHandler
的已处理事件,也会执行一些事件处理程序。 这需要进行一些改进,否则您最终会搞乱整个应用程序中的所有单元格 自定义数据网格代码:
public class MyDataGrid : DataGrid
{
static MyDataGrid()
{
EventManager.RegisterClassHandler(typeof(DataGridCell), UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(PreviewMouseLeftButtonDownHandler));
EventManager.RegisterClassHandler(typeof(DataGridCell), UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(MouseLeftButtonUpHandler), true);
EventManager.RegisterClassHandler(typeof(DataGridCell), UIElement.MouseMoveEvent, new MouseEventHandler(MouseMoveHandler), true);
}
private static bool restoreNextCells = false;
private static bool isSelectedCell = false;
private static void PreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e)
{
var cell = sender as DataGridCell;
isSelectedCell = cell.IsSelected;
restoreNextCells = cell.IsSelected && Keyboard.Modifiers == ModifierKeys.None;
}
private static void MouseMoveHandler(object sender, MouseEventArgs e)
{
var cell = sender as DataGridCell;
if (isSelectedCell && e.LeftButton == MouseButtonState.Pressed && cell.IsSelected && Keyboard.Modifiers == ModifierKeys.None)
{
DragDrop.DoDragDrop(cell, new ObjectDataProvider(), DragDropEffects.All);
}
restoreNextCells = false;
isSelectedCell = false;
}
private static void MouseLeftButtonUpHandler(object sender, MouseButtonEventArgs e)
{
restoreNextCells = false;
isSelectedCell = false;
}
protected override void OnSelectedCellsChanged(SelectedCellsChangedEventArgs e)
{
if (restoreNextCells && e.RemovedCells.Count > 0)
{
foreach (DataGridCellInfo item in e.RemovedCells)
{
SelectedCells.Add(item);
}
restoreNextCells = false;
}
base.OnSelectedCellsChanged(e);
}
}
用于多小区选择。
<local:MyDataGrid SelectionMode="Extended" SelectionUnit="Cell">
希望我在解释中没有遗漏任何重要的部分......如果有什么不清楚的话。
答案 2 :(得分:1)
实际上你有一个解决方案:为DataGridCell创建一个样式并设置一个事件处理程序,但我想你的事件处理程序中存在一个逻辑错误:你已经将e.Handled
设置为true
,如果是DataGridCell已被选中,因此无法操纵内部控件,因为DataGrid的默认行为是首先选择/取消选择行/单元格(然后才操纵内部控件),因此如果您有多个选择,则单击行/单元格已被选中,所以实际上你只需要阻止选择在多次选择的情况下点击的行/单元格。
我认为这应该按照您的预期运作:
<DataGrid.Resources>
<Style TargetType="DataGridCell">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="PreviewMouseDown"/>
</Style>
</DataGrid.Resources>
private void PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var cell = sender as DataGridCell; if (cell == null) { return; }
DataGrid parGrid = null;
var visParent = VisualTreeHelper.GetParent(cell);
while (parGrid==null && visParent != null)
{
parGrid = visParent as DataGrid;
visParent = VisualTreeHelper.GetParent(visParent);
}
if (parGrid==null) { return; }
e.Handled = cell.IsSelected && Keyboard.Modifiers == ModifierKeys.None && parGrid.SelectedItems.Count > 1;
}