我终于想出了如何恢复 ListView 的旧 Windows 8.1 行为。如果您垂直于滚动轴滑动,它仍然允许通过触摸滚动并启动 一个 项目的拖动操作。它基于Comet library 并由自定义ListView 实现。这个想法是允许 ListViewItem 中的 TranslateX/TranslateY 和系统操作。为此,您需要覆盖默认 ListViewItem 的样式。
如果要使用该控件,则必须牢记以下几点:
- 复制 Themes/Generic.xaml 中的样式并调整 local2 命名空间。
- 如果使用水平滚动的 ListView,则必须相应设置 ListView 的 Orientation 属性。控件未检测到使用的 ItemsPanel。
- 您仍然可以使用常规的 UWP 拖放机制,但您必须订阅名为 ItemStartDragging 的第二个事件以进行旧的 Windows 8.1 样式拖动。
- 如果您在使用 8.1 样式拖动时处理 Drop 事件,则数据可以在 DragEventArgs.DataView 中找到,而在使用 DragItemStarting(=默认事件)时可以在 DragEventArgs.Data.GetView() 中找到。不知道为什么他们的行为不同。
- 样式非常基本。您可能想要更改它们,使它们更类似于原始 ListViewItem 样式。
代码如下:
public class DraggingListView : ListView
{
public DraggingListView()
{
}
protected override DependencyObject GetContainerForItemOverride()
{
if (Orientation == Orientation.Horizontal)
return new HorizontalDraggingListItem(this);
else
return new VerticalDraggingListItem(this);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
(element as DraggingListItem).DataContext = item;
(element as DraggingListItem).MouseSlidingEnabled = MouseSlidingEnabled;
}
public event EventHandler<ListItemStartDraggingEventArgs> ItemStartDragging;
public void OnChildItemDragged(DraggingListItem item, Windows.ApplicationModel.DataTransfer.DataPackage data)
{
if (ItemStartDragging == null)
return;
ItemStartDragging(this, new ListItemStartDraggingEventArgs(data, item.DataContext));
}
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(DraggingListView), new PropertyMetadata(Orientation.Vertical));
/// <summary>
/// Gets or sets the ability to slide the control with the mouse. False by default
/// </summary>
public bool MouseSlidingEnabled
{
get { return (bool)GetValue(MouseSlidingEnabledProperty); }
set { SetValue(MouseSlidingEnabledProperty, value); }
}
public static readonly DependencyProperty MouseSlidingEnabledProperty =
DependencyProperty.Register("MouseSlidingEnabled", typeof(bool), typeof(DraggingListView), new PropertyMetadata(false));
}
public class ListItemStartDraggingEventArgs : EventArgs
{
public Windows.ApplicationModel.DataTransfer.DataPackage Data { get; private set; }
public object Item { get; private set; }
public ListItemStartDraggingEventArgs(Windows.ApplicationModel.DataTransfer.DataPackage data, object item)
{
Data = data;
Item = item;
}
}
public class HorizontalDraggingListItem : DraggingListItem
{
public HorizontalDraggingListItem(DraggingListView listView) : base(listView)
{
this.DefaultStyleKey = typeof(HorizontalDraggingListItem);
}
protected override bool DetectDrag(ManipulationDelta delta)
{
return Math.Abs(delta.Translation.Y) > 2;
}
}
public class VerticalDraggingListItem : DraggingListItem
{
public VerticalDraggingListItem(DraggingListView listView) : base(listView)
{
this.DefaultStyleKey = typeof(VerticalDraggingListItem);
}
protected override bool DetectDrag(ManipulationDelta delta)
{
return Math.Abs(delta.Translation.X) > 2;
}
}
[TemplatePart(Name = PART_CONTENT_GRID, Type = typeof(Grid))]
public abstract class DraggingListItem : ListViewItem
{
const string PART_CONTENT_GRID = "ContentGrid";
private Grid contentGrid;
private DraggingListView _listView;
public DraggingListItem(DraggingListView listView)
{
_listView = listView;
this.DragStarting += OnDragStarting;
}
private void OnDragStarting(UIElement sender, DragStartingEventArgs args)
{
_listView.OnChildItemDragged(this, args.Data);
}
protected override void OnApplyTemplate()
{
contentGrid = this.GetTemplateChild(PART_CONTENT_GRID) as Grid;
contentGrid.ManipulationDelta += ContentGrid_ManipulationDelta;
contentGrid.ManipulationCompleted += ContentGrid_ManipulationCompleted;
contentGrid.PointerPressed += ContentGrid_PointerPressed;
base.OnApplyTemplate();
}
private PointerPoint pp = null;
private void ContentGrid_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (!MouseSlidingEnabled && e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
return;
pp = e.GetCurrentPoint(sender as UIElement);
}
private void ContentGrid_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
if (!MouseSlidingEnabled && e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
return;
pp = null;
}
private async void ContentGrid_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
if (!MouseSlidingEnabled && e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
return;
if (DetectDrag(e.Delta) && pp != null)
{
var pointer = pp;
pp = null;
await StartDragAsync(pointer);
}
}
protected abstract bool DetectDrag(ManipulationDelta delta);
#region Dependency Properties
/// <summary>
/// Gets or sets the ability to slide the control with the mouse. False by default
/// </summary>
public bool MouseSlidingEnabled
{
get { return (bool)GetValue(MouseSlidingEnabledProperty); }
set { SetValue(MouseSlidingEnabledProperty, value); }
}
public static readonly DependencyProperty MouseSlidingEnabledProperty =
DependencyProperty.Register("MouseSlidingEnabled", typeof(bool), typeof(DraggingListItem), new PropertyMetadata(false));
#endregion
}
这是 Generic.xaml 中的 XAML:
<Style TargetType="local2:HorizontalDraggingListItem" >
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:HorizontalDraggingListItem">
<Grid ManipulationMode="TranslateY,System" x:Name="ContentGrid" Background="{TemplateBinding Background}">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local2:VerticalDraggingListItem" >
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:VerticalDraggingListItem">
<Grid ManipulationMode="TranslateX,System" x:Name="ContentGrid" Background="{TemplateBinding Background}">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>