Group Sort Adorner ListView
本文适用于任何正在寻找具有拖放分组、拖放列重新排序和列调整功能的 WPF 可排序 ListView 的用户。

引言
这是我正在进行的关于 ListView
的 3 篇文章的第二部分。第一部分(分组和排序)在此。该系列的第三部分(ComboBox
编辑)在此。本文适用于任何正在寻找具有拖放分组、拖放列重新排序和列调整功能的 WPF 可排序 Listview
的用户。还有一个会随着光标拖动的 Adorner
。我想特别感谢以下个人/网站:
- Joel Rumerman 的博客中的 WPF 可排序 GridView(我的意思是 ListView)控件
- sukram 的WPF 图表设计器:第一部分
- Josh Smith 的理解 WPF 中的视觉树和逻辑树
- Josh Smith 的在 WPF ListView 中拖放项目
关注点
好了,我认为我将对许多代码进行泛化处理,因为上面的引用很好地解释了每个部分的工作原理。我的代码中也有很多注释。
我还想指出,GroupSortAdornerListView
**是**我设想的解决方案,并且效果非常好。但是,要实现 DesignerCanvas
上每个列只允许一个 DesignerItem
,还需要做一些额外的工作,这可以在以后完成。
目前,我只是自定义 ListView
和 GridViewColumn
类,并创建自己的 ListView
样式。有关样式的更多信息可以在此处找到。GroupSortAdornerListView
类是新类,但与我在第一部分中提到的 GroupSortListView
和 GroupSortModeListView
类类似。
我遇到的主要问题是如何在 GroupSortAdornerListView
的排序、分组、调整大小和列重新排序中“共享”鼠标左键事件。要做到这一点,您必须重写 ListView
类中的 3 个方法。
这 3 个方法是:
OnPreviewMouseLeftButtonDown
OnPreviewMouseMove
OnPreviewMouseLeftButtonUp
以下是 GroupSortAdornerListView
类的重要属性和一些重要方法。此类与其他两个类一样,它也是连接 ListView
、DesignerCanvas
和 GridViewColumn
的驱动类。
GroupSortAdornerListView 类
使用此类,您可以完全控制列的功能。您的分组拖放、列重新排序拖放、排序和调整大小可以很好地协同工作。让它们协同工作的关键在于跟踪 2 个事件。一个是您**是否可以**拖动,另一个是您**何时在**拖动。下面将对该类的重要属性和方法进行进一步说明。
属性
GroupSortListViewColumnHeaderSortedAscendingStyle
- 获取/设置升序排序的列的样式名称。GroupSortListViewColumnHeaderSortedDescendingStyle
- 获取/设置降序排序的列的样式名称。GroupSortListViewColumnHeaderNotSortedStyle
- 获取/设置未排序的列的样式名称。ColumnUnderDragCursor
- 获取/设置拖动光标下的GridViewColumnHeader
,以便我们在拖动到它上面时突出显示它。它还用于在放下时查找列的索引号以进行列重新排序。ColumnBeingDragged
- 获取/设置正在拖动的GridViewColumnHeader
。此外,还可以获取有关列重新排序和分组的信息。CanStartDragOperation
- 获取一个bool
值,告诉我们是否可以开始拖动。
重要方法
OnPreviewMouseLeftButtonDown
- 此方法指示是否允许拖动,并初始化启动拖动过程所需的必要对象。OnPreviewMouseMove
- 此方法处理Adorner
,以及是否可以进行分组和重新排序的拖放,或仅进行重新排序。OnPreviewMouseLeftButtonUp
- 此方法处理排序时列标题的样式更改。MouseDragged
- 指示鼠标是否移动了超过一像素。DropForGrouping
- 处理在DesignerCanvas
上放下以进行列分组。DropForColumnReorderOnly
- 处理列的重新排序。GetColumnHeaderUnderDragCursor
- 使用自定义IsMouseOver2
方法获取拖动光标下的列标题。GetThumbUnderDragCursor
- 使用自定义IsMouseOver2
方法获取拖动光标下的拇指。InitializeAdornerLayer
- 设置拖动列时使用的Adorner
。UpdateDragAdornerLocation
- 在拖动时更新Adorner
的位置。
Using the Code
正如第一部分中所述,此应用程序有 4 个主要部分。第一部分是 Designer 控件,涉及 Canvas
控件上的自定义视觉设计器项,用于查看/可视化已分组的列。第二部分是 GridView
/ListView
控件,用于自定义 GridViewColumnHeaders
以适应排序和分组。第三部分是资源,主要用于 GridView
。最后是显示自定义 ListView
的窗口。
这是 GridViewColumnHeader
的一种样式设置方式。共有三种样式:降序、升序和未排序的列。
<Style x:Key="UpArrowHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">
<Grid>
<Border Name="HeaderBorder"
BorderThickness="0,1,0,1"
BorderBrush="{StaticResource NormalBorderBrush}"
Background="{StaticResource LightBrush}"
Padding="2,0,2,0">
<StackPanel x:Name="spHeader"
Orientation="Horizontal">
<ContentPresenter Name="HeaderContent"
Margin="0,0,0,1"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding
SnapsToDevicePixels}"/>
<Path x:Name="arrow"
VerticalAlignment="Center"
HorizontalAlignment="Center"
StrokeThickness="1" Fill="Gray"
Data="M 5,10 L 15,10 L 10,5 L 5,10"/>
</StackPanel>
</Border>
<Thumb x:Name="PART_HeaderGripper"
HorizontalAlignment="Right"
Margin="0,0,-9,0"
Style="
{StaticResource GridViewColumnHeaderGripper}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="HeaderBorder"
Property="Background"
Value="{StaticResource NormalBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="HeaderBorder"
Property="Background"
Value="{StaticResource PressedBrush}"/>
<Setter TargetName="HeaderContent"
Property="Margin" Value="1,1,0,0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{DynamicResource
{x:Static
SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="mlv:
ListViewColumnHeaderDragState.IsUnderDragCursor"
Value="True">
<Setter Property="Foreground" Value="red"/>
<Setter TargetName="HeaderBorder" Property="Background"
Value="{StaticResource DragOverBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Role" Value="Padding">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">
<Border Name="HeaderBorder"
BorderThickness="0,1,0,1"
BorderBrush="{StaticResource NormalBorderBrush}"
Background="{StaticResource LightBrush}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
OnPreviewMouseMove 拖动
这一部分是第一部分实现方式与此处实现方式的主要区别。我之前说过,您需要跟踪**是否可以**拖动,以及**何时在**拖动。此重写方法正是这样做的。
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
base.OnPreviewMouseMove(e);
if( !this.CanStartDragOperation )
return;
if (!MouseDragged(e.GetPosition(this)))
return;
isDragging = true;
GroupSortGridViewColumn sortCol = ColumnBeingDragged.Column as
GroupSortGridViewColumn;
//if((ColumnReorderOnly) or (Both ColumnReorder and Groupable))
string groupName = "";
if (sortCol != null)
groupName = sortCol.GroupPropertyName;
if (groupName != "")//(Both ColumnReorder and Groupable)
DropForGrouping(sortCol);
else//if there is no groupName then it must be a columnReorder!!!
DropForColumnReorderOnly();
}
DropForGrouping 方法
此方法同时设置了列分组和列重新排序。首先,它为列分组设置了序列化对象。其次,它为拖动设置了 Adorner
。最后,它执行放下操作,并且根据 DragDropEffects
的值,可能按该列进行分组。
private void DropForGrouping(GroupSortGridViewColumn sortCol)
{
//Serialize the object that is added to the DesignerCanvas
SerializeGroupObject
(sortCol.GroupPropertyName, sortCol.Header.ToString());
if (dataObject != null)
{
//Setup the Adorner Visual to drag
AdornerLayer layer2 = InitializeAdornerLayer(ColumnBeingDragged);
DragDropEffects allowedEffects =
DragDropEffects.Copy | DragDropEffects.Move;
DragDropEffects dde = DragDrop.DoDragDrop
(this, dataObject, allowedEffects);
if (dde == DragDropEffects.Copy)
Group(sortCol.GroupPropertyName);
if (layer2 != null)
layer2.Remove(this.dragAdorner);
//reset these to null when done
ColumnUnderDragCursor = null;
ColumnBeingDragged = null;
}
}
DropForColumnReorderOnly 方法
此方法仅用于列重新排序。它首先设置 Adorner
,然后为列重新排序执行放下操作。
private void DropForColumnReorderOnly()
{
AdornerLayer layer2 = InitializeAdornerLayer(ColumnBeingDragged);
DragDropEffects allowedEffects = DragDropEffects.Move;
DragDropEffects dde = DragDrop.DoDragDrop
(this, "Something", allowedEffects);
if (layer2 != null)
layer2.Remove(this.dragAdorner);
//
ColumnUnderDragCursor = null;
ColumnBeingDragged = null;
}
重写的 OnDrop 方法
放下操作发生在 GridViewColumnHeader
上时,列的重新排序在此处发生。您可以使用它们的索引号和列的 Move
方法来重新排序列,如下所示:
protected override void OnDrop(DragEventArgs e)
{
if (ColumnBeingDragged != null && ColumnUnderDragCursor != null)
{
//This is how you reorder the columns
GridViewColumn colDrag = ColumnBeingDragged.Column;
GridViewColumn colDrop = ColumnUnderDragCursor.Column;
e.Effects = DragDropEffects.Move;
int colDragged =
gridViewHeaderRowPresenter.Columns.IndexOf(colDrag);
int colDroppedOn =
gridViewHeaderRowPresenter.Columns.IndexOf(colDrop);
gridViewHeaderRowPresenter.Columns.Move(colDragged, colDroppedOn);
}
}
运行应用程序
StartupWindow
有 4 个按钮,分别打开其他窗口(Window1
、Window2
、Window3
和 Window4
)。最新的按钮是 GroupSort Adorner Window
,这是具有此新功能的按钮。
结论
这就是我一直在寻找的解决方案。我计划在几周内发布第三部分,介绍 ListView
编辑功能,以结束本系列。
修订历史
- 2008/9/1 - 文章创建
- 2008/9/20 - 添加了该系列的第三部分,在此