突出显示拖动的TreeView项目

时间:2009-03-12 18:25:17

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

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

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

4 个答案:

答案 0 :(得分:18)

我正在使用附加属性,然后在我的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>

答案 1 :(得分:4)

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

答案 2 :(得分:1)

这篇文章只是将Stefan对VB的强烈回应移植到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

结束命名空间

答案 3 :(得分:1)

除了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发生丢弃,则背景可能不会改变。如果按ESC键也一样。