2015-11-23 71 views
3

當使用touch觸發ListView項目的拖放操作時,WinRT(Windows 8/8.1)和UWP(Windows 10)應用程序之間的行爲似乎發生了變化。UWP ListView拖動觸摸行爲

在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> 

回答

0

在WinRT中,「撕裂」向左項目或權將使其獲得獨立,開始拖動行爲。在UWP中,用戶不得不輕敲並握住物品一會兒,然後移動它開始拖動動作。

是的,在UWP應用程序中已更改啓動拖動操作的行爲。

我的問題是:有沒有辦法恢復/實現舊的WinRT風格的行爲?

可能的方法是創建一個拖放行爲,重視的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; 
     } 
} 
+0

感謝富蘭克林,我會和這個戲後,當我有機會獲得代碼。儘管目光看待這種行爲,這不就是模仿新的UWP行爲,即用戶必須持有然後拖動嗎?我想我必須鉤入ManipulationStarted事件並僅處理TranslateX操作?是否存在這會干擾ListView基於觸摸的滾動操作的危險? –

+0

@FranklinChen你從哪裏得到'StartDragAsync'所需的'PointerPoint'? –

+0

@MikeGoatly你有沒有嘗試過這種行爲,並做它你想達到的目的? – SebastianR

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和綁定數據。我注意到,如果我使用GridView的candrop和dragstarted事件,我只是不能做我喜歡的事情。所以我使用了datatemplates stackpanels pointerpressed,立即啓動。

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); 
    } 
} 

在那裏你也有指針點。 dragstarted在堆疊面板上移動數據。

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); 

    } 

我希望這可以幫助別人,而不是太長的答案。

+0

即使ListView有ScrollViewer,你確定StackPanel在觸摸時觸發PointerPressed嗎?我已經試過這與邊界控制,它不會在那裏開火。 – SebastianR

+0

它似乎這樣做,我已經測試了我的代碼。該GridView也有ScrollViewer,因爲我禁用了垂直滾動來擺脫移動,當你觸摸一個空的地方(擺動或它可以被稱爲)。我已經嘗試了與ListView的代碼,它似乎工作。但是我不是UWP的專家,因爲我剛剛開始使用它,所以我沒有100%的信心來保證任何事情。 –

+0

我檢查過並且StackPanel確實報告了PointerPressed。但是,禁用ScrollViewer對我來說不是一種選擇,所以在PointerPressed事件觸發後我仍然必須檢測到滑動。我在這方面遇到了一些麻煩,因爲當我在模擬器上滑動時PointerPressed事件沒有被觸發,但只有當我點擊並保持一段時間時纔會觸發。我必須在Surface上查看。 – SebastianR

2

我終於想出瞭如何找回ListView的舊Windows 8.1行爲。如果您垂直滑動滾動軸,它仍然允許通過觸摸滾動並開始拖動操作一個項目。它基於Comet library,由自定義ListView實現。這個想法是允許ListViewItem中的TranslateX/TranslateY和System Manipulations。爲此,您需要重寫默認的ListViewItem風格。

如果你要使用的控制,你必須要記住幾件事情:

  1. 複製在主題/ Generic.xaml的風格和適應local2命名空間。
  2. 如果使用水平滾動的ListView,則必須相應地設置ListView的Orientation屬性。該控件不檢測使用的ItemsPanel。
  3. 您仍然可以使用常規的UWP拖動機制,但您必須訂閱第二個名爲ItemStartDragging的Event,以用於舊的Windows 8.1樣式拖動。
  4. 如果在使用8.1樣式拖動時處理Drop事件,則可以在DragEventArgs.DataView中找到數據,而在使用DragItemStarting(= default事件)時可以在DragEventArgs.Data.GetView()中找到它。不知道他們爲什麼會有不同的表現。
  5. 風格是非常基本的。您可能想要更改它們,並使它們更類似於原始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

謝謝塞巴斯蒂安 - 我會試着去玩這個,看看它是否回答了這個問題 –

+0

幾天前,我已經注意到我的應用程序實現了這個方法後出現了一個相當奇怪的錯誤:一旦用戶已經放棄一個項目的拖放動作是由我的自定義控件啓動到任何ListView上的,傳統的點擊和按住拖動開始在任何應用程序中都不起作用,並且一些控件只有在調整應用程序大小時纔會更新。這些事件似乎被吞噬了。我不確定,如果這是連接到DraggingListView或有其他原因,但我會盡快研究。 – SebastianR