124 lines
3.6 KiB
C#
124 lines
3.6 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.VisualTree;
|
|
|
|
namespace CryptoPortfolio_Tracker.Behaviors;
|
|
|
|
public static class WindowDragBehavior
|
|
{
|
|
public static readonly AttachedProperty<bool> EnableWindowDragProperty =
|
|
AvaloniaProperty.RegisterAttached<Control, bool>(
|
|
"EnableWindowDrag",
|
|
typeof(WindowDragBehavior));
|
|
|
|
public static bool GetEnableWindowDrag(Control element) =>
|
|
element.GetValue(EnableWindowDragProperty);
|
|
|
|
public static void SetEnableWindowDrag(Control element, bool value) =>
|
|
element.SetValue(EnableWindowDragProperty, value);
|
|
|
|
static WindowDragBehavior()
|
|
{
|
|
EnableWindowDragProperty.Changed.AddClassHandler<Control>(OnEnableWindowDragChanged);
|
|
}
|
|
|
|
private static void OnEnableWindowDragChanged(Control control, AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
if (e.NewValue is true)
|
|
{
|
|
// Контрол ещё может не быть в визуальном дереве
|
|
if (control.IsLoaded)
|
|
Attach(control);
|
|
else
|
|
control.Loaded += OnControlLoaded;
|
|
}
|
|
else
|
|
{
|
|
control.Loaded -= OnControlLoaded;
|
|
Detach(control);
|
|
}
|
|
}
|
|
|
|
private static void OnControlLoaded(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is Control control)
|
|
{
|
|
control.Loaded -= OnControlLoaded;
|
|
Attach(control);
|
|
}
|
|
}
|
|
|
|
private static void Attach(Control control)
|
|
{
|
|
var topBar = FindInVisualTree<Border>(control, "PART_NavigationBar");
|
|
if (topBar is null)
|
|
return;
|
|
|
|
topBar.AddHandler(
|
|
InputElement.PointerPressedEvent,
|
|
OnTopBarPointerPressed,
|
|
RoutingStrategies.Tunnel);
|
|
}
|
|
|
|
private static void Detach(Control control)
|
|
{
|
|
var topBar = FindInVisualTree<Border>(control, "Border#PART_NavigationBar");
|
|
if (topBar is null)
|
|
return;
|
|
|
|
topBar.RemoveHandler(
|
|
InputElement.PointerPressedEvent,
|
|
OnTopBarPointerPressed);
|
|
}
|
|
|
|
private static void OnTopBarPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
{
|
|
if (!e.GetCurrentPoint(null).Properties.IsLeftButtonPressed)
|
|
return;
|
|
|
|
// Проверяем — нажатие было на кнопке или интерактивном элементе?
|
|
if (e.Source is Visual source && IsInteractiveElement(source))
|
|
return;
|
|
|
|
if (sender is Visual visual &&
|
|
TopLevel.GetTopLevel(visual) is Window window)
|
|
{
|
|
window.BeginMoveDrag(e);
|
|
}
|
|
}
|
|
|
|
private static bool IsInteractiveElement(Visual visual)
|
|
{
|
|
Visual? current = visual;
|
|
while (current is not null)
|
|
{
|
|
if (current is Button or ToggleButton or CheckBox or
|
|
RadioButton or Slider or TextBox or ComboBox)
|
|
return true;
|
|
|
|
if (current is Border { Name: "Border#PART_NavigationBar" })
|
|
break;
|
|
|
|
current = current.GetVisualParent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static T? FindInVisualTree<T>(Visual root, string name) where T : Visual
|
|
{
|
|
if (root is T typed && root.Name == name)
|
|
return typed;
|
|
|
|
foreach (var child in root.GetVisualChildren())
|
|
{
|
|
var result = FindInVisualTree<T>(child, name);
|
|
if (result is not null)
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
} |