2009-03-12 29 views
5

Trong ứng dụng của tôi, tôi có TreeView cho phép kéo/thả. Tôi có tất cả các chức năng làm việc tốt, tuy nhiên tôi gặp khó khăn làm nổi bật một TreeViewItem khi nó được kéo qua. Đây là phong cách của tôi cho mục treeview của tôi. Trình kích hoạt IsMouseOver không hoạt động khi đang kéo, vì việc kéo dường như chặn các sự kiện chuột khác. Bất cứ ai có thể giúp tôi kích hoạt các thay đổi biên giới tương tự trên mục treeview của tôi trong khi kéo?Tô sáng mục TreeView đang được kéo trên

<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TreeViewItem}"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition MinWidth="19" Width="Auto"/> 
         <ColumnDefinition Width="Auto"/> 
         <ColumnDefinition Width="*"/> 
        </Grid.ColumnDefinitions> 
        <Grid.RowDefinitions> 
         <RowDefinition Height="Auto"/> 
         <RowDefinition/> 
        </Grid.RowDefinitions> 
        <ToggleButton 
         x:Name="PART_Expander" 
         Style="{StaticResource ExpandCollapseToggleStyle}" 
         IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" 
         ClickMode="Press" 
         /> 
        <Border 
         x:Name="OuterBorder" 
         Grid.Column="1" 
         SnapsToDevicePixels="True" 
         BorderThickness="1" 
         CornerRadius="3" 
         BorderBrush="Transparent" 
         Background="Transparent" 
         > 
         <Border 
          x:Name="InnerBorder" 
          SnapsToDevicePixels="True" 
          BorderThickness="1" 
          CornerRadius="2" 
          BorderBrush="Transparent" 
          Background="Transparent" 
          > 
          <ContentPresenter 
           x:Name="PART_Content" 
           ContentSource="Header" 
           HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
           /> 
         </Border> 
        </Border> 
        <ItemsPresenter 
         x:Name="PART_ItemsHost" 
         Grid.Row="1" 
         Grid.Column="1" 
         Grid.ColumnSpan="2" 
         /> 
       </Grid> 
       <ControlTemplate.Triggers> 
        <Trigger Property="IsMouseOver" SourceName="OuterBorder" Value="True"> 
         <Setter TargetName="OuterBorder" Property="BorderBrush" Value="Blue" /> 
         <Setter TargetName="OuterBorder" Property="Background" Value="Red" /> 
         <Setter TargetName="InnerBorder" Property="BorderBrush" Value="White" /> 
        </Trigger> 
        <MultiTrigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Trả lời

16

Tôi đang sử dụng một tài sản gắn liền cho điều này, và sau đó sử dụng tài sản đó trong file XAML của tôi để thay đổi màu nền của mặt hàng đó xem dạng cây:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 

namespace SKNotes.Utilities 
{ 
    /// <summary> 
    /// Implements an attached property used for styling TreeViewItems when 
    /// they're a possible drop target. 
    /// </summary> 
    public static class TreeViewDropHighlighter 
    { 
     #region private variables 
     /// <summary> 
     /// the TreeViewItem that is the current drop target 
     /// </summary> 
     private static TreeViewItem _currentItem = null; 

     /// <summary> 
     /// Indicates whether the current TreeViewItem is a possible 
     /// drop target 
     /// </summary> 
     private static bool _dropPossible; 
     #endregion 

     #region IsPossibleDropTarget 
     /// <summary> 
     /// Property key (since this is a read-only DP) for the IsPossibleDropTarget property. 
     /// </summary> 
     private static readonly DependencyPropertyKey IsPossibleDropTargetKey = 
            DependencyProperty.RegisterAttachedReadOnly(
             "IsPossibleDropTarget", 
             typeof(bool), 
             typeof(TreeViewDropHighlighter), 
             new FrameworkPropertyMetadata(null, 
              new CoerceValueCallback(CalculateIsPossibleDropTarget))); 


     /// <summary> 
     /// Dependency Property IsPossibleDropTarget. 
     /// Is true if the TreeViewItem is a possible drop target (i.e., if it would receive 
     /// the OnDrop event if the mouse button is released right now). 
     /// </summary> 
     public static readonly DependencyProperty IsPossibleDropTargetProperty = IsPossibleDropTargetKey.DependencyProperty; 

     /// <summary> 
     /// Getter for IsPossibleDropTarget 
     /// </summary> 
     public static bool GetIsPossibleDropTarget(DependencyObject obj) 
     { 
      return (bool)obj.GetValue(IsPossibleDropTargetProperty); 
     } 

     /// <summary> 
     /// Coercion method which calculates the IsPossibleDropTarget property. 
     /// </summary> 
     private static object CalculateIsPossibleDropTarget(DependencyObject item, object value) 
     { 
      if ((item == _currentItem) && (_dropPossible)) 
       return true; 
      else 
       return false; 
     } 
     #endregion 

     /// <summary> 
     /// Initializes the <see cref="TreeViewDropHighlighter"/> class. 
     /// </summary> 
     static TreeViewDropHighlighter() 
     { 
      // Get all drag enter/leave events for TreeViewItem. 
      EventManager.RegisterClassHandler(typeof(TreeViewItem), 
             TreeViewItem.PreviewDragEnterEvent, 
             new DragEventHandler(OnDragEvent), true); 
      EventManager.RegisterClassHandler(typeof(TreeViewItem), 
             TreeViewItem.PreviewDragLeaveEvent, 
             new DragEventHandler(OnDragLeave), true); 
      EventManager.RegisterClassHandler(typeof(TreeViewItem), 
             TreeViewItem.PreviewDragOverEvent, 
             new DragEventHandler(OnDragEvent), true); 
     } 

     #region event handlers 
     /// <summary> 
     /// Called when an item is dragged over the TreeViewItem. 
     /// </summary> 
     /// <param name="sender">The sender.</param> 
     /// <param name="args">The <see cref="System.Windows.DragEventArgs"/> instance containing the event data.</param> 
     static void OnDragEvent(object sender, DragEventArgs args) 
     { 
      lock (IsPossibleDropTargetProperty) 
      { 
       _dropPossible = false; 

       if (_currentItem != null) 
       { 
        // Tell the item that previously had the mouse that it no longer does. 
        DependencyObject oldItem = _currentItem; 
        _currentItem = null; 
        oldItem.InvalidateProperty(IsPossibleDropTargetProperty); 
       } 

       if (args.Effects != DragDropEffects.None) 
       { 
        _dropPossible = true; 
       } 

       TreeViewItem tvi = sender as TreeViewItem; 
       if (tvi != null) 
       { 
        _currentItem = tvi; 
        // Tell that item to re-calculate the IsPossibleDropTarget property 
        _currentItem.InvalidateProperty(IsPossibleDropTargetProperty); 
       } 
      } 
     } 

     /// <summary> 
     /// Called when the drag cursor leaves the TreeViewItem 
     /// </summary> 
     /// <param name="sender">The sender.</param> 
     /// <param name="args">The <see cref="System.Windows.DragEventArgs"/> instance containing the event data.</param> 
     static void OnDragLeave(object sender, DragEventArgs args) 
     { 
      lock (IsPossibleDropTargetProperty) 
      { 
       _dropPossible = false; 

       if (_currentItem != null) 
       { 
        // Tell the item that previously had the mouse that it no longer does. 
        DependencyObject oldItem = _currentItem; 
        _currentItem = null; 
        oldItem.InvalidateProperty(IsPossibleDropTargetProperty); 
       } 

       TreeViewItem tvi = sender as TreeViewItem; 
       if (tvi != null) 
       { 
        _currentItem = tvi; 
        tvi.InvalidateProperty(IsPossibleDropTargetProperty); 
       } 
      } 
     } 
     #endregion 
    } 
} 

và sau đó trong file XAML:

<TreeView.ItemContainerStyle> 
     <Style TargetType="{x:Type TreeViewItem}"> 
      <Setter Property="FontWeight" Value="Normal" /> 
      <Style.Triggers> 
       <Trigger Property="utils:TreeViewDropHighlighter.IsPossibleDropTarget" Value="True"> 
        <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </TreeView.ItemContainerStyle> 
+0

Đó là một điều tuyệt vời, cảm ơn bạn rất nhiều! –

+2

Đây chính xác là những gì tôi cần hôm nay. Cảm ơn Stefan rất nhiều! Trong treeview của tôi tôi nhận thấy mặc dù đôi khi nó sẽ không tự động loại bỏ các điểm nổi bật từ thả của tôi, vì vậy tôi đã đăng ký sự kiện thả và viết một hàm đơn giản mà đặt _dropPossible trở lại false và Invalidates IsPossibleDropTargetProperty. Có thể hữu ích cho người khác? – chocojosh

+0

Công việc tuyệt vời! Một điều mà tôi thêm vào ngoài những gì #chocojosh thêm vào, đang nói cho TreeViewItem mở rộng nếu nó có các mục trên sự kiện OnDragEvent. Bạn có thể làm điều này bằng cách thêm: nếu (_currentItem.HasItems) _currentItem.IsExpanded = true; – Brent

4

Nhìn vào sự kiện DragOver (và có thể DragEnter/DragLeave) thay vì IsMouseOver.

+0

"DragOver" không phải là thuộc tính hợp lệ để đặt trình kích hoạt theo cùng kiểu với "IsMouseOver". Tôi có thể sử dụng một EventTrigger, tuy nhiên họ dường như chỉ chấp nhận StoryBoards và tôi không thể tìm ra cách thiết lập thuộc tính biên giới của tôi trong StoryBoard giống như cách tôi đã làm cho các trình giải quyết IsMouseOver. –

+0

Tôi đã phải thêm một dòng vào hàm tạo TreeViewDropHighlighter(), cũng để loại bỏ các thay đổi sau khi thực hiện xong việc thả. EventManager.RegisterClassHandler (typeof (TreeViewItem), TreeViewItem.PreviewDropEvent, mới DragEventHandler (OnDragLeave), true) ; –

+0

Đó là một sự kiện. Xin lỗi điều đó không rõ ràng! Bạn có thể sử dụng trình thiết lập sự kiện theo kiểu của bạn để thêm nó vào tất cả các mục xem dạng cây của bạn. –

1

Bài đăng này chỉ đơn giản là việc chuyển giao phản hồi tuyệt vời của Stefan ở trên cho VB, cho những người trong chúng ta làm việc trong những giới hạn đó; Tôi không có gì để cung cấp ngoại trừ hy vọng tôi đã không vít bất cứ điều gì lên quá nặng. Nó xuất hiện để làm việc cho tôi:

Namespace SKNotes.Utilities 

''' <summary> 
''' Implements an attached property used for styling TreeViewItems when 
''' they are a possible drop target. 
''' </summary> 
Public Class TreeViewDropHighlighter 

    ''' <summary> 
    ''' The TreeViewItem that is the current drop target 
    ''' </summary> 
    Private Shared _CurrentItem As TreeViewItem = Nothing 

    ''' <summary> 
    ''' Indicates whether the current TreeView Item is a possible drop target 
    ''' </summary> 
    ''' <remarks></remarks> 
    Private Shared _DropPossible As Boolean 

    ''' <summary> 
    ''' Property Key (since this is a read only DP) for the IsPossibleDropTarget property. 
    ''' </summary> 
    ''' <remarks></remarks> 
    Private Shared ReadOnly IsPossibleDropTargetKey As DependencyPropertyKey = _ 
              DependencyProperty.RegisterAttachedReadOnly _ 
              (_ 
               "IsPossibleDropTarget", _ 
               GetType(Boolean), _ 
               GetType(TreeViewDropHighlighter), _ 
               New FrameworkPropertyMetadata(Nothing, _ 
                       New CoerceValueCallback(AddressOf CalculateIsPossibleDropTarget) 
                       ) 
              ) 
    ''' <summary> 
    ''' Dependency Property IsPossibleDropTarget. 
    ''' Is true if the TreeViewItem is a possible drop target (ie, if it would receive the 
    ''' OnDrop even if the mouse button is release right now). 
    ''' </summary> 
    ''' <remarks></remarks> 
    Public Shared ReadOnly IsPossibleDropTargetProperty As DependencyProperty = IsPossibleDropTargetKey.DependencyProperty 

    ''' <summary> 
    ''' Getter for IsPossibleDropTarget 
    ''' </summary> 
    Public Shared Function GetIsPossibleDropTarget(ByVal obj As DependencyObject) As Boolean 
     Return CBool(obj.GetValue(IsPossibleDropTargetProperty)) 
    End Function 

    ''' <summary> 
    ''' Coercion method which calculates the IsPossibleDropTarget property 
    ''' </summary> 
    Private Shared Function CalculateIsPossibleDropTarget(item As DependencyObject, value As Object) As Object 
     If item.Equals(_CurrentItem) And _ 
      _DropPossible Then 
      Return True 
     Else 
      Return False 
     End If 
    End Function 

    ''' <summary> 
    ''' Initializes the TreeViewDropHighlighter class 
    ''' </summary> 
    Shared Sub New() 
     EventManager.RegisterClassHandler(GetType(TreeViewItem), _ 
              TreeViewItem.PreviewDragEnterEvent, _ 
              New DragEventHandler(AddressOf OnDragEvent), True) 
     EventManager.RegisterClassHandler(GetType(TreeViewItem), _ 
              TreeViewItem.PreviewDragLeaveEvent, 
              New DragEventHandler(AddressOf OnDragLeave), True) 
     EventManager.RegisterClassHandler(GetType(TreeViewItem), _ 
              TreeViewItem.PreviewDragOverEvent, _ 
              New DragEventHandler(AddressOf OnDragEvent), True) 
    End Sub 

    ''' <summary> 
    ''' Called when an item is dragged over the TreeView Item 
    ''' </summary> 
    ''' <param name="sender">The sender</param> 
    ''' <param name="args">The System.Windows.DragEventArgs instance containing the event data</param> 
    ''' <remarks></remarks> 
    Shared Sub OnDragEvent(sender As Object, args As DragEventArgs) 
     SyncLock (IsPossibleDropTargetProperty) 
      _DropPossible = False 
      If Not IsNothing(_CurrentItem) Then 
       'Tell the item that previously had the mouse that it no longer does. 
       Dim OldItem As DependencyObject = _CurrentItem 
       _CurrentItem = Nothing 
       OldItem.InvalidateProperty(IsPossibleDropTargetProperty) 
      End If 

      If args.Effects <> DragDropEffects.None Then 
       _DropPossible = True 
      End If 

      Dim tvi As TreeViewItem = CType(sender, TreeViewItem) 
      If Not IsNothing(tvi) Then 
       _CurrentItem = tvi 
       'Tell that item to recalculate the IsPossibleDropTarget property 
       _CurrentItem.InvalidateProperty(IsPossibleDropTargetProperty) 
      End If 
     End SyncLock 
    End Sub 

    Shared Sub OnDragLeave(sender As Object, args As DragEventArgs) 

     SyncLock (IsPossibleDropTargetProperty) 
      _DropPossible = False 
      If Not IsNothing(_CurrentItem) Then 
       'Tell the item that previously had the mouse that it no longer does 
       Dim OldItem As DependencyObject = _CurrentItem 
       _CurrentItem = Nothing 
       OldItem.InvalidateProperty(IsPossibleDropTargetProperty) 
      End If 

      Dim tvi As TreeViewItem = CType(sender, TreeViewItem) 
      If Not IsNothing(tvi) Then 
       _CurrentItem = tvi 
       tvi.InvalidateProperty(IsPossibleDropTargetProperty) 
      End If 

     End SyncLock 

    End Sub 

End Class 

End Namespace

+0

Cảm ơn dansan, nó hoạt động hoàn hảo! Chúng tôi, những người VB là một giống chó chết và cần phải gắn bó với nhau! –

1

Ngoài câu trả lời Stefan, tôi thấy rằng là cần thiết để thêm cũng là sự kiện Drop:

static void OnDragDrop(object sender, DragEventArgs args) 
{ 
    lock (IsPossibleDropTargetProperty) 
    { 
     _dropPossible = false; 

     if (_currentItem != null) 
     { 
      _currentItem.InvalidateProperty(IsPossibleDropTargetProperty); 
     } 

     TreeViewItem tvi = sender as TreeViewItem; 
     if (tvi != null) 
     { 
      tvi.InvalidateProperty(IsPossibleDropTargetProperty); 
     } 
    } 
} 

và đăng ký thả sự kiện cũng :

EventManager.RegisterClassHandler(typeof(TreeViewItem), 
      TreeViewItem.PreviewDropEvent, 
      new DragEventHandler(OnDragDrop), true); 

Nếu không xảy ra sự sụt giảm của một câyNó khác, nền có thể không thay đổi. Điều tương tự nếu bạn nhấn ESC.

Các vấn đề liên quan