WhenAnyの拡張クラスを作成しました。ReactiveUIのWhenAnyのようにするためにやるべきことはもっとたくさんありますが、今は十分です。まず、使用法を見てください。
public class A : ReactiveObject
{
public A()
{
//Using almost like WhenAny from ReactiveUI
CanExecuteObservable = this.WhenAny(() => AProp, CanExecute);
Command = new ReactiveCommand(CanExecuteObservable);
Command.Subscribe(x => Execute());
}
protected CanExecuteObservable CanExecuteObservable { get; private set; }
public ReactiveCommand Command { get; private set; }
protected virtual bool CanExecute()
{
return AProp > 10;
}
private int aProp = 10;
public int AProp { get { return aProp; } set { this.RaiseAndSetIfChanged(x => x.AProp, value); } }
}
public class B : A
{
public B()
{
//Add one more property dependency for CanExecute
CanExecuteObservable.AddProperties(() => BProp);
}
private int bProp = 10;
public int BProp { get { return bProp; } set { this.RaiseAndSetIfChanged(x => x.BProp, value); } }
protected override bool CanExecute()
{
return base.CanExecute() && BProp > 100;
}
}
実装:
public static class WhenAnyExtensions
{
public static CanExecuteObservable WhenAny(this IReactiveNotifyPropertyChanged obj,
IEnumerable<Expression<Func<object>>> expressions, Func<bool> func)
{
return new CanExecuteObservable(obj, expressions, func);
}
public static CanExecuteObservable WhenAny(this IReactiveNotifyPropertyChanged obj, Expression<Func<object>> property1, Func<bool> func)
{
return obj.WhenAny(new[] { property1 }, func);
}
public static CanExecuteObservable WhenAny(this IReactiveNotifyPropertyChanged obj, Expression<Func<object>> property1, Expression<Func<object>> property2, Func<bool> func)
{
return obj.WhenAny(new[] { property1, property2 }, func);
}
//etc...
}
public class CanExecuteObservable : IObservable<bool>
{
internal CanExecuteObservable(IReactiveNotifyPropertyChanged obj,
IEnumerable<Expression<Func<object>>> expressions, Func<bool> func)
{
this.func = func;
AddProperties(expressions);
obj
.Changed
.Where(oc => propertyNames.Any(propertyName => propertyName == oc.PropertyName))
.Subscribe(oc => Fire());
}
private readonly List<string> propertyNames = new List<string>();
private readonly Func<bool> func;
public void AddProperties(IEnumerable<Expression<Func<object>>> expressions)
{
foreach (var expression in expressions)
{
string propertyName = ReflectionHelper.GetPropertyNameFromExpression(expression);
propertyNames.Add(propertyName);
}
}
public void AddProperties(Expression<Func<object>> property1) { AddProperties(new[] { property1 }); }
public void AddProperties(Expression<Func<object>> property1, Expression<Func<object>> property2) { AddProperties(new[] { property1, property2 }); }
//etc...
public void Clear()
{
propertyNames.Clear();
}
private readonly Subject<bool> subject = new Subject<bool>();
private void Fire()
{
subject.OnNext(func());
}
public IDisposable Subscribe(IObserver<bool> observer)
{
return subject.Subscribe(observer);
}
}
そして、この文脈では、式からプロパティ名を取得するためのヘルパークラスは面白くありません。
public class ReflectionHelper
{
public static string GetPropertyNameFromExpression<T>(Expression<Func<T>> property)
{
var lambda = (LambdaExpression)property;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
return memberExpression.Member.Name;
}
}