mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-20 23:58:05 +08:00
Add Static API for MessageBox
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+6
-7
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user