Files
aistudio-wpf-diagram/AIStudio.Wpf.DiagramDesigner.Additionals/Commands/DelegateCommandBase.cs
2023-01-25 15:58:05 +08:00

140 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
{
/// <summary>
/// An <see cref="ICommand"/> whose delegates can be attached for <see cref="Execute"/> and <see cref="CanExecute"/>.
/// </summary>
public abstract class DelegateCommandBase : ICommand, IActiveAware
{
private bool _isActive;
private SynchronizationContext _synchronizationContext;
private readonly HashSet<string> _observedPropertiesExpressions = new HashSet<string>();
/// <summary>
/// Creates a new instance of a <see cref="DelegateCommandBase"/>, specifying both the execute action and the can execute function.
/// </summary>
protected DelegateCommandBase()
{
_synchronizationContext = SynchronizationContext.Current;
}
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public virtual event EventHandler CanExecuteChanged;
/// <summary>
/// Raises <see cref="ICommand.CanExecuteChanged"/> so every
/// command invoker can requery <see cref="ICommand.CanExecute"/>.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
_synchronizationContext.Post((o) => handler.Invoke(this, EventArgs.Empty), null);
else
handler.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// Raises <see cref="CanExecuteChanged"/> so every command invoker
/// can requery to check if the command can execute.
/// </summary>
/// <remarks>Note that this will trigger the execution of <see cref="CanExecuteChanged"/> once for each invoker.</remarks>
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
void ICommand.Execute(object parameter)
{
Execute(parameter);
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute(parameter);
}
/// <summary>
/// Handle the internal invocation of <see cref="ICommand.Execute(object)"/>
/// </summary>
/// <param name="parameter">Command Parameter</param>
protected abstract void Execute(object parameter);
/// <summary>
/// Handle the internal invocation of <see cref="ICommand.CanExecute(object)"/>
/// </summary>
/// <param name="parameter"></param>
/// <returns><see langword="true"/> if the Command Can Execute, otherwise <see langword="false" /></returns>
protected abstract bool CanExecute(object parameter);
/// <summary>
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
/// </summary>
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
protected internal void ObservesPropertyInternal<T>(Expression<Func<T>> propertyExpression)
{
if (_observedPropertiesExpressions.Contains(propertyExpression.ToString()))
{
throw new ArgumentException($"{propertyExpression.ToString()} is already being observed.",
nameof(propertyExpression));
}
else
{
_observedPropertiesExpressions.Add(propertyExpression.ToString());
PropertyObserver.Observes(propertyExpression, RaiseCanExecuteChanged);
}
}
#region IsActive
/// <summary>
/// Gets or sets a value indicating whether the object is active.
/// </summary>
/// <value><see langword="true" /> if the object is active; otherwise <see langword="false" />.</value>
public bool IsActive
{
get { return _isActive; }
set
{
if (_isActive != value)
{
_isActive = value;
OnIsActiveChanged();
}
}
}
/// <summary>
/// Fired if the <see cref="IsActive"/> property changes.
/// </summary>
public virtual event EventHandler IsActiveChanged;
/// <summary>
/// This raises the <see cref="DelegateCommandBase.IsActiveChanged"/> event.
/// </summary>
protected virtual void OnIsActiveChanged()
{
IsActiveChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
}
}