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

來源:互聯網
上載者:User

朋友們,注意了!從本文開始,將使用 Visual Studio 2010 , Blend 4 作為主要開發工具。

本文包括:
  • Converter 綁定轉換及修改模板完善 TreeViewItem 在 MouseOver 時呈現外觀
  • Behavior 附加拖拽行為
  • Adorner 裝飾器裝飾拖拽效果
完善 TreeView 外觀
  1. 建立一 WPF 應用程式。
    (注意本節開始用 VS2010 和 Blend 4 開發,使用 VS 08 或 Blend 3 將無法正常開啟工程項目。實在抱歉!但不影響查看代碼學習!)
  2. 建立一使用者控制項,參見上節用 Blend 建立基本 TreeView 外觀。
    (整個 Win7 學習版不小,接下來的章節也將會建立繼承 UserControl 或 Control 的使用者控制項,以方便管理與複用)
  3. TreeViewItem 的 ControlTemplate 實質布局如,一個兩行三列的 Grid:
  4. 預設布局致使子節點(即 ItemHost)總會比父節點向右位移第一列的寬度(值:19)。
    為達到位移效果並且使得 ItemHost 能橫跨所有列(這樣 Border 就可以填充整行了),現在修改如,兩行一列的 Grid,StackPanel 左邊被 Margin 填充(值:19):
  5. XAML 中 StackPanel 的 Margin 綁定如下(綁定對象是 TreeViewItem):
    <StackPanel x:Name="stackPanel"                                            Orientation="Horizontal"                                            Margin="{Binding RelativeSource=
    {RelativeSource FindAncestor, AncestorType=TreeViewItem, AncestorLevel=1}, Converter={StaticResource ConverterLoginMarginLeft}}">
  6. ConverterLoginMarginLeft類定義如下(利用 VisualTreeHelper):

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){    double columnWidth = 19.0;    double left = 0.0;    UIElement element = value as TreeViewItem;    while (element.GetType() != typeof(TreeView))    {        element = (UIElement)VisualTreeHelper.GetParent(element);        if (element.GetType() == typeof(TreeViewItem))            left += columnWidth;    }    return new Thickness(left,0,0,0);}
  7. 最初做法是綁定到到父節點的 StackPanel,然後擷取其 Margin 並在原基礎上加一值(如 19),但受 WPF 布局系統的布局順序影響(如果用 Mode=OneWaytoSource)以及 Converter 在 OneWay 模式下目標資料的更新並沒有導致資料來源更改,以致尋找上級 StackPanel 時 Margin 值沒增加(如果 Model=Default),所以Border 都沒能成功填充整行。
附加行為,裝飾器
  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 : Adornerpublic 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 中)。

    注意,這個行為稍加修改還可以複用到後面將要介紹的主顯示控制項中。

    所以 class ItemsControlDragDropBehavior : Behavior<ItemsControl>  中的 T 類型是 ItemsControl。

    代碼中也盡量免去具體的類型(如 TreeView)。

  11. 為了避免不必要的判斷邏輯,注意 ItemsControlDragDropBehavior 行為只處理 Item 的拖拽,所以可以把紅矩形的去掉,或直接 ''Create Empty'' 建立一個只有 ItemsPresenter 的 TreeView 的 ControlTemplate,或設定 ScrollViewer 控制項 HorizontalScrollBarVisibility="Disabled",主要目的是使 TreeView 控制項出現水平捲軸,防止拖拽捲軸時出錯,而且如果分類樹有多個 TreeView 時出現多個捲軸也不合適。還有,有時因為ScollViewer 可能會佔無限空間,致使拖拽不能移出 ScollViewer範圍。

    當然也可以添加邏輯,判斷拖拽的目標類型。

    簡單的實現方法是讓水平與豎直方向的捲軸放在整個分類樹的外面。

  12. 最後把建立的使用者控制項拖拽到 MainWindow 上,並加個 GridSpitter。 
  13. 附圖:

 

小結
  1. 通過修改基本控制項的模板樣式,可以定製外觀,注意利用 Blend 的可視化介面學習預設控制項的布局與模板樣式。
  2. VisualTreeHelper 與 ItemContainerGenerator 有幾個常用的靜態方法方法尋找控制項。

    例如可以尋找樣式模板中的元素控制項。
  3. 附加行為松耦合并靈活複用。

    與附加屬性不同,可以用附加行為處理行為;當涉及綁定時可以考慮用附加屬性。
  4. 整個應用程式都常用到的方法可以建個工具類,方法為靜態方法。如擷取滑鼠座標等。
TreeView 的 Drop 等更多內容將在下節介紹。如有建議或意見歡迎留言。

代碼下載

Technorati 標籤: WPF,TreeView,Adorner,Behavior
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.