65.9K
CodeProject 正在变化。 阅读更多。
Home

Group Sort Adorner ListView

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (9投票s)

2008年9月1日

CPOL

5分钟阅读

viewsIcon

57961

downloadIcon

1221

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

引言

这是我正在进行的关于 ListView 的 3 篇文章的第二部分。第一部分(分组和排序)在此。该系列的第三部分(ComboBox 编辑)在此。本文适用于任何正在寻找具有拖放分组、拖放列重新排序和列调整功能的 WPF 可排序 Listview 的用户。还有一个会随着光标拖动的 Adorner。我想特别感谢以下个人/网站:

关注点

好了,我认为我将对许多代码进行泛化处理,因为上面的引用很好地解释了每个部分的工作原理。我的代码中也有很多注释。

我还想指出,GroupSortAdornerListView **是**我设想的解决方案,并且效果非常好。但是,要实现 DesignerCanvas 上每个列只允许一个 DesignerItem,还需要做一些额外的工作,这可以在以后完成。

目前,我只是自定义 ListViewGridViewColumn 类,并创建自己的 ListView 样式。有关样式的更多信息可以在此处找到。GroupSortAdornerListView 类是新类,但与我在第一部分中提到的 GroupSortListViewGroupSortModeListView 类类似。

我遇到的主要问题是如何在 GroupSortAdornerListView 的排序、分组、调整大小和列重新排序中“共享”鼠标左键事件。要做到这一点,您必须重写 ListView 类中的 3 个方法。
这 3 个方法是:

  • OnPreviewMouseLeftButtonDown
  • OnPreviewMouseMove
  • OnPreviewMouseLeftButtonUp

以下是 GroupSortAdornerListView 类的重要属性和一些重要方法。此类与其他两个类一样,它也是连接 ListViewDesignerCanvasGridViewColumn 的驱动类。

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 个按钮,分别打开其他窗口(Window1Window2Window3Window4)。最新的按钮是 GroupSort Adorner Window,这是具有此新功能的按钮。

结论

这就是我一直在寻找的解决方案。我计划在几周内发布第三部分,介绍 ListView 编辑功能,以结束本系列。

修订历史

  • 2008/9/1 - 文章创建
  • 2008/9/20 - 添加了该系列的第三部分,在此
© . All rights reserved.