2009-03-12 5 views
5

Nella mia applicazione, ho un TreeView che consente il trascinamento/rilascio. Ho tutte le funzionalità che funzionano bene, tuttavia ho difficoltà a evidenziare un oggetto TreeView quando viene trascinato. Ecco il mio stile per il mio articolo treeview. Il trigger di IsMouseOver non funziona durante il trascinamento, poiché il trascinamento sembra bloccare altri eventi del mouse. Qualcuno può aiutarmi ad attivare le stesse modifiche al bordo sul mio elemento treeview durante il trascinamento?Evidenzia elemento TreeView trascinato su

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

risposta

16

che sto utilizzando una proprietà associata per questo, e quindi utilizzare tale proprietà nel mio file XAML per cambiare il colore di sfondo della voce di visualizzazione ad albero:

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

e poi nel 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

Questa è una cosa bellissima, grazie mille! –

+2

Questo è esattamente ciò di cui avevo bisogno oggi. Grazie mille Stefan! Nella mia vista ad albero ho notato che a volte non rimuoveva automaticamente l'evidenziazione dalla mia caduta, quindi ho registrato l'evento di rilascio e ho scritto una semplice funzione che imposta _dropPossible su false e Invalidates IsPossibleDropTargetProperty. Forse utile a qualcun altro? – chocojosh

+0

Ottimo lavoro! Una cosa che ho aggiunto in aggiunta a ciò che #chocojosh ha aggiunto, sta dicendo all'oggetto TreeViewItem di espandersi se contiene elementi nell'evento OnDragEvent. È possibile farlo aggiungendo: if (_currentItem.HasItems) _currentItem.IsExpanded = true; – Brent

4

all'evento DragOver (e possibilmente DragEnter/DragLeave) invece di IsMouseOver.

+0

"DragOver" non è una proprietà valida per impostare un trigger nello stesso modo di "IsMouseOver". Posso usare EventTrigger, tuttavia sembrano accettare solo StoryBoards e non riesco a capire come impostare le proprietà dei bordi all'interno di StoryBoard nello stesso modo in cui ho fatto per i setter di IsMouseOver. –

+0

Ho dovuto aggiungere una riga al costruttore TreeViewDropHighlighter(), per rimuovere anche le modifiche dopo che era stata effettuata una caduta .... EventManager.RegisterClassHandler (typeof (TreeViewItem), TreeViewItem.PreviewDropEvent, new DragEventHandler (OnDragLeave), true) ; –

+0

È un evento. Scusa non era chiaro! Puoi utilizzare un event setter nel tuo stile per aggiungerlo a tutti gli elementi della vista ad albero. –

1

Questo post è semplicemente un porting dell'enorme risposta di Stefan sopra a VB, per quelli di noi che lavorano in quei confini; Non ho niente da offrire se non sperare di non aver rovinato niente di male. Sembra di lavorare per 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

+0

Grazie a dansan, funziona perfettamente! Noi VB siamo una razza in via di estinzione e dobbiamo restare uniti! –

1

Oltre alla risposta di Stefan, ho scoperto che è necessario aggiungere anche l'evento di goccia:

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

e registrazione evento di rilascio anche :

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

In caso contrario, se si verifica una caduta di un albero su un altro, lo sfondo potrebbe non cambiare. Stessa cosa se si preme ESC.