首页 文章

UWP ListView拖动触摸行为

提问于
浏览
4

当使用触摸触发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>

3 回答

  • 2

    我想要在这里受到质疑的类似行为,因为我对默认的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>
    

    上面是我的GridView和绑定数据 . 我注意到如果我使用gridviews candrop和dragstarted事件,我就不能做我喜欢的事情 . 所以我使用了指向压缩的datatemplates stackpanels,它会立即启动 .

    private async void StackPanel_PointerPressed(object sender, PointerRoutedEventArgs e)
    {
        var obj = (StackPanel)sender;
        if (obj != null)
        {
            var pointerPoint = e.GetCurrentPoint(sender as UIElement);
            await obj.StartDragAsync(pointerPoint);
        }
    }
    

    那里你也有指针点 . 在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;
    }
    

    在我设法在我的动物列表中传递动物的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);
    
        }
    

    我希望这对某人有所帮助并且答案不会太长 .

  • 2

    我终于想出了如何恢复ListView的旧Windows 8.1行为 . 如果垂直滑动滚动轴,它仍然允许滚动滚动并启动 one 项目的拖动操作 . 它基于Comet library,由自定义ListView实现 . 我们的想法是在ListViewItem中允许TranslateX / TranslateY和System Manipulations . 为此,您需要覆盖默认的ListViewItem样式 .

    如果你想使用控件,你必须记住一些事情:

    • 复制Themes / Generic.xaml中的样式并调整local2命名空间 .

    • 如果使用水平滚动ListView,则必须相应地设置ListView的Orientation属性 . 控件未检测到已使用的ItemsPanel .

    • 您仍然可以使用常规的UWP拖放机制,但是您必须为旧的Windows 8.1样式拖动订阅名为ItemStartDragging的第二个事件 .

    • 如果在使用8.1样式拖动时处理Drop事件,则可以在DragEventArgs.DataView中找到数据,而在使用DragItemStarting(= default事件)时可以在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>
    
  • 0

    在WinRT中,向左或向右“撕开”项目会导致其分离,从而启动拖动行为 . 在UWP中,用户必须轻敲并暂停一个项目,然后移动它以启动拖动动作 .

    是的,在UWP应用程序中已更改了开始拖动操作的行为 .

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

    可能的方法是创建一个Drag Behavior并附加到ListView,在此行为中,我们可以处理相关的触摸事件并使用UIElement.StartDragAsync method以编程方式启动拖放操作,以查找当前的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;
            }
    }
    

相关问题