mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-05-25 06:49:03 -05:00
162 lines
7.6 KiB
C#
162 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Windows.Input;
|
|
|
|
namespace HavenSoft.HexManiac.Core.ViewModels {
|
|
public static class PropertyChangedEventHandlerExtensions {
|
|
public static void Notify(this PropertyChangedEventHandler handler, INotifyPropertyChanged sender, [CallerMemberName]string propertyName = null) {
|
|
handler?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
|
|
public static void Notify<T>(this PropertyChangedEventHandler handler, INotifyPropertyChanged sender, T oldValue, [CallerMemberName]string propertyName = null) {
|
|
handler?.Invoke(sender, new ExtendedPropertyChangedEventArgs<T>(oldValue, propertyName));
|
|
}
|
|
|
|
public static void Notify(this PropertyChangedEventHandler handler, INotifyPropertyChanged sender, PropertyChangedEventArgs args) => handler?.Invoke(sender, args);
|
|
|
|
public static bool TryUpdate<T>(this PropertyChangedEventHandler handler, INotifyPropertyChanged sender, ref T field, T value, [CallerMemberName]string propertyName = null) where T : IEquatable<T> {
|
|
if (field == null && value == null) return false;
|
|
if (field != null && field.Equals(value)) return false;
|
|
var oldValue = field;
|
|
field = value;
|
|
handler.Notify(sender, oldValue, propertyName);
|
|
return true;
|
|
}
|
|
|
|
public static bool TryUpdateEnum<T>(this PropertyChangedEventHandler handler, INotifyPropertyChanged sender, ref T field, T value, [CallerMemberName]string propertyName = null) where T : Enum {
|
|
if (field.Equals(value)) return false;
|
|
var oldValue = field;
|
|
field = value;
|
|
handler.Notify(sender, oldValue, propertyName);
|
|
return true;
|
|
}
|
|
|
|
public static void Bind<T>(this T viewModel, string propertyName, Action<T, PropertyChangedEventArgs> handler) where T : INotifyPropertyChanged {
|
|
viewModel.PropertyChanged += (sender, e) => {
|
|
if (propertyName == e.PropertyName) {
|
|
var instance = (T)sender;
|
|
handler(instance, e);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Utility base-class that adds the PropertyChanged event and adds protected methods to simplify calling it.
|
|
/// It also adds protected methods to simplify the creation of commands.
|
|
/// </summary>
|
|
public class ViewModelCore : INotifyPropertyChanged {
|
|
#if DEBUG
|
|
private static int IDGenerator = 0;
|
|
public int ID { get; } = ++IDGenerator;
|
|
#endif
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
protected void NotifyPropertyChanged([CallerMemberName]string propertyName = null) {
|
|
Debug.Assert(GetType().GetProperty(propertyName) != null, $"Expected {propertyName} to be a property on type {GetType().Name}!");
|
|
PropertyChanged.Notify(this, propertyName);
|
|
}
|
|
|
|
protected void NotifyPropertyChanged<T>(T oldValue, [CallerMemberName]string propertyName = null) {
|
|
Debug.Assert(GetType().GetProperty(propertyName) != null, $"Expected {propertyName} to be a property on type {GetType().Name}!");
|
|
PropertyChanged.Notify(this, oldValue, propertyName);
|
|
}
|
|
|
|
protected void NotifyPropertyChanged(PropertyChangedEventArgs args) {
|
|
Debug.Assert(GetType().GetProperty(args.PropertyName) != null, $"Expected {args.PropertyName} to be a property on type {GetType().Name}!");
|
|
PropertyChanged.Notify(this, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Utility function to make writing property updates easier.
|
|
/// If the backing field's value does not match the new value, the backing field is updated and PropertyChanged gets called.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the property being updated.</typeparam>
|
|
/// <param name="backingField">A reference to the backing field of the property being changed.</param>
|
|
/// <param name="newValue">The new value for the property.</param>
|
|
/// <param name="propertyName">The name of the property to notify on. If the property is the caller, the compiler will figure this parameter out automatically.</param>
|
|
/// <returns>false if the data did not need to be updated, true if it did.</returns>
|
|
protected bool TryUpdate<T>(ref T backingField, T newValue, [CallerMemberName]string propertyName = null) where T : IEquatable<T> {
|
|
return PropertyChanged.TryUpdate(this, ref backingField, newValue, propertyName);
|
|
}
|
|
|
|
protected void Set<T>(ref T field, T value, Action<T> changeHandler, [CallerMemberName]string propertyName = null) where T : IEquatable<T> {
|
|
var oldValue = field;
|
|
if (PropertyChanged.TryUpdate(this, ref field, value, propertyName)) {
|
|
changeHandler?.Invoke(oldValue);
|
|
}
|
|
}
|
|
|
|
protected void SetEnum<T>(ref T field, T value, Action<T> changeHandler, [CallerMemberName]string propertyName = null) where T : Enum {
|
|
var oldValue = field;
|
|
if (PropertyChanged.TryUpdateEnum(this, ref field, value, propertyName)) {
|
|
changeHandler?.Invoke(oldValue);
|
|
}
|
|
}
|
|
|
|
protected void Set<T>(ref T field, T value, [CallerMemberName]string propertyName = null) where T : IEquatable<T> {
|
|
Set(ref field, value, null, propertyName);
|
|
}
|
|
|
|
protected void SetEnum<T>(ref T field, T value, [CallerMemberName]string propertyName = null)where T : Enum {
|
|
SetEnum(ref field, value, null, propertyName);
|
|
}
|
|
|
|
protected bool TryUpdateEnum<T>(ref T backingField, T newValue, [CallerMemberName]string propertyName = null) where T : Enum {
|
|
return PropertyChanged.TryUpdateEnum(this, ref backingField, newValue, propertyName);
|
|
}
|
|
|
|
protected bool TryUpdateSequence<T, U>(ref T backingField, T newValue, [CallerMemberName]string propertyName = null) where T : IEnumerable<U> where U : IEquatable<U> {
|
|
if (backingField == null && newValue == null) return false;
|
|
if (backingField != null && backingField.Count() == newValue.Count()) {
|
|
bool allMatch = true;
|
|
foreach (var pair in backingField.Zip(newValue, (a, b) => (a, b))) {
|
|
if (pair.a.Equals(pair.b)) continue;
|
|
allMatch = false;
|
|
break;
|
|
}
|
|
if (allMatch) return false;
|
|
}
|
|
var oldValue = backingField;
|
|
backingField = newValue;
|
|
NotifyPropertyChanged(oldValue, propertyName);
|
|
return true;
|
|
}
|
|
|
|
protected StubCommand StubCommand<T>(ref StubCommand field, Action<T> execute, Func<T, bool> canExecute = null) {
|
|
if (field != null) return field;
|
|
field = new StubCommand {
|
|
Execute = arg => execute(arg is T t ? t : default(T)),
|
|
CanExecute = arg => canExecute?.Invoke((T)arg) ?? true,
|
|
};
|
|
return field;
|
|
}
|
|
|
|
protected StubCommand StubCommand(ref StubCommand field, Action execute, Func<bool> canExecute = null) {
|
|
if (field != null) return field;
|
|
field = new StubCommand {
|
|
Execute = arg => execute(),
|
|
CanExecute = arg => canExecute?.Invoke() ?? true,
|
|
};
|
|
return field;
|
|
}
|
|
|
|
protected static IDisposable Scope<T>(ref T field, T value, Action<T> lambda) {
|
|
var originalValue = field;
|
|
var disposable = new StubDisposable { Dispose = () => lambda(originalValue) };
|
|
field = value;
|
|
return disposable;
|
|
}
|
|
}
|
|
|
|
public class ExtendedPropertyChangedEventArgs<T> : PropertyChangedEventArgs {
|
|
public T OldValue { get; }
|
|
public ExtendedPropertyChangedEventArgs(T oldValue, string propertyName) : base(propertyName) => OldValue = oldValue;
|
|
}
|
|
}
|