Add initial project structure with basic view models, views, and resources
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user