2013-10-23 80 views
4

我有一個Collection項目綁定到ItemsControl元素。在ItemsControl中移動行爲

我需要啓用每個元素的鼠標拖動行爲,但我似乎無法使其工作。

將此代碼添加

<i:Interaction.Behaviors> 
    <el:MouseDragElementBehavior/> 
</i:Interaction.Behaviors> 

將使其工作,但只有當我把我的項目ItemsControl之外。

這裏是ItemsControl組件的代碼:

<ItemsControl ItemsSource="{Binding Items}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <utils:TemplateSelector Content="{Binding}"/> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

我做錯什麼了嗎?

編輯:新代碼,但仍無法正常工作(有和沒有Canvas

 <ItemsControl ItemsSource="{Binding Items}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <utils:TemplateSelector Content="{Binding}"> 
         <i:Interaction.Behaviors> 
          <el:MouseDragElementBehavior/> 
         </i:Interaction.Behaviors> 
        </utils:TemplateSelector> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
      <ItemsControl.ItemsPanel> 
       <ItemsPanelTemplate> 
        <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/> 
       </ItemsPanelTemplate> 
      </ItemsControl.ItemsPanel> 
     </ItemsControl> 
+0

通過鼠標拖動,你的意思是拖放類型的行爲,如[AllowDrop](http://msdn.microsoft.com/en-us/library/system.windows.uielement.allowdrop.aspx)功能?或者你是否只是試圖提供物品,並允許每一個被拖動到整個屏幕? –

+0

第二個。我只需要通過握住和移動它們來「重新定位」控件。沒有拖放或類似的東西 – StepTNT

+0

嗯,並且你將行爲附加到'ItemTemplate'中的項目上?你可以試着將你的'ItemPanelTemplate'設置爲一個'Canvas',起初我以爲你有事搞亂了它,但它是一個ItemsControl,所以它不應該這樣做,但沒有測試它很難說。希望我對你有一個絕對的答案,但這是我最終想弄明白的那些事情之一。 –

回答

3

好像,一旦你開始你突然發現一個解決方案的賞金,你沒能在過去幾個月中找到。

我回答我的解決方案,目前的工作,但肯定可以做得更好。

什麼,我是基於兩個不同來源的提議:由科林·埃伯哈特

讓我們先從XAML:

<ItemsControl ItemsSource="{Binding Items}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <Border BorderBrush="{StaticResource PhoneForegroundBrush}"> 
       <utils:TemplateSelector Content="{Binding}"/> 
       <toolkit:GestureService.GestureListener> 
        <toolkit:GestureListener 
         DragStarted="GestureListener_OnDragStarted" 
         DragDelta="GestureListener_OnDragDelta" 
         DragCompleted="GestureListener_OnDragCompleted"/> 
       </toolkit:GestureService.GestureListener> 
      </Border> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <Canvas x:Name="WidgetsCanvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
</ItemsControl> 

toolkit來自Windows Phone Toolkit

現在讓我們來看看這三個阻力處理程序的執行。

private int _zindex; //Used to keep the moved element on the top, not working for now 
private FrameworkElement _movedObject; //The element that we're moving. Used to avoid moving multiple items if they overlap 

private void GestureListener_OnDragStarted(object sender, DragStartedGestureEventArgs e) 
{ 
    if (_movedObject != null) return; // We're already moving something!    
    // Initialize the drag    
    var fe = sender as FrameworkElement; // The element that we want to move 
    (fe as Border).BorderThickness = new Thickness(5); // A simple effect to mark the element on the screen 
    _movedObject = fe; // We set the current object to the one which is moving 
    Canvas.SetZIndex(fe, _zindex++); // This should take the moved object on the top but it's not working 
} 

private void GestureListener_OnDragDelta(object sender, DragDeltaGestureEventArgs e) 
{ 
    var fe = sender as FrameworkElement; 
    if (!fe.Equals(_movedObject)) return; // We change the object's position only if this is the one who started the event 
    var offset = DragManager.GetOffset(fe); // We get the current position 
    var canvas = DragManager.FindChild<Canvas>(Application.Current.RootVisual, "ItemsCanvas"); // We need the container of our object to force it to stay inside the container 
    //The new position is given by the old one plus the change reported by the event 
    var horizontalOffset = offset.HorizontalValue + e.HorizontalChange; 
    var verticalOffset = offset.VerticalValue + e.VerticalChange; 
    // We need to check if the new position is outside our container's bounds 
    if (horizontalOffset < 0) horizontalOffset = 0; 
    else if (horizontalOffset > (canvas.ActualWidth - fe.ActualWidth)) horizontalOffset = canvas.ActualWidth - fe.ActualWidth; 
    if (verticalOffset < 0) verticalOffset = 0; 
    else if (verticalOffset > (canvas.ActualHeight - fe.ActualHeight)) verticalOffset = canvas.ActualHeight - fe.ActualHeight;   
    // Once we've got everything set, we can move our component 
    DragManager.SetOffset(fe, horizontalOffset, verticalOffset); 
} 

private void GestureListener_OnDragCompleted(object sender, DragCompletedGestureEventArgs e) 
{ 
    var fe = sender as FrameworkElement;      
    (fe as Border).BorderThickness = new Thickness(0); // We undo our effect 
    _movedObject = null; // The movement is done so we can reset our current object and wait for a new one to come 
} 

處理程序使用我稱爲DragManager的類。它的代碼是相當simplet:

public static class DragManager 
{ 
    public static void SetOffset(FrameworkElement fe, double horizontalOffset, double verticalOffset) 
    { 
     var trans = new TranslateTransform 
     { 
      X = horizontalOffset, 
      Y = verticalOffset 
     }; 
     // I don't know what may change, in terms of performance, between applying the transform or just changing the margins. I'm using the margins because the transform may be needed for some other purpose 
     //fe.RenderTransform = trans;    
     fe.Margin = new Thickness(horizontalOffset, verticalOffset, 0, 0); // We just change our object's margins to reflect its new position 
     // We store the current position in the objects Tag (maybe there's a better solution but I'm quite new to C#/xaml) 
     fe.Tag = new Offset 
     { 
      VerticalValue = verticalOffset, 
      HorizontalValue = horizontalOffset, 
      Transform = trans 
     }; 
    } 

    public static Offset GetOffset(FrameworkElement fe) 
    { 
     if (fe.Tag == null) fe.Tag = new Offset(); 
     return (Offset)fe.Tag; 
    } 

    public struct Offset 
    { 
     public double HorizontalValue { get; set; } 
     public double VerticalValue { get; set; } 
     public TranslateTransform Transform { get; set; } 
    } 

    public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject 
    { 
     // Confirm parent and childName are valid. 
     if (parent == null) 
     { 
      return null; 
     } 

     T foundChild = null; 

     var childrenCount = VisualTreeHelper.GetChildrenCount(parent); 
     for (var i = 0; i < childrenCount; i++) 
     { 
      var child = VisualTreeHelper.GetChild(parent, i); 
      // If the child is not of the request child type child 
      var childType = child as T; 
      if (childType == null) 
      { 
       // recursively drill down the tree 
       foundChild = FindChild<T>(child, childName); 

       // If the child is found, break so we do not overwrite the found child. 
       if (foundChild != null) 
       { 
        break; 
       } 
      } 
      else if (!String.IsNullOrEmpty(childName)) 
      { 
       var frameworkElement = child as FrameworkElement; 
       // If the child's name is set for search 
       if (frameworkElement != null && frameworkElement.Name == childName) 
       { 
        // if the child's name is of the request name 
        foundChild = (T)child; 
        break; 
       } 

       // Need this in case the element we want is nested 
       // in another element of the same type 
       foundChild = FindChild<T>(child, childName); 
      } 
      else 
      { 
       // child element found. 
       foundChild = (T)child; 
       break; 
      } 
     } 

     return foundChild; 
    } 
} 

正如我已經說過,這個代碼工作現在。

如果有人有一些改進它的建議,請在這裏寫下來,以便我可以看到/測試一個更好的方式來做這種事情!