考虑将继承与ReactiveUI一起使用。 我有基于ViewModel的DoSomethingCommand类。此命令的“CanExecute”取决于属性Prop1
public class A : ReactiveObject
{
public int Prop1 { get {...} set {...} }
public ReactiveCommand DoSomethingCommand { get; private set; }
public A()
{
IObservable<bool> canDoSomething = this.WhenAny(vm => vm.Prop1, p1 => CanDoSomething());
DoSomethingCommand = new ReactiveCommand(canDoSomething);
DoSomethingCommand.Subscribe(x => DoSomething());
}
protected virtual bool CanDoSomething()
{
return ...
}
}
在继承的类中,此命令的“CanExecute”还取决于属性Prop2
public class B : A
{
public int Prop2 { get {...} set {...} }
public B()
{
//Senseless code. For explanation only
IObservable<bool> canDeleteExecute = this.WhenAny(vm => vm.Prop1, vm => vm.Prop2, (p1, p2) => CanDoSomething());
}
}
创建命令并使'CanExecute'依赖于基类和继承类的属性的最佳做法是什么? 当然,我希望当基类中的'CanExecute'变得依赖于AnotherProp属性时,继承的类不应该改变。
答案 0 :(得分:1)
我会在我的基类中创建一个注册函数来注册 使用CombineLatest的可观察量
property IObservable<bool> CanDo;
public IObservable<bool> RegisterCanDo( IObservable<bool> toRegister ){
if ( CanDo == null ){
CanDo = toRegister;
}else{
Cando = CanDo.CombineLatest(toRegister, (a,b) => a && b);
}
}
所以,现在任何地方都有一个可以观察到你想成为你的一部分 CanDo链接你只需用RegisterCanDo
添加它 public class B : A
{
public int Prop2 { get {...} set {...} }
public B()
{
//Senseless code. For explanation only
IObservable<bool> canDeleteExecute = this.WhenAny(vm => vm.Prop1, vm => vm.Prop2, (p1, p2) => CanDoSomething());
RegisterCanDo(canDeleteExecute);
}
}
答案 1 :(得分:1)
这正是你想要的。但要小心线程。我没有为组合值添加和删除消息源做出线程安全。
您可以向Concentrator对象添加和删除消息源,您可以观察Concentrator的真实值。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
public class BoolObservableConcentrator : IObservable<bool>
{
private readonly Dictionary<IObservable<bool>, bool> dict = new Dictionary<IObservable<bool>, bool>();
public IDisposable Register(IObservable<bool> observable)
{
dict.Add(observable, false);
var d = observable.Subscribe(value =>
{
dict[observable] = value;
Fire();
});
return Disposable.Create(() =>
{
d.Dispose();
dict.Remove(observable);
Fire();
});
}
private readonly Subject<bool> subject = new Subject<bool>();
private void Fire()
{
subject.OnNext(dict.Values.All(x => x));
}
public IDisposable Subscribe(IObserver<bool> observer)
{
return subject.Subscribe(observer);
}
}
并测试它:
using System;
using System.Reactive.Subjects;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class UnitTest
{
[TestMethod]
public void TestMethod1()
{
var s0 = new BehaviorSubject<bool>(false);
var s1 = new BehaviorSubject<bool>(false);
bool l = false;
var c = new BoolObservableConcetrator();
var r0 = c.RegisterSource(s0);
var r1 = c.RegisterSource(s1);
var s = c.Subscribe(v => l = v);
Assert.AreEqual(false, l);
s0.OnNext(true);
Assert.AreEqual(false, l);
s1.OnNext(true);
Assert.AreEqual(true, l);
s0.OnNext(false);
Assert.AreEqual(false, l);
// Removing one of the message sources should update the result
r0.Dispose();
Assert.AreEqual(true, l);
}
}
答案 2 :(得分:1)
为什么不在派生的构造函数中替换它:
更新:这是一个将原始版本考虑在内的版本
public class A
{
public ReactiveCommand SomeCommand { get; protected set; }
public A()
{
SomeCommand = new ReactiveCommand(this.WhenAny(x => x.SomeProp, ...));
}
}
public class B : A
{
public A()
{
var newWhenAny = this.WhenAny(x => x.SomeOtherProp, ...);
var canExecute = SomeCommand == null ?
newWhenAny :
SomeCommand.CanExecuteObservable.CombineLatest(newWhenAny,(oldCommand, whenAny) => oldCommand && whenAny);
SomeCommand = new ReactiveCommand(canExecute);
}
}
答案 3 :(得分:1)
我为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;
}
}