在现代的应用程序开发中,用户界面的交互性变得越来越重要。拖放和多选功能是提升用户体验的关键特性之一。本文将介绍如何使用UIEventHub以及DragDropEventProcessor和MultiSelectEventProcessor来实现这些功能。
UIEventHub是一个用于支持ListBox/View和TreeView的事件处理器集,它允许通过XAML语法或C#代码来注册UI处理器。以下是如何使用UIEventHub的示例。
<ListView Grid.Column="1" Grid.Row="2">
<bc:UIEventAdapter.Processors>
<bc:DragDropEventProcessor EnableDrag="True" EnableDrop="True"/>
<bc:MultiSelectEventProcessor UnselectAllCommand="{Binding UnselectAllCommand}"/>
</bc:UIEventAdapter.Processors>
</ListView>
treeView.RegisterEventProcessors(new DragDropEventProcessor());
listView.RegisterEventProcessors(new DragDropEventProcessor(), new MultiSelectEventProcessor(vm1.UnselectAllCommand));
需要注意的是,TreeView不支持多选。对于拖放操作,除了注册UI处理器外,还需要实现ISupportDrag/Drop/DragHelper/DropHelper接口,以便系统能够正常工作。
IUIEventProcessor接口允许一个应用程序(例如拖动)处理来自控件的事件。
public interface IUIEventProcessor
{
IEnumerable<RoutedEvent> ProcessEvents { get; }
IScriptCommand OnEvent(RoutedEvent eventId);
IScriptCommand OnMouseDrag { get; }
// Previous method
IScriptCommand OnMouseDragOver { get; }
...
}
UIEventProcessor包含了多个IScriptCommand,并有一个OnEvent()方法。当触发特定事件时,会创建并运行适当的IScriptCommand(例如Control.MouseMove -> OnEvent() -> ContinueDrag)。因为IScriptCommand.Execute()可以返回另一个IScriptCommand,所以复杂的操作被分解成多个IScriptCommand。
ScriptRunner类实现了IScriptRunner接口,用于运行一系列IScriptCommand。
public class ScriptRunner : IScriptRunner
{
public void Run(Queue<IScriptCommand> cmds, ParameterDic initialParameters)
{
ParameterDic pd = initialParameters;
while (cmds.Any())
{
var current = cmds.Dequeue();
if (current.CanExecute(pd))
{
var retCmd = current.Execute(pd);
if (retCmd != null)
cmds.Enqueue(retCmd);
}
else
throw new Exception(String.Format("Cannot execute {0}", current));
}
}
}
DragDropEventProcessor允许从支持ISupportDrag的DataContext拖动一个或多个对象到另一个支持ISupportDrop的DataContext。
public interface ISupportDragHelper
{
ISupportDrag DragHelper { get; }
}
public interface ISupportDrag
{
bool HasDraggables { get; }
IEnumerable<IDraggable> GetDraggables();
DragDropEffects QueryDrag(IEnumerable<IDraggable> draggables);
IDataObject GetDataObject(IEnumerable<IDraggable> draggables);
void OnDragCompleted(IEnumerable<IDraggable> draggables, IDataObject da, DragDropEffects effect);
}
每当用户尝试拖动时,处理器会检查DataContext是否支持ISupportDrag或ISupportDragHelper,如果HasDraggables等于true,则调用GetDraggables()和GetDataObject()获取一个数据对象,并初始化拖动(使用System.Windows.DragDrop.DoDragDrop)。
MultiSelectEventProcessor允许通过在ListView上用鼠标拖动来选择多个项目。这是一个非常基础的功能,框架至今仍然没有提供。
<DataTemplate x:Key="dragnDropItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Value}"/>
<TextBlock Text="[ing]" Foreground="Blue" Visibility="{Binding (bc:AttachedProperties.IsSelecting), RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Converter={StaticResource btvc}}"/>
<TextBlock Text="[ed]" Foreground="Green" Visibility="{Binding IsSelected, Converter={StaticResource btvc}}"/>
</StackPanel>
</DataTemplate>
选择查找例程(FindSelectedItems)是前两篇文章的结合,它使用GridView的第一个/最后一个选中项目,支持IChildInfo的面板使用IChildInfo,否则使用HitTest。