Skip to content

Commit

Permalink
Add DelegateCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
ghost1372 committed Feb 24, 2025
1 parent fb39a4b commit 55a1713
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ If you encounter any issues or have feedback, please report them [here](https://

## 🔥 DevWinUI 🔥
### ⚡ What’s Inside? ⚡
- ✨ DelegateCommand
- ✨ RichTextFormatter
- ✨ Converter
- ✨ Extensions
Expand Down
76 changes: 76 additions & 0 deletions dev/DevWinUI/Common/Command/DelegateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace DevWinUI;

public static partial class DelegateCommand
{
public static IDelegateCommand Create(Action? execute)
{
return new SyncDelegateCommand(WrapAction(execute), CanExecuteTrue);
}

public static IDelegateCommand Create(Action<object?>? execute)
{
return new SyncDelegateCommand(execute ?? DefaultExecute, CanExecuteTrue);
}

public static IDelegateCommand Create(Action? execute, Func<bool>? canExecute)
{
return new SyncDelegateCommand(WrapAction(execute), WrapAction(canExecute));
}

public static IDelegateCommand Create(Action<object?>? execute, Func<object?, bool>? canExecute)
{
return new SyncDelegateCommand(execute ?? DefaultExecute, canExecute ?? CanExecuteTrue);
}

public static IDelegateCommand Create(Func<Task>? execute)
{
return new AsyncDelegateCommand(WrapAction(execute), CanExecuteTrue);
}

public static IDelegateCommand Create(Func<object?, Task>? execute)
{
return new AsyncDelegateCommand(execute ?? DefaultExecuteAsync, CanExecuteTrue);
}

public static IDelegateCommand Create(Func<Task>? execute, Func<bool>? canExecute)
{
return new AsyncDelegateCommand(WrapAction(execute), WrapAction(canExecute));
}

public static IDelegateCommand Create(Func<object?, Task>? execute, Func<object?, bool>? canExecute)
{
return new AsyncDelegateCommand(execute ?? DefaultExecuteAsync, canExecute ?? CanExecuteTrue);
}

private static void DefaultExecute(object? _)
{
}

private static Task DefaultExecuteAsync(object? _) => Task.CompletedTask;

private static bool CanExecuteTrue(object? _) => true;

private static Func<object?, Task> WrapAction(Func<Task>? action)
{
if (action is null)
return DefaultExecuteAsync;

return _ => action();
}

private static Action<object?> WrapAction(Action? action)
{
if (action is null)
return DefaultExecute;

return _ => action();
}

private static Func<object?, bool> WrapAction(Func<bool>? action)
{
if (action is null)
return CanExecuteTrue;

return _ => action();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Microsoft.UI.Dispatching;
using System.Diagnostics.CodeAnalysis;

namespace DevWinUI;

internal sealed partial class AsyncDelegateCommand : IDelegateCommand
{
private readonly Func<object?, Task> _execute;
private readonly Func<object?, bool> _canExecute;
private readonly DispatcherQueue _dispatcher;
private bool _isExecuting;

public event EventHandler? CanExecuteChanged;

public AsyncDelegateCommand(Func<object?, Task> execute, Func<object?, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
_dispatcher = DispatcherQueue.GetForCurrentThread();
}

public bool CanExecute(object? parameter)
{
return !_isExecuting && _canExecute.Invoke(parameter);
}

[SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Must be void")]
public async void Execute(object? parameter)
{
if (_isExecuting)
return;

try
{
_isExecuting = true;
RaiseCanExecuteChanged();
await _execute.Invoke(parameter);
}
finally
{
_isExecuting = false;
RaiseCanExecuteChanged();
}
}

public void RaiseCanExecuteChanged()
{
var canExecuteChanged = CanExecuteChanged;
if (canExecuteChanged is not null)
{
if (_dispatcher is not null)
{
_dispatcher.TryEnqueue(() => canExecuteChanged.Invoke(this, EventArgs.Empty));
}
else
{
canExecuteChanged.Invoke(this, EventArgs.Empty);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;

namespace DevWinUI;

public interface IDelegateCommand : ICommand
{
[SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "This method raise an existing event")]
void RaiseCanExecuteChanged();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Microsoft.UI.Dispatching;

namespace DevWinUI;

internal sealed partial class SyncDelegateCommand : IDelegateCommand
{
private readonly Action<object?> _execute;
private readonly Func<object?, bool> _canExecute;
private readonly DispatcherQueue _dispatcher;

public event EventHandler? CanExecuteChanged;

public SyncDelegateCommand(Action<object?> execute, Func<object?, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
_dispatcher = DispatcherQueue.GetForCurrentThread();
}

public bool CanExecute(object? parameter)
{
return _canExecute.Invoke(parameter);
}

public void Execute(object? parameter)
{
_execute.Invoke(parameter);
}

public void RaiseCanExecuteChanged()
{
if (_dispatcher is not null)
{
_dispatcher.TryEnqueue(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty));
}
else
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

0 comments on commit 55a1713

Please sign in to comment.