Add Static API for MessageBox

This commit is contained in:
chinboy
2025-12-23 14:18:07 +08:00
parent 08c2bf4f1f
commit 8aaadfec4c
11 changed files with 332 additions and 115 deletions
@@ -100,14 +100,15 @@
<TextBlock VerticalAlignment="Center">Native Window:</TextBlock>
<atom:ToggleSwitch Name="StyleCaseHostTypeSwitch" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="10">
<atom:Separator Title="Normal"/>
<WrapPanel Orientation="Horizontal" ItemSpacing="10" LineSpacing="10">
<Panel>
<atom:MessageBox PlacementTarget="ConfirmMsgBoxBtn"
Title="Do you want to delete these items?"
IsOpen="{Binding IsConfirmMsgBoxOpened, Mode=TwoWay}"
Style="Confirm"
HostType="{Binding MessageBoxStyleCaseHostType}">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical" Spacing="5">
<TextBlock>Some descriptions</TextBlock>
</StackPanel>
</atom:MessageBox>
@@ -119,7 +120,7 @@
IsOpen="{Binding IsInformationMsgBoxOpened, Mode=TwoWay}"
Style="Information"
HostType="{Binding MessageBoxStyleCaseHostType}">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical" Spacing="5">
<TextBlock>some messages...some messages...</TextBlock>
<TextBlock>some messages...some messages...</TextBlock>
</StackPanel>
@@ -132,7 +133,7 @@
Style="Success"
HostType="{Binding MessageBoxStyleCaseHostType}"
IsOpen="{Binding IsSuccessMsgBoxOpened, Mode=TwoWay}">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical" Spacing="5">
<TextBlock>some messages...some messages...</TextBlock>
<TextBlock>some messages...some messages...</TextBlock>
</StackPanel>
@@ -145,7 +146,7 @@
Style="Error"
HostType="{Binding MessageBoxStyleCaseHostType}"
IsOpen="{Binding IsErrorMsgBoxOpened, Mode=TwoWay}">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical" Spacing="5">
<TextBlock>some messages...some messages...</TextBlock>
<TextBlock>some messages...some messages...</TextBlock>
</StackPanel>
@@ -158,14 +159,23 @@
Style="Warning"
HostType="{Binding MessageBoxStyleCaseHostType}"
IsOpen="{Binding IsWarningMsgBoxOpened, Mode=TwoWay}">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical" Spacing="5">
<TextBlock>some messages...some messages...</TextBlock>
<TextBlock>some messages...some messages...</TextBlock>
</StackPanel>
</atom:MessageBox>
<atom:Button Name="WarningMsgBoxBtn">Warning</atom:Button>
</Panel>
</StackPanel>
</WrapPanel>
<atom:Separator Title="Static API" />
<WrapPanel Orientation="Horizontal" ItemSpacing="10" LineSpacing="10">
<atom:Button Click="HandleCreateConfirmMessageBox">Confirm</atom:Button>
<atom:Button Click="HandleCreateInformationMessageBox">Information</atom:Button>
<atom:Button Click="HandleCreateSuccessMessageBox">Success</atom:Button>
<atom:Button Click="HandleCreateErrorMessageBox">Error</atom:Button>
<atom:Button Click="HandleCreateWarningMessageBox">Warning</atom:Button>
</WrapPanel>
</StackPanel>
</gallery:ShowCaseItem>
@@ -259,7 +259,7 @@ public partial class ModalShowCase : ReactiveUserControl<ModalViewModel>
// 鼠标会被卡死,强制刷新一次事件循环
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildDialogContent();
var options = new DialogOptions()
var options = new DialogOptions
{
IsModal = true,
Title = "Basic Modal",
@@ -272,7 +272,7 @@ public partial class ModalShowCase : ReactiveUserControl<ModalViewModel>
VerticalOffset = new Dimension(30, DimensionUnitType.Percentage),
MinWidth = 400
};
Dialog.ShowDialog(content, null, options);
await Dialog.ShowDialogAsync(content, null, options);
}
private async void HandleOpenWindowDialogButtonClick(object? sender, RoutedEventArgs e)
@@ -280,7 +280,7 @@ public partial class ModalShowCase : ReactiveUserControl<ModalViewModel>
// 鼠标会被卡死,强制刷新一次事件循环
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildDialogContent();
var options = new DialogOptions()
var options = new DialogOptions
{
IsModal = true,
Title = "Basic Modal",
@@ -294,14 +294,14 @@ public partial class ModalShowCase : ReactiveUserControl<ModalViewModel>
VerticalOffset = new Dimension(30, DimensionUnitType.Percentage),
MinWidth = 400
};
Dialog.ShowDialog(content, null, options);
await Dialog.ShowDialogAsync(content, null, options);
}
private async void HandleOpenCustomViewDialogButtonClick(object? sender, RoutedEventArgs e)
{
// 鼠标会被卡死,强制刷新一次事件循环
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var options = new DialogOptions()
var options = new DialogOptions
{
IsModal = true,
Title = "Basic Modal",
@@ -318,30 +318,124 @@ public partial class ModalShowCase : ReactiveUserControl<ModalViewModel>
var viewModel = new ModalUserControlViewModel()
{
Name = "AtomUI",
Age = 2
Age = 2
};
Dialog.ShowDialog<ModalUserControlView, ModalUserControlViewModel>(viewModel, options);
}
private Control BuildDialogContent()
{
var stackPanel = new StackPanel()
var stackPanel = new StackPanel
{
Orientation = Orientation.Vertical,
Spacing = 5
};
stackPanel.Children.Add(new TextBlock()
stackPanel.Children.Add(new TextBlock
{
Text = "Some contents..."
});
stackPanel.Children.Add(new TextBlock()
stackPanel.Children.Add(new TextBlock
{
Text = "Some contents..."
});
stackPanel.Children.Add(new TextBlock()
stackPanel.Children.Add(new TextBlock
{
Text = "Some contents..."
});
return stackPanel;
}
private async void HandleCreateConfirmMessageBox(object? sender, RoutedEventArgs e)
{
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildMessageBoxContent();
var options = new MessageBoxOptions()
{
IsModal = true,
Title = "Do you want to delete these items?",
IsDragMovable = true,
IsCenterOnStartup = true,
Style = MessageBoxStyle.Confirm
};
await MessageBox.ShowMessageAsync(content, null, options);
}
private async void HandleCreateInformationMessageBox(object? sender, RoutedEventArgs e)
{
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildMessageBoxContent();
var options = new MessageBoxOptions()
{
IsModal = true,
Title = "This is a notification message",
IsDragMovable = true,
IsCenterOnStartup = true,
Style = MessageBoxStyle.Information
};
await MessageBox.ShowMessageAsync(content, null, options);
}
private async void HandleCreateSuccessMessageBox(object? sender, RoutedEventArgs e)
{
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildMessageBoxContent();
var options = new MessageBoxOptions()
{
IsModal = true,
Title = "Operation successful",
IsDragMovable = true,
IsCenterOnStartup = true,
Style = MessageBoxStyle.Success
};
await MessageBox.ShowMessageAsync(content, null, options);
}
private async void HandleCreateErrorMessageBox(object? sender, RoutedEventArgs e)
{
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildMessageBoxContent();
var options = new MessageBoxOptions()
{
IsModal = true,
Title = "This is an error message",
IsDragMovable = true,
IsCenterOnStartup = true,
Style = MessageBoxStyle.Error
};
await MessageBox.ShowMessageAsync(content, null, options);
}
private async void HandleCreateWarningMessageBox(object? sender, RoutedEventArgs e)
{
await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Background);
var content = BuildMessageBoxContent();
var options = new MessageBoxOptions()
{
IsModal = true,
Title = "This is a warning message",
IsDragMovable = true,
IsCenterOnStartup = true,
Style = MessageBoxStyle.Warning
};
await MessageBox.ShowMessageAsync(content, null, options);
}
private Control BuildMessageBoxContent()
{
var stackPanel = new StackPanel
{
Orientation = Orientation.Vertical,
Spacing = 5
};
stackPanel.Children.Add(new TextBlock
{
Text = "some messages...some messages..."
});
stackPanel.Children.Add(new TextBlock
{
Text = "some messages...some messages..."
});
return stackPanel;
}
}
@@ -1,6 +1,5 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace AtomUI.Desktop.Controls;
@@ -36,7 +35,7 @@ public partial class Dialog
return result;
}
public static Task<object?>? ShowDialogAsync<TView, TViewModel>(TViewModel? dataContext, DialogOptions? options = null, TopLevel? topLevel = null)
public static async Task ShowDialogAsync<TView, TViewModel>(TViewModel? dataContext, DialogOptions? options = null, TopLevel? topLevel = null)
where TView : Control, new()
{
var dialogManager = FindDialogManager(topLevel);
@@ -47,10 +46,10 @@ public partial class Dialog
}
dialog.Closed += (_, _) => dialogManager.Children.Remove(dialog);
dialogManager.Children.Add(dialog);
return dialog.OpenAsync();
await Dispatcher.UIThread.InvokeAsync(async () => await dialog.OpenAsync());
}
public static Task<object?>? ShowDialogAsync(Control content, object? dataContext = null, DialogOptions? options = null, TopLevel? topLevel = null)
public static async Task ShowDialogAsync(Control content, object? dataContext = null, DialogOptions? options = null, TopLevel? topLevel = null)
{
var dialogManager = FindDialogManager(topLevel);
var dialog = CreateDialog(content, dataContext, options);
@@ -60,7 +59,7 @@ public partial class Dialog
}
dialog.Closed += (_, _) => dialogManager.Children.Remove(dialog);
dialogManager.Children.Add(dialog);
return dialog.OpenAsync();
await Dispatcher.UIThread.InvokeAsync(async () => await dialog.OpenAsync());
}
private static Dialog CreateDialog(Control content, object? dataContext, DialogOptions? options)
@@ -99,7 +98,7 @@ public partial class Dialog
private static GlobalDialogManager FindDialogManager(TopLevel? topLevel)
{
var toplevel = topLevel ?? GetMainWindow();
var toplevel = topLevel ?? Window.GetMainWindow();
var dialogManager = toplevel.FindDescendantOfType<GlobalDialogManager>();
if (dialogManager == null)
{
@@ -107,14 +106,4 @@ public partial class Dialog
}
return dialogManager;
}
private static Window? GetMainWindow()
{
var lifetime = Application.Current?.ApplicationLifetime;
if (lifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
return desktopLifetime.MainWindow as Window;
}
return null;
}
}
+18 -45
View File
@@ -4,7 +4,6 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Disposables;
using System.Reactive.Disposables.Fluent;
using System.Reactive.Linq;
using AtomUI.Controls;
using AtomUI.Data;
using AtomUI.Desktop.Controls.DialogPositioning;
@@ -275,12 +274,6 @@ public partial class Dialog : TemplatedControl,
set => SetValue(DialogHostTypeProperty, value);
}
public Task<object?>? ResultTask
{
get;
private set;
}
public DialogStandardButtons StandardButtons
{
get => GetValue(StandardButtonsProperty);
@@ -401,7 +394,6 @@ public partial class Dialog : TemplatedControl,
private bool _ignoreIsOpenChanged;
private DialogOpenState? _openState;
private Action<IDialogHost?>? _dialogHostChangedHandler;
private IDisposable? _modalSubscription;
private CancellationTokenSource? _frameCancellationTokenSource;
private bool _startupLocationCalculated;
private bool _opening;
@@ -446,16 +438,16 @@ public partial class Dialog : TemplatedControl,
var token = _frameCancellationTokenSource.Token;
var frame = new DispatcherFrame();
token.Register(() => frame.Continue = false);
var resultTask = OpenAsync();
var _ = OpenAsync();
Dispatcher.UIThread.PushFrame(frame);
return resultTask?.Result;
return Result;
}
public Task<object?>? OpenAsync()
public async Task OpenAsync()
{
if (_openState != null || _opening)
{
return null;
return;
}
_opening = true;
@@ -466,7 +458,7 @@ public partial class Dialog : TemplatedControl,
IDialogHost? dialogHost = null;
CompositeDisposable relayBindingDisposables = new CompositeDisposable();
DialogHost? windowDialogHost = null;
OverlayDialogHost? overlayDialogHost = null;
if (DialogHostType == DialogHostType.Window)
{
windowDialogHost = CreateDialogHost(topLevel, this);
@@ -479,7 +471,7 @@ public partial class Dialog : TemplatedControl,
var dialogLayer = DialogLayer.GetDialogLayer(placementTarget);
if (dialogLayer != null)
{
overlayDialogHost = CreateOverlayDialogHost(dialogLayer, this);
var overlayDialogHost = CreateOverlayDialogHost(dialogLayer, this);
OverlayInputPassThroughElement = overlayDialogHost;
RelayOverlayDialogBindings(relayBindingDisposables, overlayDialogHost);
overlayDialogHost.CustomButtons.AddRange(CustomButtons);
@@ -535,6 +527,12 @@ public partial class Dialog : TemplatedControl,
}
var inputManager = AvaloniaLocator.Current.GetService<IInputManager>();
inputManager?.Process.Subscribe(ListenForNonClientClick).DisposeWith(handlerCleanup);
TaskCompletionSource? modalTsc = null;
if (IsModal)
{
modalTsc = new TaskCompletionSource();
}
var cleanupPopup = Disposable.Create((dialogHost, handlerCleanup), state =>
{
@@ -554,6 +552,7 @@ public partial class Dialog : TemplatedControl,
dialogAwareDataContext.NotifyClosed();
}
Closed?.Invoke(this, EventArgs.Empty);
modalTsc?.TrySetResult();
});
if (DialogHostType == DialogHostType.Window)
{
@@ -596,7 +595,7 @@ public partial class Dialog : TemplatedControl,
{
if (topLevel is Window windowTopLevel)
{
windowDialog.ShowDialog(windowTopLevel);
await windowDialog.ShowDialog(windowTopLevel);
}
}
else
@@ -609,32 +608,6 @@ public partial class Dialog : TemplatedControl,
dialogHost.Show();
}
if (IsModal)
{
var tcs = new TaskCompletionSource<object?>();
var disposables = new CompositeDisposable(
[
Observable.FromEventPattern(
x => Closed += x,
x => Closed -= x)
.Take(1)
.Subscribe(_ =>
{
_modalSubscription?.Dispose();
}),
Disposable.Create(() =>
{
_modalSubscription = null;
// owner!.Activate();
tcs.SetResult(Result);
})
]);
_modalSubscription = disposables;
ResultTask = tcs.Task;
}
using (BeginIgnoringIsOpen())
{
SetCurrentValue(IsOpenProperty, true);
@@ -642,8 +615,10 @@ public partial class Dialog : TemplatedControl,
Opened?.Invoke(this, EventArgs.Empty);
_opening = false;
_dialogHostChangedHandler?.Invoke(Host);
return ResultTask;
if (modalTsc != null)
{
await modalTsc.Task;
}
}
private protected virtual DialogHost CreateDialogHost(TopLevel topLevel, Dialog dialog)
@@ -761,8 +736,6 @@ public partial class Dialog : TemplatedControl,
_openState = null;
_dialogHostChangedHandler?.Invoke(null);
_modalSubscription?.Dispose();
_modalSubscription = null;
using (BeginIgnoringIsOpen())
{
SetCurrentValue(IsOpenProperty, false);
@@ -1,4 +1,3 @@
using AtomUI.Controls;
using Avalonia.Controls;
namespace AtomUI.Desktop.Controls;
@@ -7,11 +6,14 @@ public record DialogOptions
{
public string? Title { get; init; }
public PathIcon? TitleIcon { get; init; }
public bool IsLightDismissEnabled { get; init; } = false;
/// <summary>
/// 次选项仅 Overlay Host 类型的弹窗有效
/// </summary>
public bool IsLightDismissEnabled { get; init; }
public bool IsModal { get; init; } = true;
public bool IsResizable { get; init; } = false;
public bool IsResizable { get; init; }
public bool IsClosable { get; init; } = true;
public bool IsMaximizable { get; init; } = false;
public bool IsMaximizable { get; init; }
public double MaxHeight { get; init; } = double.PositiveInfinity;
public double MaxWidth { get; init; } = double.PositiveInfinity;
@@ -21,11 +23,11 @@ public record DialogOptions
public double Height { get; init; } = double.NaN;
/// <summary>
/// 仅仅对 Window Host 类型的弹窗有效
/// 次选项仅 Window Host 类型的弹窗有效
/// </summary>
public bool IsMinimizable { get; init; } = true;
public bool IsDragMovable { get; init; } = false;
public bool IsDragMovable { get; init; }
public bool IsFooterVisible { get; init; } = true;
public Control? PlacementTarget { get; init; }
public Dimension? HorizontalOffset { get; init; }
@@ -11,6 +11,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Metadata;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace AtomUI.Desktop.Controls;
@@ -38,7 +39,7 @@ public class MessageBox : TemplatedControl,
public static readonly StyledProperty<IDataTemplate?> ContentTemplateProperty = Dialog.ContentTemplateProperty.AddOwner<MessageBox>();
public static readonly StyledProperty<MessageBoxStyle> StyleProperty =
AvaloniaProperty.Register<MessageBox, MessageBoxStyle>(nameof (Style));
AvaloniaProperty.Register<MessageBox, MessageBoxStyle>(nameof (Style), MessageBoxStyle.Information);
public static readonly StyledProperty<bool> IsMotionEnabledProperty =
MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<MessageBox>();
@@ -250,37 +251,145 @@ public class MessageBox : TemplatedControl,
public MessageBox()
{
CustomButtons.CollectionChanged += new NotifyCollectionChangedEventHandler(HandleCustomButtonsChanged);
CustomButtons.CollectionChanged += HandleCustomButtonsChanged;
}
public object? Open()
{
if (_dialog == null)
{
return null;
}
Debug.Assert(_dialog != null);
return _dialog.Open();
}
public Task<object?>? OpenAsync()
public async Task OpenAsync()
{
if (_dialog == null)
{
return null;
}
return _dialog.OpenAsync();
Debug.Assert(_dialog != null);
await _dialog.OpenAsync();
}
public void Cancel()
{
_dialog?.Reject();
Debug.Assert(_dialog != null);
_dialog.Reject();
}
public void Confirm()
{
_dialog?.Accept();
Debug.Assert(_dialog != null);
_dialog.Accept();
}
#region 静态 API
public static object? ShowMessageBox<TView, TViewModel>(TViewModel? dataContext, MessageBoxOptions? options = null,
TopLevel? topLevel = null)
where TView : Control, new()
{
var dialogManager = FindDialogManager(topLevel);
var messageBox = CreateMessageBox(new TView(), dataContext, options);
if (options?.HostType == DialogHostType.Window)
{
messageBox.PlacementTarget = dialogManager;
}
dialogManager.Children.Add(messageBox);
var result = messageBox.Open();
dialogManager.Children.Remove(messageBox);
return result;
}
public static object? ShowMessageBox(Control content, object? dataContext = null, MessageBoxOptions? options = null, TopLevel? topLevel = null)
{
var dialogManager = FindDialogManager(topLevel);
var messageBox = CreateMessageBox(content, dataContext, options);
if (options?.HostType == DialogHostType.Window)
{
messageBox.PlacementTarget = dialogManager;
}
dialogManager.Children.Add(messageBox);
var result = messageBox.Open();
dialogManager.Children.Remove(messageBox);
return result;
}
public static async Task<object?> ShowMessageBoxAsync<TView, TViewModel>(TViewModel? dataContext, MessageBoxOptions? options = null, TopLevel? topLevel = null)
where TView : Control, new()
{
var dialogManager = FindDialogManager(topLevel);
var messageBox = CreateMessageBox(new TView(), dataContext, options);
if (options?.HostType == DialogHostType.Window)
{
messageBox.PlacementTarget = dialogManager;
}
messageBox.Closed += (_, _) => dialogManager.Children.Remove(messageBox);
dialogManager.Children.Add(messageBox);
await Dispatcher.UIThread.InvokeAsync(async () => await messageBox.OpenAsync());
return messageBox.Result;
}
public static async Task<object?> ShowMessageAsync(Control content, object? dataContext = null, MessageBoxOptions? options = null, TopLevel? topLevel = null)
{
var dialogManager = FindDialogManager(topLevel);
var messageBox = CreateMessageBox(content, dataContext, options);
if (options?.HostType == DialogHostType.Window)
{
messageBox.PlacementTarget = dialogManager;
}
messageBox.Closed += (_, _) => dialogManager.Children.Remove(messageBox);
dialogManager.Children.Add(messageBox);
var tsc = new TaskCompletionSource<object?>();
await Dispatcher.UIThread.InvokeAsync(async () =>
{
await messageBox.OpenAsync();
tsc.SetResult(messageBox.Result);
});
await tsc.Task;
return messageBox.Result;
}
private static MessageBox CreateMessageBox(Control content, object? dataContext, MessageBoxOptions? options)
{
var messageBox = new MessageBox()
{
Title = options?.Title,
IsLightDismissEnabled = options?.IsLightDismissEnabled ?? false,
IsModal = options?.IsModal ?? true,
IsDragMovable = options?.IsDragMovable ?? false,
Style = options?.Style ?? MessageBoxStyle.Information,
PlacementTarget = options?.PlacementTarget,
HorizontalOffset = options?.HorizontalOffset,
VerticalOffset = options?.VerticalOffset,
HostType = options?.HostType ?? DialogHostType.Overlay,
IsCenterOnStartup = options?.IsCenterOnStartup ?? true,
Content = content,
DataContext = dataContext,
Width = options?.Width ?? double.NaN,
Height = options?.Height ?? double.NaN,
MinWidth = options?.MinWidth ?? 0d,
MinHeight = options?.MinHeight ?? 0d,
MaxWidth = options?.MaxWidth ?? double.PositiveInfinity,
MaxHeight = options?.MaxHeight ?? double.PositiveInfinity,
IsConfirmLoading = options?.IsConfirmLoading ?? false,
IsLoading = options?.IsLoading ?? false,
};
if (options?.Icon != null)
{
messageBox.Icon = options.Icon;
}
return messageBox;
}
private static GlobalDialogManager FindDialogManager(TopLevel? topLevel)
{
var toplevel = topLevel ?? Window.GetMainWindow();
var dialogManager = toplevel.FindDescendantOfType<GlobalDialogManager>();
if (dialogManager == null)
{
throw new InvalidOperationException("The DialogManager was not found in TopLevel; you may not be using the atom:Window class.");
}
return dialogManager;
}
#endregion
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@@ -306,13 +415,13 @@ public class MessageBox : TemplatedControl,
base.OnApplyTemplate(e);
if (_dialog != null)
{
_dialog.Opened -= HandleDialogOpened;
_dialog.Closed -= HandleDialogClosed;
_dialog.Rejected -= HandleDialogCancelled;
_dialog.Accepted -= HandleDialogConfirmed;
_dialog.Finished -= HandleDialogFinished;
_dialog.Opened -= HandleDialogOpened;
_dialog.Closed -= HandleDialogClosed;
_dialog.Rejected -= HandleDialogCancelled;
_dialog.Accepted -= HandleDialogConfirmed;
_dialog.Finished -= HandleDialogFinished;
}
_dialog = e.NameScope.Find<Dialog>(MessageBoxThemeConstants.DialogPart);
_dialog = e.NameScope.Find<MessageBoxDialog>(MessageBoxThemeConstants.DialogPart);
if (_dialog != null)
{
_dialog.Opened += HandleDialogOpened;
@@ -450,4 +559,4 @@ public class MessageBox : TemplatedControl,
}
}
}
}
}
@@ -1,5 +1,4 @@
using System.Reactive.Disposables;
using AtomUI.Controls;
using AtomUI.Data;
using AtomUI.Desktop.Controls.Primitives;
using Avalonia;
@@ -0,0 +1,31 @@
using Avalonia.Controls;
namespace AtomUI.Desktop.Controls;
public record MessageBoxOptions
{
public DialogHostType HostType { get; init; } = DialogHostType.Overlay;
public string? Title { get; init; }
public PathIcon? Icon { get; init; }
public MessageBoxStyle Style { get; init; } = MessageBoxStyle.Information;
public bool IsModal { get; init; } = true;
public bool IsDragMovable { get; init; }
public Control? PlacementTarget { get; init; }
public Dimension? HorizontalOffset { get; init; }
public Dimension? VerticalOffset { get; init; }
public bool IsCenterOnStartup { get; init; } = true;
/// <summary>
/// 次选项仅 Overlay Host 类型的弹窗有效
/// </summary>
public bool IsLightDismissEnabled { get; init; }
public bool IsLoading { get; init; }
public bool IsConfirmLoading { get; init; }
public double MaxHeight { get; init; } = double.PositiveInfinity;
public double MaxWidth { get; init; } = double.PositiveInfinity;
public double MinHeight { get; init; } = 0d;
public double MinWidth { get; init; } = 0d;
public double Width { get; init; } = double.NaN;
public double Height { get; init; } = double.NaN;
}
@@ -61,19 +61,18 @@
</ControlTemplate>
</Setter>
<Style Selector="^ /template/ atom|IconPresenter#StyleIconPresenter">
<Setter Property="Width" Value="{DynamicResource {x:Static atom:MessageBoxTokenKey.StyleIconSize}}" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:MessageBoxTokenKey.StyleIconSize}}" />
</Style>
<Style Selector="^[Style=Confirm]">
<Style Selector="^ /template/ atom|IconPresenter#StyleIconPresenter">
<Setter Property="IconBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorWarning}}" />
</Style>
</Style>
<Style Selector="^ /template/ atom|IconPresenter#StyleIconPresenter">
<Setter Property="Width" Value="{DynamicResource {x:Static atom:MessageBoxTokenKey.StyleIconSize}}" />
<Setter Property="Height" Value="{DynamicResource {x:Static atom:MessageBoxTokenKey.StyleIconSize}}" />
</Style>
<Style Selector="^[Style=Information]">
<Style Selector="^ /template/ atom|IconPresenter#StyleIconPresenter">
<Setter Property="IconBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorInfo}}" />
@@ -339,6 +339,16 @@ public class Window : AvaloniaWindow,
ConfigureCustomResizerVisible();
}
public static Window? GetMainWindow()
{
var lifetime = Application.Current?.ApplicationLifetime;
if (lifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
return desktopLifetime.MainWindow as Window;
}
return null;
}
protected override void OnClosed(EventArgs e)
{
Dispose();
@@ -2,6 +2,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform;
namespace AtomUI.Desktop.Controls;