mirror of
https://github.com/jie65535/Helpers.git
synced 2025-06-01 17:29:12 +08:00
Add MVVM Base
Add DelegateCommand Add ViewModelBase
This commit is contained in:
parent
f856080ba3
commit
15b87f4685
297
MVVM/DelegateCommand.cs
Normal file
297
MVVM/DelegateCommand.cs
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
|
namespace Command
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="ICommand"/> implementation which relays the Execute and CanExecute
|
||||||
|
/// method to the specified delegates.
|
||||||
|
/// </summary>
|
||||||
|
public class DelegateCommand : ICommand
|
||||||
|
{
|
||||||
|
#region Static disabled command
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="DelegateCommand"/> instance that does nothing and can never be executed.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DelegateCommand Disabled = new DelegateCommand(() => { }) { IsEnabled = false };
|
||||||
|
|
||||||
|
#endregion Static disabled command
|
||||||
|
|
||||||
|
#region Private data
|
||||||
|
|
||||||
|
private readonly Action<object> execute;
|
||||||
|
private readonly Func<object, bool> canExecute;
|
||||||
|
private List<WeakReference> weakHandlers;
|
||||||
|
private bool? isEnabled;
|
||||||
|
private bool raiseCanExecuteChangedPending;
|
||||||
|
|
||||||
|
#endregion Private data
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DelegateCommand"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="execute">Delegate to execute when Execute is called on the command.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">The execute argument must not be null.</exception>
|
||||||
|
public DelegateCommand(Action execute)
|
||||||
|
: this(execute != null ? p => execute() : (Action<object>)null, (Func<object, bool>)null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DelegateCommand"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="execute">Delegate to execute when Execute is called on the command.</param>
|
||||||
|
/// <param name="canExecute">Delegate to execute when CanExecute is called on the command.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">The execute argument must not be null.</exception>
|
||||||
|
public DelegateCommand(Action execute, Func<bool> canExecute)
|
||||||
|
: this(execute != null ? p => execute() : (Action<object>)null, canExecute != null ? p => canExecute() : (Func<object, bool>)null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DelegateCommand"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="execute">Delegate to execute when Execute is called on the command.</param>
|
||||||
|
/// <param name="canExecute">Delegate to execute when CanExecute is called on the command.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">The execute argument must not be null.</exception>
|
||||||
|
public DelegateCommand(Action execute, Func<object, bool> canExecute)
|
||||||
|
: this(execute != null ? p => execute() : (Action<object>)null, canExecute)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DelegateCommand"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="execute">Delegate to execute when Execute is called on the command.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">The execute argument must not be null.</exception>
|
||||||
|
public DelegateCommand(Action<object> execute)
|
||||||
|
: this(execute, (Func<object, bool>)null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DelegateCommand"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="execute">Delegate to execute when Execute is called on the command.</param>
|
||||||
|
/// <param name="canExecute">Delegate to execute when CanExecute is called on the command.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">The execute argument must not be null.</exception>
|
||||||
|
public DelegateCommand(Action<object> execute, Func<bool> canExecute)
|
||||||
|
: this(execute, canExecute != null ? p => canExecute() : (Func<object, bool>)null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DelegateCommand"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="execute">Delegate to execute when Execute is called on the command.</param>
|
||||||
|
/// <param name="canExecute">Delegate to execute when CanExecute is called on the command.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">The execute argument must not be null.</exception>
|
||||||
|
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
|
||||||
|
{
|
||||||
|
if (execute == null)
|
||||||
|
throw new ArgumentNullException(nameof(execute));
|
||||||
|
|
||||||
|
this.execute = execute;
|
||||||
|
this.canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Constructors
|
||||||
|
|
||||||
|
#region CanExecuteChanged event
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when changes occur that affect whether or not the command should execute.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler CanExecuteChanged
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
if (weakHandlers == null)
|
||||||
|
{
|
||||||
|
weakHandlers = new List<WeakReference>(new[] { new WeakReference(value) });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
weakHandlers.Add(new WeakReference(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
if (weakHandlers == null) return;
|
||||||
|
|
||||||
|
for (int i = weakHandlers.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
WeakReference weakReference = weakHandlers[i];
|
||||||
|
EventHandler handler = weakReference.Target as EventHandler;
|
||||||
|
if (handler == null || handler == value)
|
||||||
|
{
|
||||||
|
weakHandlers.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:CanExecuteChanged"/> event.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public void RaiseCanExecuteChanged() => OnCanExecuteChanged();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:CanExecuteChanged"/> event after all other processing has
|
||||||
|
/// finished. Multiple calls to this function before the asynchronous action has been
|
||||||
|
/// started are ignored.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public void RaiseCanExecuteChangedAsync()
|
||||||
|
{
|
||||||
|
if (!raiseCanExecuteChangedPending)
|
||||||
|
{
|
||||||
|
// Don't do anything if not on the UI thread. The dispatcher event will never be
|
||||||
|
// fired there and probably there's nobody interested in changed properties anyway
|
||||||
|
// on that thread.
|
||||||
|
if (Dispatcher.CurrentDispatcher == Application.Current.Dispatcher)
|
||||||
|
{
|
||||||
|
Dispatcher.CurrentDispatcher.BeginInvoke((Action)OnCanExecuteChanged, DispatcherPriority.Loaded);
|
||||||
|
raiseCanExecuteChangedPending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:CanExecuteChanged"/> event.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
protected virtual void OnCanExecuteChanged()
|
||||||
|
{
|
||||||
|
raiseCanExecuteChangedPending = false;
|
||||||
|
PurgeWeakHandlers();
|
||||||
|
if (weakHandlers == null) return;
|
||||||
|
|
||||||
|
WeakReference[] handlers = weakHandlers.ToArray();
|
||||||
|
foreach (WeakReference reference in handlers)
|
||||||
|
{
|
||||||
|
EventHandler handler = reference.Target as EventHandler;
|
||||||
|
handler?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
private void PurgeWeakHandlers()
|
||||||
|
{
|
||||||
|
if (weakHandlers == null) return;
|
||||||
|
|
||||||
|
for (int i = weakHandlers.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!weakHandlers[i].IsAlive)
|
||||||
|
{
|
||||||
|
weakHandlers.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weakHandlers.Count == 0)
|
||||||
|
weakHandlers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion CanExecuteChanged event
|
||||||
|
|
||||||
|
#region ICommand methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the method that determines whether the command can execute in its current state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
|
||||||
|
/// <returns>true if this command can be executed; otherwise, false.</returns>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public bool CanExecute(object parameter) => isEnabled ?? canExecute?.Invoke(parameter) ?? true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convenience method that invokes CanExecute without parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if this command can be executed; otherwise, false.</returns>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public bool CanExecute() => CanExecute(null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the method to be called when the command is invoked.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
|
||||||
|
/// <exception cref="InvalidOperationException">The <see cref="CanExecute(object)"/> method returns false.</exception>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public void Execute(object parameter)
|
||||||
|
{
|
||||||
|
if (!CanExecute(parameter))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The command cannot be executed because CanExecute returned false.");
|
||||||
|
}
|
||||||
|
execute(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convenience method that invokes the command without parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">The <see cref="CanExecute(object)"/> method returns false.</exception>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public void Execute() => Execute(null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the command if the <see cref="CanExecute(object)"/> method returns true.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
|
||||||
|
/// <returns>true if this command was executed; otherwise, false.</returns>
|
||||||
|
public bool TryExecute(object parameter)
|
||||||
|
{
|
||||||
|
if (CanExecute(parameter))
|
||||||
|
{
|
||||||
|
Execute(parameter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convenience method that invokes the command without parameters if the
|
||||||
|
/// <see cref="CanExecute(object)"/> method returns true.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if this command was executed; otherwise, false.</returns>
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
public bool TryExecute() => TryExecute(null);
|
||||||
|
|
||||||
|
#endregion ICommand methods
|
||||||
|
|
||||||
|
#region Enabled state
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the current DelegateCommand is enabled. If this
|
||||||
|
/// value is not null, it takes precedence over the canExecute function was passed in the
|
||||||
|
/// constructor. If no function was passed and this value is null the command is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsEnabled
|
||||||
|
{
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
[DebuggerStepThrough]
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != isEnabled)
|
||||||
|
{
|
||||||
|
isEnabled = value;
|
||||||
|
RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Enabled state
|
||||||
|
}
|
||||||
|
}
|
46
MVVM/ViewModelBase.cs
Normal file
46
MVVM/ViewModelBase.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace ViewModel
|
||||||
|
{
|
||||||
|
public abstract class ViewModelBase : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Multicast event for property change notifications.
|
||||||
|
/// </summary>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a property already matches a desired value. Sets the property and
|
||||||
|
/// notifies listeners only when necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of the property.</typeparam>
|
||||||
|
/// <param name="storage">Reference to a property with both getter and setter.</param>
|
||||||
|
/// <param name="value">Desired value for the property.</param>
|
||||||
|
/// <param name="propertyName">Name of the property used to notify listeners.This
|
||||||
|
/// value is optional and can be provided automatically when invoked from compilers that
|
||||||
|
/// support CallerMemberName.</param>
|
||||||
|
/// <returns>True if the value was changed, false if the existing value matched the
|
||||||
|
/// desired value.</returns>
|
||||||
|
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
if (Equals(storage, value))
|
||||||
|
return false;
|
||||||
|
storage = value;
|
||||||
|
// Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies listeners that a property value has changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyName">Name of the property used to notify listeners. This
|
||||||
|
/// value is optional and can be provided automatically when invoked from compilers
|
||||||
|
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user