1. 程式人生 > >WPF學習版Win7之TreeView_提高(1)

WPF學習版Win7之TreeView_提高(1)

  1. 為實現附加行為,首先新增引用:System.Windows.Interactivity.dll (還需要引用相應名稱空間,建議先敲上程式碼,再按 Shitf+Alt+F10 顯示提示新增名稱空間)
  2. 新建一類並繼承 Behavior<T> 介面。
    注意這裡是直接把行為應用在 ItemsControl 控制元件(TreeView)而不是 TreeViewItem 上。
    因為像後面將要介紹的主顯示控制元件將會有多種顯示檢視,如果每個檢視都增加拖拽行為不靈活;而且如果在拖拽的物件上附加行為,當按 Ctrl 多選時將較難模擬 Win7 裝飾拖拽的多個物件。
    class ItemsControlDragDropBehavior
    :Behavior<ItemsControl>
  3. 首先方法 OnAttached,OnDetaching,並增加相應委託事件:
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_PreviewMouseLeftButtonDown);
        ......
    }
    protected override void OnDetaching()
    {
        //同理......
    ; }
  4. 滑鼠按下時:_isDown = true;
    滑鼠移動時需要判斷:(關鍵邏輯)
    注意:一呼叫_adornerlayer.CaptureMouse(); 將會再次執行AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)事件。
    void AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (_isDown == false)
            return;
        if (_isDragging == true
    ) { DragMoved(e); } else if (OverMiniDistance(e)) { DragStarted(e); } }
    private void DragStarted(MouseEventArgs e)
    {
        _isDragging = true;
        _adornerlayer.CaptureMouse();
        _startPoint = e.GetPosition(_adornerlayer);
        _adornerlayer = this.AssociatedObject as ItemsControl;
        Object data = Utilities.GetDataContext(_adornerlayer, _startPoint);
        _adorner = new TreeViewItemAdorner(_adornerlayer, data);
        AdornerLayer.GetAdornerLayer(_adornerlayer).Add(_adorner);
    }
  5. 上面的 GetDataContext 方法(這樣就避免了要查詢特定的控制元件型別,靜態方法定義在 Utilities.cs 工具類裡):
    public static object GetDataContext(ItemsControl itemsControl, Point p)
    {
        FrameworkElement element = itemsControl.InputHitTest(p) as FrameworkElement;
        var data = element.DataContext;
        return data;
    }
  6. 最後處理放開滑鼠。
  7. 這裡的滑鼠移動只有(實際是通過改變 TreeViewItemAdorner 裝飾器的屬性來實現移動):
    private void DragMoved(MouseEventArgs e)
    {
        Point CurrentPosition = e.GetPosition(_adornerlayer);
        _adorner.LeftOffset = CurrentPosition.X - _startPoint.X;
        _adorner.TopOffset = CurrentPosition.Y - _startPoint.Y;
    }
  8. 建立一個繼承 Adorner 的類,並且要呼叫基類的建構函式,這樣才能知道在哪個物件上應用裝飾:
    class TreeViewItemAdorner : Adorner
    public TreeViewItemAdorner(UIElement adornedElement, object data)
        : base(adornedElement)
  9. 如果想獲取被裝飾元素的副本,可以在建構函式中:
    VisualBrush _brush = new VisualBrush(adornedElement);
    例如可以在 Rectangle 中填充 _brush。
    當然了,這個示例裡我傳入的 UIElement adornerElement 引數型別為 ItemsControl(即整個 TreeView 控制元件),如果想獲取選中項的 TreeViewItem 可以用 ItemContainerGenerator 或者 VisualTreeHelper 遞迴查詢。
    如果只是在被裝飾元素中加些裝飾物,則一般過載 OnRender 方法並在方法:
    protected override void OnRender(DrawingContext drawingContext){......};
    如:
    drawingContext.DrawImage(bi, adornedElementRect);
    可以加動畫,透明度等,具體 API 可以查詢 MSDN 的 Adorner 或下載本示例程式碼。
  10. 最後編譯程式(Ctrl+Shift+B),可以在 Blend 中的 Assets 面板中拖拽 ItemsControlDragDropBehavior 行為(在 Behaviors 中)。
    drag注意,這個行為稍加修改還可以複用到後面將要介紹的主顯示控制元件中。
    所以 class ItemsControlDragDropBehavior : Behavior<ItemsControl>  中的 T 型別是 ItemsControl。
    程式碼中也儘量免去具體的型別(如 TreeView)。
  11. 為了避免不必要的判斷邏輯,注意 ItemsControlDragDropBehavior 行為只處理 Item 的拖拽,所以可以把紅矩形的去掉,或直接 ''Create Empty'' 建立一個只有 ItemsPresenter 的 TreeView 的 ControlTemplate,或設定 ScrollViewer 控制元件 HorizontalScrollBarVisibility="Disabled",主要目的是使 TreeView 控制元件出現水平滾動條,防止拖拽滾動條時出錯,而且如果目錄樹有多個 TreeView 時出現多個滾動條也不合適。還有,有時因為ScollViewer 可能會佔無限空間,致使拖拽不能移出 ScollViewer範圍。
    當然也可以新增邏輯,判斷拖拽的目標型別。
    簡單的實現方法是讓水平與豎直方向的滾動條放在整個目錄樹的外面。
    remove
  12. 最後把新建的使用者控制元件拖拽到 MainWindow 上,並加個 GridSpitter。 
    over
  13. 附圖:over1