In my application, I have a TreeView that allows drag/drop. I have all the functionality working fine, however I am having difficulty highlighting a TreeViewItem when it is dragged over. Here is my style for my treeview item. The IsMouseOver trigger does not work while dragging, because dragging seems to block other mouse events. Can anyone help me trigger the same border changes on my treeview item while dragging?
<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>
I'm using an attached property for this, and then use that property in my xaml file to change the background color of the tree view item:
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
}
}
and then in the xaml file:
<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>
查看DragOver事件(可能还有DragEnter / DragLeave)而不是IsMouseOver。
This post is simply a porting of Stefan's awesome response above to VB, for those of us working in those confines; I have nothing to offer except to hope I didn't screw anything up too badly. It appears to work for me:
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
In addition to Stefan's answer, I found that is necessary to add also the Drop event:
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);
}
}
}
and registering drop event also:
EventManager.RegisterClassHandler(typeof(TreeViewItem),
TreeViewItem.PreviewDropEvent,
new DragEventHandler(OnDragDrop), true);
Otherwise if drop occurs of one treeItem over another, the background might not change. Same thing if you press ESC.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.