using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Windows.Input;
namespace HandyControl.Interactivity.Commands
{
///
/// An whose delegates can be attached for and .
///
public abstract class DelegateCommandBase : BindableBase, ICommand, IActiveAware
{
private bool _isActive;
private SynchronizationContext? _synchronizationContext;
private readonly HashSet _observedPropertiesExpressions = new();
///
/// Provides an Exception Handler to register callbacks or handle encountered exceptions within
///
protected readonly MulticastExceptionHandler ExceptionHandler = new();
///
/// Creates a new instance of a , specifying both the execute action and the can execute function.
///
protected DelegateCommandBase()
{
_synchronizationContext = SynchronizationContext.Current;
}
///
/// Occurs when changes occur that affect whether or not the command should execute.
///
public virtual event EventHandler? CanExecuteChanged;
///
/// Raises so every
/// command invoker can re-query .
///
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);
}
}
///
/// Raises so every command invoker
/// can re-query to check if the command can execute.
///
/// Note that this will trigger the execution of once for each invoker.
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
void ICommand.Execute(object? parameter)
{
Execute(parameter);
}
bool ICommand.CanExecute(object? parameter)
{
return CanExecute(parameter);
}
///
/// Handle the internal invocation of
///
/// Command Parameter
protected abstract void Execute(object? parameter);
///
/// Handle the internal invocation of
///
///
/// if the Command Can Execute, otherwise
protected abstract bool CanExecute(object? parameter);
///
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
///
/// The object type containing the property specified in the expression.
/// The property expression. Example: ObservesProperty(() => PropertyName).
protected internal void ObservesPropertyInternal(Expression> propertyExpression)
{
if (_observedPropertiesExpressions.Contains(propertyExpression.ToString()))
{
throw new ArgumentException($"{propertyExpression} is already being observed.",
nameof(propertyExpression));
}
else
{
_observedPropertiesExpressions.Add(propertyExpression.ToString());
PropertyObserver.Observes(propertyExpression, RaiseCanExecuteChanged);
}
}
#region IsActive
///
/// Gets or sets a value indicating whether the object is active.
///
/// if the object is active; otherwise .
public bool IsActive
{
get => _isActive;
set => SetProperty(ref _isActive, value, OnIsActiveChanged);
}
///
/// Fired if the property changes.
///
public virtual event EventHandler? IsActiveChanged;
///
/// This raises the event.
///
protected virtual void OnIsActiveChanged()
{
IsActiveChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
}
}