2011-10-25 44 views

回答

2

有一堆不同的方式來做到這一點,但這裏有一對夫婦的想法:

  1. 與X偏移,Y偏移和IsSnapped(默認爲true)附加屬性的自定義面板。當用戶拖動一個項目時,將IsSnapped設置爲false,並在拖動時更新XOffset/YOffset。除非IsSnapped爲false,否則面板的佈局邏輯可以排列具有強對齊的項目(基於其XOffset/YOffset),在這種情況下,它們只出現在(XOffset,YOffset)。
  2. 使用混合提供的現有拖動行爲,但在子項被刪除後強加自己的座標。 UPDATE:這不起作用,或者會變得混亂,因爲Blend會隨時修改拖動元素的RenderTransform,而不是更新一些可以從中進行計算的附加屬性。
  3. 在使用MVVM的視圖模型中占主導地位。也就是說,在視圖模型的頂部,左側,寬度和高度上都有屬性,所有這些屬性都通過行爲(DragBehavior和SizeObserver行爲)進行更新。有一個父視圖模型公開子視圖模型(每個孩子代表一個可拖動的項目)。讓父虛擬機監視器更改爲子座標並根據需要施加。

我現在正在做類似的事情,並已與#3。現在還沒有遺憾,它比你想要在這裏實現的複雜得多,所以你不應該有任何阻滯劑。

UPDATE:這是我的DragDrop行爲:

using System.Diagnostics; 
using System.Windows; 
using System.Windows.Input; 
using System.Windows.Media; 
using Kent.Boogaart.HelperTrinity.Extensions; 

public static class DragDrop 
{ 
    public static readonly RoutedEvent PreviewBeginDragEvent = EventManager.RegisterRoutedEvent(
     "PreviewBeginDrag", 
     RoutingStrategy.Tunnel, 
     typeof(RoutedEventHandler), 
     typeof(DragDrop)); 

    public static readonly RoutedEvent BeginDragEvent = EventManager.RegisterRoutedEvent(
     "BeginDrag", 
     RoutingStrategy.Bubble, 
     typeof(RoutedEventHandler), 
     typeof(DragDrop)); 

    public static readonly RoutedEvent PreviewDragEvent = EventManager.RegisterRoutedEvent(
     "PreviewDrag", 
     RoutingStrategy.Tunnel, 
     typeof(RoutedEventHandler), 
     typeof(DragDrop)); 

    public static readonly RoutedEvent DragEvent = EventManager.RegisterRoutedEvent(
     "Drag", 
     RoutingStrategy.Bubble, 
     typeof(RoutedEventHandler), 
     typeof(DragDrop)); 

    public static readonly RoutedEvent PreviewEndDragEvent = EventManager.RegisterRoutedEvent(
     "PreviewEndDrag", 
     RoutingStrategy.Tunnel, 
     typeof(RoutedEventHandler), 
     typeof(DragDrop)); 

    public static readonly RoutedEvent EndDragEvent = EventManager.RegisterRoutedEvent(
     "EndDrag", 
     RoutingStrategy.Bubble, 
     typeof(RoutedEventHandler), 
     typeof(DragDrop)); 

    public static readonly DependencyProperty CanDragProperty = DependencyProperty.RegisterAttached(
     "CanDrag", 
     typeof(bool), 
     typeof(DragDrop), 
     new FrameworkPropertyMetadata(OnCanDragChanged)); 

    public static readonly DependencyProperty IsDragInProgressProperty; 

    public static readonly DependencyProperty DragParentProperty = DependencyProperty.RegisterAttached(
     "DragParent", 
     typeof(FrameworkElement), 
     typeof(DragDrop)); 

    public static readonly DependencyProperty XOffsetProperty = DependencyProperty.RegisterAttached(
     "XOffset", 
     typeof(double), 
     typeof(DragDrop)); 

    public static readonly DependencyProperty YOffsetProperty = DependencyProperty.RegisterAttached(
     "YOffset", 
     typeof(double), 
     typeof(DragDrop)); 

    private static readonly DependencyPropertyKey isDragInProgressPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
     "IsDragInProgress", 
     typeof(bool), 
     typeof(DragDrop), 
     new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits)); 

    private static readonly DependencyProperty DragPointProperty = DependencyProperty.RegisterAttached(
     "DragPoint", 
     typeof(Point?), 
     typeof(DragDrop), 
     new PropertyMetadata()); 

    static DragDrop() 
    { 
     IsDragInProgressProperty = isDragInProgressPropertyKey.DependencyProperty; 
    } 

    public static void AddPreviewBeginDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.AddHandler(PreviewBeginDragEvent, handler); 
     } 
    } 

    public static void RemovePreviewBeginDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.RemoveHandler(PreviewBeginDragEvent, handler); 
     } 
    } 

    public static void AddBeginDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.AddHandler(BeginDragEvent, handler); 
     } 
    } 

    public static void RemoveBeginDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.RemoveHandler(BeginDragEvent, handler); 
     } 
    } 

    public static void AddPreviewDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.AddHandler(PreviewDragEvent, handler); 
     } 
    } 

    public static void RemovePreviewDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.RemoveHandler(PreviewDragEvent, handler); 
     } 
    } 

    public static void AddDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.AddHandler(DragEvent, handler); 
     } 
    } 

    public static void RemoveDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.RemoveHandler(DragEvent, handler); 
     } 
    } 

    public static void AddPreviewEndDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.AddHandler(PreviewEndDragEvent, handler); 
     } 
    } 

    public static void RemovePreviewEndDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.RemoveHandler(PreviewEndDragEvent, handler); 
     } 
    } 

    public static void AddEndDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.AddHandler(EndDragEvent, handler); 
     } 
    } 

    public static void RemoveEndDragHandler(DependencyObject dependencyObject, RoutedEventHandler handler) 
    { 
     var inputElement = dependencyObject as IInputElement; 

     if (inputElement != null) 
     { 
      inputElement.RemoveHandler(EndDragEvent, handler); 
     } 
    } 

    public static bool GetCanDrag(FrameworkElement frameworkElement) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     return (bool)frameworkElement.GetValue(CanDragProperty); 
    } 

    public static void SetCanDrag(FrameworkElement frameworkElement, bool canDrag) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     frameworkElement.SetValue(CanDragProperty, canDrag); 
    } 

    public static FrameworkElement GetDragParent(DependencyObject dependencyObject) 
    { 
     dependencyObject.AssertNotNull("dependencyObject"); 
     return dependencyObject.GetValue(DragParentProperty) as FrameworkElement; 
    } 

    public static void SetDragParent(DependencyObject dependencyObject, FrameworkElement dragParent) 
    { 
     dependencyObject.AssertNotNull("dependencyObject"); 
     dependencyObject.SetValue(DragParentProperty, dragParent); 
    } 

    public static double GetXOffset(FrameworkElement frameworkElement) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     return (double)frameworkElement.GetValue(XOffsetProperty); 
    } 

    public static void SetXOffset(FrameworkElement frameworkElement, double xOffset) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     frameworkElement.SetValue(XOffsetProperty, xOffset); 
    } 

    public static double GetYOffset(FrameworkElement frameworkElement) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     return (double)frameworkElement.GetValue(YOffsetProperty); 
    } 

    public static void SetYOffset(FrameworkElement frameworkElement, double yOffset) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     frameworkElement.SetValue(YOffsetProperty, yOffset); 
    } 

    public static bool GetIsDragInProgress(DependencyObject dependencyObject) 
    { 
     dependencyObject.AssertNotNull("dependencyObject"); 
     return (bool)dependencyObject.GetValue(IsDragInProgressProperty); 
    } 

    private static void SetIsDragInProgress(DependencyObject dependencyObject, bool isDragInProgress) 
    { 
     dependencyObject.AssertNotNull("dependencyObject"); 
     dependencyObject.SetValue(isDragInProgressPropertyKey, isDragInProgress); 
    } 

    private static Point? GetDragPoint(FrameworkElement frameworkElement) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     return (Point?)frameworkElement.GetValue(DragPointProperty); 
    } 

    private static void SetDragPoint(FrameworkElement frameworkElement, Point? dragPoint) 
    { 
     frameworkElement.AssertNotNull("frameworkElement"); 
     frameworkElement.SetValue(DragPointProperty, dragPoint); 
    } 

    private static void OnCanDragChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
    { 
     var frameworkElement = (FrameworkElement)dependencyObject; 

     if ((bool)e.NewValue) 
     { 
      frameworkElement.MouseLeftButtonDown += OnFrameworkElementMouseLeftButtonDown; 
      frameworkElement.MouseMove += OnFrameworkElementMouseMove; 
      frameworkElement.MouseLeftButtonUp += OnFrameworkElementMouseLeftButtonUp; 

      var parent = GetDragParent<FrameworkElement>(frameworkElement); 

      if (parent == null) 
      { 
       frameworkElement.SetCurrentValue(XOffsetProperty, 0d); 
       frameworkElement.SetCurrentValue(YOffsetProperty, 0d); 
      } 
      else 
      { 
       var pointRelativeToParent = frameworkElement.TranslatePoint(new Point(0, 0), parent); 
       frameworkElement.SetCurrentValue(XOffsetProperty, pointRelativeToParent.X); 
       frameworkElement.SetCurrentValue(YOffsetProperty, pointRelativeToParent.Y); 
      } 
     } 
     else 
     { 
      frameworkElement.MouseLeftButtonDown -= OnFrameworkElementMouseLeftButtonDown; 
      frameworkElement.MouseMove -= OnFrameworkElementMouseMove; 
      frameworkElement.MouseLeftButtonUp -= OnFrameworkElementMouseLeftButtonUp; 
     } 
    } 

    private static void OnFrameworkElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
    { 
     var frameworkElement = (FrameworkElement)sender; 
     var parent = GetDragParent<FrameworkElement>(frameworkElement); 

     if (parent == null) 
     { 
      return; 
     } 

     var previewBeginDragEventArgs = new RoutedEventArgs(PreviewBeginDragEvent); 
     frameworkElement.RaiseEvent(previewBeginDragEventArgs); 

     if (previewBeginDragEventArgs.Handled) 
     { 
      return; 
     } 

     SetIsDragInProgress(frameworkElement, true); 
     SetDragPoint(frameworkElement, e.GetPosition(parent)); 
     frameworkElement.CaptureMouse(); 
     frameworkElement.RaiseEvent(new RoutedEventArgs(BeginDragEvent)); 
    } 

    private static void OnFrameworkElementMouseMove(object sender, MouseEventArgs e) 
    { 
     var frameworkElement = (FrameworkElement)sender; 

     if (frameworkElement.IsMouseCaptured) 
     { 
      var previewDragEventArgs = new RoutedEventArgs(PreviewDragEvent); 
      frameworkElement.RaiseEvent(previewDragEventArgs); 

      if (previewDragEventArgs.Handled) 
      { 
       return; 
      } 

      var parent = GetDragParent<FrameworkElement>(frameworkElement); 

      if (parent == null) 
      { 
       return; 
      } 

      var currentPointRelativeToParent = e.GetPosition(parent); 
      var dragPoint = GetDragPoint(frameworkElement); 
      Debug.Assert(dragPoint.HasValue, "dragPoint should be set."); 

      frameworkElement.SetCurrentValue(XOffsetProperty, GetXOffset(frameworkElement) + (currentPointRelativeToParent.X - dragPoint.Value.X)); 
      frameworkElement.SetCurrentValue(YOffsetProperty, GetYOffset(frameworkElement) + (currentPointRelativeToParent.Y - dragPoint.Value.Y)); 
      SetDragPoint(frameworkElement, currentPointRelativeToParent); 
      frameworkElement.RaiseEvent(new RoutedEventArgs(DragEvent)); 
     } 
    } 

    private static void OnFrameworkElementMouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
    { 
     var frameworkElement = (FrameworkElement)sender; 

     if (frameworkElement.IsMouseCaptured) 
     { 
      frameworkElement.RaiseEvent(new RoutedEventArgs(PreviewEndDragEvent)); 
      SetDragPoint(frameworkElement, null); 
      frameworkElement.ReleaseMouseCapture(); 
      frameworkElement.RaiseEvent(new RoutedEventArgs(EndDragEvent)); 
      SetIsDragInProgress(frameworkElement, false); 
     } 
    } 

    private static T GetDragParent<T>(DependencyObject dependencyObject) 
     where T : DependencyObject 
    { 
     var dragParent = GetDragParent(dependencyObject) as T; 

     if (dragParent != null) 
     { 
      return dragParent; 
     } 

     var parent = VisualTreeHelper.GetParent(dependencyObject); 

     while (parent != null && !(parent is T)) 
     { 
      parent = VisualTreeHelper.GetParent(parent); 
     } 

     return (T)parent; 
    } 
} 
+0

非常感謝您的幫助。我猜想選項二將是最簡單的。唯一的問題是,當你拖動一個控件時,如何在使用它時檢索座標?我認爲這是我爲了實現你的想法編號2 –

+0

我需要知道的唯一事情你不應該爲佈局強加職位,這是一個小組的責任。 –

+0

是的,但是當你在一個面板內的對象上調用CaptureMouse()方法時,面板的行爲就像是在不同的控件所在的位置。一旦我完成拖動用戶控件,它不會對齊它。 –

1

如果你想要這個動態發生,那麼我認爲最好的解決辦法是建立一個UniformWrapPanel從WrapPanel派生,然後重寫的MeasureOverride和ArrangeOverride方法。然後在MeasureOverride方法中,記下最大物品所需寬度(用於水平對齊),然後在ArrangeOverride方法中,對於經線編排過程中的每一行,獲取每個物品以將其自身排列在總是偏移最大值的矩形中項目所需的寬度。

如果你知道這些項目的大小,那麼我相信它可以通過在WrapPanel上設置ItemWidth和ItemHeight屬性來工作。例如:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <Grid> 
    <WrapPanel ItemWidth="150"> 
     <Rectangle Width="50" Height="50" Fill="Red" Margin="10" /> 
     <Rectangle Width="150" Height="50" Fill="Red" Margin="10" /> 
     <Rectangle Width="20" Height="50" Fill="Red" Margin="10" /> 
     <Rectangle Width="50" Height="50" Fill="Red" Margin="10" /> 
    </WrapPanel> 
    </Grid> 
</Page> 

黑客攻擊的解決方案可能是從WrapPanel派生,重寫ArrangeOverride方法,迭代所有的兒童和發現的最大尺寸,設置ItemWidth和ItemHeight屬性,然後調用base.ArrangeOverride做其餘的工作。