Xaml 用于触摸的UWP ListView拖动行为

Xaml 用于触摸的UWP ListView拖动行为,xaml,listview,drag-and-drop,uwp,Xaml,Listview,Drag And Drop,Uwp,使用触摸键触发ListView项目的拖放操作时,WinRT(Windows 8/8.1)和UWP(Windows 10)应用程序之间的行为似乎发生了变化 在WinRT中,向左或向右“撕裂”一个项目会导致它分离,从而启动拖动行为。在UWP中,用户必须轻触并按住一个项目一段时间,然后移动它启动拖动操作 我的问题是:有没有办法恢复/实现旧的WinRT风格的行为?新的方法不是很明显,在有限的用户测试中,我没有看到一个人在没有向他们解释的情况下解决它 举个简单的例子,下面的XAML适用于WinRT和UWP

使用触摸键触发ListView项目的拖放操作时,WinRT(Windows 8/8.1)和UWP(Windows 10)应用程序之间的行为似乎发生了变化

在WinRT中,向左或向右“撕裂”一个项目会导致它分离,从而启动拖动行为。在UWP中,用户必须轻触并按住一个项目一段时间,然后移动它启动拖动操作

我的问题是:有没有办法恢复/实现旧的WinRT风格的行为?新的方法不是很明显,在有限的用户测试中,我没有看到一个人在没有向他们解释的情况下解决它

举个简单的例子,下面的XAML适用于WinRT和UWP,但是基于触摸的交互在WinRT中很容易发现

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView AllowDrop="True" CanReorderItems="True">
        <ListView.Items>
            <x:String>Item 1</x:String>
            <x:String>Item 2</x:String>
            <x:String>Item 3</x:String>
            <x:String>Item 4</x:String>
            <x:String>Item 5</x:String>
        </ListView.Items>
    </ListView>
</Grid>

项目1
项目2
项目3
项目4
项目5
在WinRT中,向左或向右“撕裂”一个项目会导致它分离,从而启动拖动行为。在UWP中,用户必须轻触并按住一个项目一段时间,然后移动它启动拖动操作

是的,在UWP应用程序中启动拖动操作的行为已更改

我的问题是:有没有办法恢复/实现旧的WinRT风格的行为

可能的方法是创建拖动行为并附加到ListView,在该行为中,我们可以处理相关的触摸事件,并使用以编程方式启动拖放操作,以查找当前ListViewItem

public class FrameworkElementDragBehavior : DependencyObject, IBehavior
{
        private bool isMouseClicked = false;
        public DependencyObject AssociatedObject { get; private set; }

        public void Attach(DependencyObject associatedObject)
        {
            var control = associatedObject as Control;
            if (control == null)
                throw new ArgumentException(
                    "FrameworkElementDragBehavior can be attached only to Control");

            AssociatedObject = associatedObject;

            ((FrameworkElement)this.AssociatedObject).Holding += FrameworkElementDragBehavior_Holding;
            ((FrameworkElement)this.AssociatedObject).DragStarting += FrameworkElementDragBehavior_DragStarting;
        }

        private void FrameworkElementDragBehavior_Holding(object sender, Windows.UI.Xaml.Input.HoldingRoutedEventArgs e)
        {
            //Just for example, not the completed code
            var obj = ((ListView)sender).SelectedItem as ListViewItem;

            if (obj != null)
            {
                //Call the UIElement.StartDragAsync method
            }
        }

        private void FrameworkElementDragBehavior_DragStarting(UIElement sender, DragStartingEventArgs args)
        {
            throw new NotImplementedException();
        }

        public void Detach()
        {
            AssociatedObject = null;
        }
}

我想要类似的行为,因为我对默认的win10行为感到恼火。也许有更好的解决方案,但这是我想到的

<GridView Name="MySourceGridView" ItemsSource="{x:Bind Animals}" ScrollViewer.VerticalScrollMode="Disabled" >
    <GridView.ItemTemplate>
        <DataTemplate x:DataType="data:Animal">
            <StackPanel Margin="20" Width="200" Height="200" PointerPressed="StackPanel_PointerPressed" DragStarting="StackPanel_DragStarting">
                <StackPanel.Background>
                    <SolidColorBrush Color="{x:Bind Color}" />
                </StackPanel.Background>
                <TextBlock Text="{x:Bind Name}" />
            </StackPanel>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>
你也有一个指针。拖动从stackpanel上开始以移动数据

private void StackPanel_DragStarting(UIElement sender, DragStartingEventArgs args)
{
    var senderElement = sender as FrameworkElement;
    var ani = (Animal)senderElement.DataContext;

    args.Data.SetText(ani.ID.ToString());
    args.Data.RequestedOperation = DataPackageOperation.Copy;
}
Rest只是在我成功地将动物列表中的动物ID向前传递之后捕获的正常数据

private void MyTargetRectangle_DragEnter(object sender, DragEventArgs e)
{
    e.AcceptedOperation = DataPackageOperation.Copy;
    e.DragUIOverride.Caption = "Kokeiles";
    e.DragUIOverride.IsCaptionVisible = true;
    e.DragUIOverride.IsContentVisible = true;
    e.DragUIOverride.IsGlyphVisible = false;
}

private async void MyTargetRectangle_Drop(object sender, DragEventArgs e)
    {
        var droppedAnimalId = await e.DataView.GetTextAsync();

        Animal ani = Animals.Where(p => p.ID == int.Parse(droppedAnimalId)).FirstOrDefault();
        MyTargetRectangle.Fill = new SolidColorBrush(ani.Color);

    }

我希望这对某人有所帮助,而且答案不会太长。

我终于找到了恢复ListView旧的Windows 8.1行为的方法。如果您垂直于滚动轴滑动,它仍然允许触摸滚动,并启动一个项目的拖动操作。它基于,由自定义ListView实现。其思想是允许在ListViewItem中进行TranslateX/TranslateY和系统操作。为此,您需要覆盖默认ListViewItem的样式

如果要使用控件,必须记住以下几点:

  • 复制Themes/Generic.xaml中的样式并调整local2名称空间
  • 如果使用水平滚动的ListView,则必须相应地设置ListView的方向属性。控件未检测到已使用的ItemsPanel
  • 您仍然可以使用常规的UWP拖放机制,但必须为旧的Windows 8.1样式的拖动订阅第二个名为ItemStartDraging的事件
  • 如果在使用8.1样式拖动时处理Drop事件,则可以在DragEventArgs.DataView中找到数据,而在使用DragItemStart(=默认事件)时,可以在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
    }
    
    公共类DraggingListView:ListView { 公共DraggingListView() { } 受保护的覆盖依赖对象GetContainerForItemOverride() { 如果(方向==方向.水平) 返回新的HorizontalDraggingListItem(此项); 其他的 返回新的垂直拖动列表项(此项); } 受保护的覆盖void PrepareContainerForItemOverride(DependencyObject元素,对象项) { base.PrepareContainerForItemOverride(元素、项); (元素作为DraggingListItem); (元素作为DraggingListItem)。MouseSlidingEnabled=MouseSlidingEnabled; } 公共事件处理程序ItemStartDraging; public void onChildItemDraged(DraggingListItem项,Windows.ApplicationModel.DataTransfer.DataPackage数据) { if(ItemStartDragging==null) 返回; ItemStartDraging(这是新的ListItemStartDragingEventArgs(数据,item.DataContext)); } 公众导向 { 获取{return(Orientation)GetValue(OrientationProperty);} set{SetValue(方向属性,值);} } 公共静态只读DependencyProperty方向属性= DependencyProperty.Register(“方向”、typeof(方向)、typeof(DraggingListView)、new PropertyMetadata(方向.垂直)); /// ///获取或设置使用鼠标滑动控件的能力。默认情况下为False /// 公共场所的鼠标可滑动 { 获取{return(bool)GetValue(MouseSlidingEnabledProperty);} set{SetValue(MouseSlidingEnabledProperty,value);} } public static readonly dependencProperty MouseSlidingEnabledProperty= DependencyProperty.Register(“MouseSlidingEnabled”、typeof(bool)、typeof(DraggingListView)、new PropertyMetadata(false)); } 公共类ListItemStartDragingEventArgs:EventArgs { public Windows.ApplicationModel.DataTransfer.DataPackage数据{get;private set;} 公共对象项{get;private set;} 公共列表项StartDragingEventArgs(Windows.ApplicationModel.DataTransfer.DataPackage数据,对象项) { 数据=数据; 项目=项目; } } 公共类HorizontalDraggingListItem:DraggingListItem { 公共水平DraggingListItem(DraggingListView listView):基础(listView) { this.DefaultStyleKey=typeof(HorizontalDraggingListItem); } 受保护的超控布尔检测梯度(操纵增量) { 返回Math.Abs(delta.Translation.Y)>2; } } 公共类垂直
    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
    }
    
     <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>