This repository has been archived by the owner on Nov 1, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
samples: add highlight effects to TreeView drag-and-drop
- Loading branch information
1 parent
d62d2aa
commit 38a0a9e
Showing
5 changed files
with
226 additions
and
36 deletions.
There are no files selected for viewing
135 changes: 135 additions & 0 deletions
135
samples/DragAndDropSample/Behaviors/BaseTreeViewDropHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
using System.Linq; | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Input; | ||
using Avalonia.Interactivity; | ||
using Avalonia.LogicalTree; | ||
using Avalonia.VisualTree; | ||
using Avalonia.Xaml.Interactions.DragAndDrop; | ||
|
||
namespace DragAndDropSample.Behaviors; | ||
|
||
public abstract class BaseTreeViewDropHandler : DropHandlerBase | ||
{ | ||
private const string rowDraggingUpStyleClass = "DraggingUp"; | ||
private const string rowDraggingDownStyleClass = "DraggingDown"; | ||
private const string targetHighlightStyleClass = "TargetHighlight"; | ||
|
||
protected abstract (bool Valid, bool WillSourceItemBeMovedToDifferentParent) Validate(TreeView tv, DragEventArgs e, object? sourceContext, object? targetContext, bool bExecute); | ||
|
||
public override bool Validate(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) | ||
{ | ||
if (e.Source is Control && sender is TreeView tv) | ||
{ | ||
var (valid, willSourceItemChangeParent) = Validate(tv, e, sourceContext, targetContext, false); | ||
var targetVisual = tv.GetVisualAt(e.GetPosition(tv)); | ||
if (valid) | ||
{ | ||
var targetItem = FindTreeViewItemFromChildView(targetVisual); | ||
// If its a movement within the same tree level, | ||
// then an adorner layer will be applied. | ||
|
||
// But, if the source item will move to a different level, | ||
// the level's owner will receive a background highlight. | ||
|
||
// In the case of being moved to a root target item, | ||
// (with targetItem.Parent not being another TreeViewItem), | ||
// then this root target item will receive this style. | ||
var itemToApplyStyle = (willSourceItemChangeParent && targetItem?.Parent is TreeViewItem tviParent) ? | ||
tviParent : targetItem; | ||
string direction = e.Data.Contains("direction") ? (string)e.Data.Get("direction")! : "down"; | ||
ApplyDraggingStyleToItem(itemToApplyStyle!, direction, willSourceItemChangeParent); | ||
ClearDraggingStyleFromAllItems(sender, exceptThis: itemToApplyStyle); | ||
} | ||
return valid; | ||
} | ||
ClearDraggingStyleFromAllItems(sender); | ||
return false; | ||
} | ||
|
||
public override bool Execute(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) | ||
{ | ||
ClearDraggingStyleFromAllItems(sender); | ||
if (e.Source is Control && sender is TreeView tv) | ||
{ | ||
var (valid, _) = Validate(tv, e, sourceContext, targetContext, true); | ||
return valid; | ||
} | ||
return false; | ||
} | ||
|
||
public override void Cancel(object? sender, RoutedEventArgs e) | ||
{ | ||
base.Cancel(sender, e); | ||
// This is necessary to clear styles | ||
// when mouse exists TreeView, else, | ||
// they would remain even after changing screens. | ||
ClearDraggingStyleFromAllItems(sender); | ||
} | ||
|
||
private static TreeViewItem? FindTreeViewItemFromChildView(StyledElement? sourceChild) | ||
{ | ||
if (sourceChild is null) | ||
return null; | ||
|
||
int maxDepth = 16; | ||
StyledElement? current = sourceChild; | ||
while (maxDepth --> 0) | ||
{ | ||
if (current is TreeViewItem tvi) | ||
return tvi; | ||
else | ||
current = current?.Parent; | ||
} | ||
return null; | ||
} | ||
|
||
private static void ClearDraggingStyleFromAllItems(object? sender, TreeViewItem? exceptThis = null) | ||
{ | ||
if (sender is not Visual rootVisual) | ||
return; | ||
|
||
foreach (var item in rootVisual.GetLogicalChildren().OfType<TreeViewItem>()) | ||
{ | ||
if (item == exceptThis) | ||
continue; | ||
|
||
if (item.Classes is not null) | ||
{ | ||
item.Classes.Remove(rowDraggingUpStyleClass); | ||
item.Classes.Remove(rowDraggingDownStyleClass); | ||
item.Classes.Remove(targetHighlightStyleClass); | ||
} | ||
ClearDraggingStyleFromAllItems(item, exceptThis); | ||
} | ||
} | ||
|
||
private static void ApplyDraggingStyleToItem(TreeViewItem? item, string direction, bool willSourceItemBeMovedToDifferentParent) | ||
{ | ||
if (item is null) | ||
return; | ||
|
||
// Avalonia's Classes.Add() verifies | ||
// if a class has already been added | ||
// (avoiding duplications); no need to | ||
// verify .Contains() here. | ||
if (willSourceItemBeMovedToDifferentParent) | ||
{ | ||
item.Classes.Remove(rowDraggingDownStyleClass); | ||
item.Classes.Remove(rowDraggingUpStyleClass); | ||
item.Classes.Add(targetHighlightStyleClass); | ||
} | ||
else if (direction == "up") | ||
{ | ||
item.Classes.Remove(rowDraggingDownStyleClass); | ||
item.Classes.Remove(targetHighlightStyleClass); | ||
item.Classes.Add(rowDraggingUpStyleClass); | ||
} | ||
else if (direction == "down") | ||
{ | ||
item.Classes.Remove(rowDraggingUpStyleClass); | ||
item.Classes.Remove(targetHighlightStyleClass); | ||
item.Classes.Add(rowDraggingDownStyleClass); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters