Files
HandyControl/src/Avalonia/HandyControl_Avalonia/Controls/Dialog/Dialog.cs
T

212 lines
6.2 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.VisualTree;
using HandyControl.Tools;
namespace HandyControl.Controls;
[TemplatePart("PART_BackElement", typeof(Border))]
public class Dialog : ContentControl
{
private const string BackElement = "PART_BackElement";
private string? _token;
private Border? _backElement;
private Panel? _hostPanel;
private static readonly ControlTokenManager<Control> TokenManager = new();
private static readonly Dictionary<string, Dialog> DialogDict = new();
public static readonly StyledProperty<bool> IsClosedProperty =
AvaloniaProperty.Register<Dialog, bool>(nameof(IsClosed));
public bool IsClosed
{
get => GetValue(IsClosedProperty);
internal set => SetValue(IsClosedProperty, value);
}
public static readonly AttachedProperty<bool> MaskCanCloseProperty =
AvaloniaProperty.RegisterAttached<Dialog, AvaloniaObject, bool>("MaskCanClose", false);
public static void SetMaskCanClose(AvaloniaObject element, bool value) =>
element.SetValue(MaskCanCloseProperty, value);
public static bool GetMaskCanClose(AvaloniaObject element) =>
element.GetValue(MaskCanCloseProperty);
public static readonly StyledProperty<IBrush?> MaskBrushProperty =
AvaloniaProperty.Register<Dialog, IBrush?>(nameof(MaskBrush));
public IBrush? MaskBrush
{
get => GetValue(MaskBrushProperty);
set => SetValue(MaskBrushProperty, value);
}
public static readonly AttachedProperty<string?> TokenProperty =
AvaloniaProperty.RegisterAttached<Dialog, AvaloniaObject, string?>("Token");
static Dialog()
{
TokenProperty.Changed.AddClassHandler<AvaloniaObject>(TokenManager.OnTokenChanged);
}
public static void SetToken(AvaloniaObject element, string? value) =>
element.SetValue(TokenProperty, value);
public static string? GetToken(AvaloniaObject element) =>
element.GetValue(TokenProperty);
public static Dialog Show<T>(string? token = null) where T : new() => Show(new T(), token);
public static Dialog Show(object content, string? token = null)
{
var dialog = new Dialog
{
_token = token,
Content = content
};
if (string.IsNullOrEmpty(token))
{
var window = GetActiveWindow();
if (window != null)
{
ShowOnWindow(dialog, window);
}
}
else
{
Close(token);
DialogDict[token] = dialog;
if (TokenManager.TryGetControl(token, out var ctrl) && ctrl != null)
{
if (ctrl is Window window)
{
ShowOnWindow(dialog, window);
}
else
{
var panel = FindDialogContainer(ctrl);
if (panel != null)
{
dialog._hostPanel = panel;
dialog.IsClosed = false;
dialog.Width = panel.Bounds.Width;
dialog.Height = panel.Bounds.Height;
panel.Children.Add(dialog);
panel.InvalidateMeasure();
}
else
{
var win = GetActiveWindow();
if (win != null)
ShowOnWindow(dialog, win);
}
}
}
}
return dialog;
}
private static void ShowOnWindow(Dialog dialog, Window window)
{
var overlay = OverlayLayer.GetOverlayLayer(window);
if (overlay == null) return;
var size = overlay.Bounds.Size;
if (size.Width <= 0 || size.Height <= 0)
{
var topLevel = TopLevel.GetTopLevel(overlay);
if (topLevel != null)
size = topLevel.ClientSize;
}
dialog._hostPanel = overlay;
dialog.IsClosed = false;
dialog.Width = size.Width;
dialog.Height = size.Height;
overlay.Children.Add(dialog);
void OnOverlayResized(object? sender, SizeChangedEventArgs e)
{
if (dialog._hostPanel == null) return;
dialog.Width = e.NewSize.Width;
dialog.Height = e.NewSize.Height;
}
overlay.SizeChanged += OnOverlayResized;
}
public static void Close(string? token)
{
if (!string.IsNullOrEmpty(token) && DialogDict.TryGetValue(token, out var dialog))
{
dialog.Close();
}
}
public void Close()
{
if (_hostPanel == null) return;
_hostPanel.Children.Remove(this);
_hostPanel = null;
IsClosed = true;
if (!string.IsNullOrEmpty(_token))
{
DialogDict.Remove(_token);
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_backElement = e.NameScope.Find<Border>(BackElement);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (e.InitialPressMouseButton == MouseButton.Left &&
GetMaskCanClose(this) && _backElement != null && _backElement.IsPointerOver)
{
Close();
}
}
private static DialogContainer? FindDialogContainer(Visual root)
{
foreach (var child in root.GetVisualChildren())
{
if (child is DialogContainer found)
return found;
var nested = FindDialogContainer(child);
if (nested != null)
return nested;
}
return null;
}
private static Window? GetActiveWindow()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
return desktop.Windows.FirstOrDefault(w => w.IsActive) ?? desktop.MainWindow;
}
return null;
}
}