feat: add async command support.

This commit is contained in:
Dong Bin
2025-04-18 02:37:27 +08:00
parent e9a94798e3
commit 5da5e0626a
3 changed files with 57 additions and 13 deletions

View File

@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@@ -15,12 +16,13 @@ public partial class PopConfirmDemoViewModel: ObservableObject
public PopConfirmDemoViewModel() public PopConfirmDemoViewModel()
{ {
ConfirmCommand = new RelayCommand(OnConfirm); ConfirmCommand = new AsyncRelayCommand(OnConfirm);
CancelCommand = new RelayCommand(OnCancel); CancelCommand = new RelayCommand(OnCancel);
} }
private void OnConfirm() private async Task OnConfirm()
{ {
await Task.Delay(3000);
ToastManager?.Show(new Toast("Confirmed"), type: NotificationType.Success, classes: ["Light"]); ToastManager?.Show(new Toast("Confirmed"), type: NotificationType.Success, classes: ["Light"]);
} }

View File

@@ -7,14 +7,24 @@
<ControlTemplate TargetType="u:PopConfirm"> <ControlTemplate TargetType="u:PopConfirm">
<Panel> <Panel>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/> <ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Popup IsOpen="True"> <Popup IsOpen="True"
Name="{x:Static u:PopConfirm.PART_Popup}"
Placement="Bottom" >
<Border Theme="{DynamicResource CardBorder}"> <Border Theme="{DynamicResource CardBorder}">
<StackPanel> <StackPanel>
<ContentPresenter Content="{TemplateBinding PopupHeader}" ContentTemplate="{TemplateBinding PopupHeaderTemplate}"/> <ContentPresenter Content="{TemplateBinding PopupHeader}" ContentTemplate="{TemplateBinding PopupHeaderTemplate}"/>
<ContentPresenter Content="{TemplateBinding PopupContent}" ContentTemplate="{TemplateBinding PopupContentTemplate}"/> <ContentPresenter Content="{TemplateBinding PopupContent}" ContentTemplate="{TemplateBinding PopupContentTemplate}"/>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="Confirm" Command="{TemplateBinding ConfirmCommand}" CommandParameter="{TemplateBinding ConfirmCommandParameter}"/> <Button
<Button Content="Cancel" Command="{TemplateBinding CancelCommand}" CommandParameter="{TemplateBinding CancelCommandParameter}"/> Name="{x:Static u:PopConfirm.PART_ConfirmButton}"
Content="Confirm"
Command="{TemplateBinding ConfirmCommand}"
CommandParameter="{TemplateBinding ConfirmCommandParameter}"/>
<Button
Name="{x:Static u:PopConfirm.PART_CancelButton}"
Content="Cancel"
Command="{TemplateBinding CancelCommand}"
CommandParameter="{TemplateBinding CancelCommandParameter}"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>

View File

@@ -1,9 +1,11 @@
using System.ComponentModel;
using System.Windows.Input; using System.Windows.Input;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Irihi.Avalonia.Shared.Helpers; using Irihi.Avalonia.Shared.Helpers;
@@ -11,6 +13,14 @@ namespace Ursa.Controls;
public class PopConfirm: ContentControl public class PopConfirm: ContentControl
{ {
public const string PART_ConfirmButton = "PART_ConfirmButton";
public const string PART_CancelButton = "PART_CancelButton";
public const string PART_Popup = "PART_Popup";
private Button? _confirmButton;
private Button? _cancelButton;
private Popup? _popup;
public static readonly StyledProperty<object?> PopupHeaderProperty = AvaloniaProperty.Register<PopConfirm, object?>( public static readonly StyledProperty<object?> PopupHeaderProperty = AvaloniaProperty.Register<PopConfirm, object?>(
nameof(PopupHeader)); nameof(PopupHeader));
@@ -100,14 +110,7 @@ public class PopConfirm: ContentControl
private void OnCommandChanged(AvaloniaPropertyChangedEventArgs<ICommand?> args) private void OnCommandChanged(AvaloniaPropertyChangedEventArgs<ICommand?> args)
{ {
var newValue = args.GetNewValue<ICommand>();
newValue.CanExecuteChanged += (sender, e) =>
{
if (args.Sender is PopConfirm popconfirm)
{
var b = newValue.CanExecute(this.ConfirmCommandParameter);
}
};
} }
public PopConfirm() public PopConfirm()
@@ -129,8 +132,37 @@ public class PopConfirm: ContentControl
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
_confirmButton = e.NameScope.Find<Button>(PART_ConfirmButton);
_cancelButton = e.NameScope.Find<Button>(PART_CancelButton);
_popup = e.NameScope.Find<Popup>(PART_Popup);
Button.ClickEvent.AddHandler(OnButtonClicked, _confirmButton, _cancelButton);
} }
private void OnButtonClicked(object sender, RoutedEventArgs e)
{
// This is a hack for MVVM toolkit that uses INotifyPropertyChanged for async command. It counts the number of
// IsRunning property changes to determine when the command is finished.
if (sender is Button button && button.Command is INotifyPropertyChanged inpc)
{
var count = 0;
void OnCommandPropertyChanged(object? sender, PropertyChangedEventArgs args)
{
if (args.PropertyName != "IsRunning") return;
count++;
if (count != 2) return;
inpc.PropertyChanged -= OnCommandPropertyChanged;
_popup?.SetValue(Popup.IsOpenProperty, false);
}
inpc.PropertyChanged += OnCommandPropertyChanged;
}
else
{
_popup?.SetValue(Popup.IsOpenProperty, false);
}
}
private IDisposable? _childChangeDisposable; private IDisposable? _childChangeDisposable;
protected override bool RegisterContentPresenter(ContentPresenter presenter) protected override bool RegisterContentPresenter(ContentPresenter presenter)