【问题标题】:Highlight TreeView item being dragged over突出显示被拖动的 TreeView 项目
【发布时间】:2010-10-12 23:56:56
【问题描述】:

在我的应用程序中,我有一个允许拖放的 TreeView。我的所有功能都可以正常工作,但是当拖拽 TreeViewItem 时我很难突出显示它。这是我的树视图项目的风格。 IsMouseOver 触发器在拖动时不起作用,因为拖动似乎会阻止其他鼠标事件。任何人都可以帮助我在拖动时触发树视图项上的相同边框更改吗?

<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>

【问题讨论】:

    标签: .net wpf xaml drag-and-drop treeview


    【解决方案1】:

    我为此使用了一个附加属性,然后在我的 xaml 文件中使用该属性来更改树视图项的背景颜色:

    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
        }
    }
    

    然后在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>
    

    【讨论】:

    • 这正是我今天所需要的。非常感谢斯特凡!在我的树形视图中,我注意到有时它不会自动从我的 drop 中删除突出显示,因此我注册了 drop 事件并编写了一个简单的函数,将 _dropPossible 设置回 false 并使 IsPossibleDropTargetProperty 无效。也许对其他人有用?
    • 干得好!除了#chocojosh 添加的内容之外,我添加的一件事是告诉 TreeViewItem 如果它在 OnDragEvent 事件上有项目则展开。您可以通过添加: if (_currentItem.HasItems) _currentItem.IsExpanded = true;
    • 该代码运行良好,只是释放鼠标时目标项背景仍在HighlightBrushKey中。
    • 嗨@Dia 我在使用这段代码时注意到了同样的问题。释放鼠标拖动后,您是否找到了使目标项目背景恢复正常的方法?
    • @Stavros 尝试在TreeViewDropHighlighter()构造函数EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.PreviewDropEvent, new DragEventHandler(OnDragDrop), true);中添加一行
    【解决方案2】:

    查看DragOver 事件(可能还有 DragEnter/DragLeave)而不是 IsMouseOver。

    【讨论】:

    • “DragOver”不是以与“IsMouseOver”相同的方式设置触发器的有效属性。我可以使用 EventTrigger,但它们似乎只接受 StoryBoards,我无法弄清楚如何在 StoryBoard 中设置我的边框属性,就像我为 IsMouseOver Setter 所做的那样。
    • 我必须在 TreeViewDropHighlighter() 构造函数中添加一行,以便在放置完成后删除更改.... EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.PreviewDropEvent, new DragEventHandler( OnDragLeave), true);
    • 这是一个事件。抱歉,不清楚!您可以在您的样式中使用事件设置器将其添加到所有树视图项中。
    【解决方案3】:

    除了 Stefan 的回答,我发现还需要添加 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);
            }
        }
    }
    

    并同时注册 drop 事件:

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

    否则,如果一个 treeItem 掉落到另一个 treeItem 上,背景可能不会改变。按 ESC 键也是一样。

    【讨论】:

      【解决方案4】:

      这篇文章只是将 Stefan 上面对 VB 的精彩回应的移植,供我们这些在这些范围内工作的人使用;除了希望我没有把任何事情搞砸之外,我没有什么可提供的。它似乎对我有用:

      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
      

      结束命名空间

      【讨论】:

      • 感谢 dansan,它运行良好!我们 VB 人是一个垂死的品种,需要团结在一起!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-27
      • 2015-05-30
      相关资源
      最近更新 更多