2012-10-30 3 views
1

ReactiveUI를 사용하여 상속을 고려하십시오. DoSomethingCommand를 기반으로 기본 ViewModel 클래스가 있습니다. 이 명령에 대한 'CanExecute'는이 명령에 대한 'CanExecute가' 상속 된 클래스에서 ReactiveCommand 및 WhenAny 사용

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()); 
    } 
} 

Prop2

추가 특성에 따라 상속 클래스에서 특성에 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 '기본 및 상속 된 클래스에서 속성에 종속적입니까? 물론 기본 클래스의 'CanExecute'가 AnotherProp 속성에 따라 달라지면 상속 된 클래스가 변경되지 않아야합니다.

답변

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; 
    } 
} 
1

나는 그래서

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의 일부 당신은 단지 그것을 추가 체인 만들고 싶어한다는 관측이 CombineLatest

를 사용하여 관찰 가능한 등록 내 기본 클래스에 등록 함수를 만들 것 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); 
     } 
    } 
+0

그러나 CanDo를 등록 해제 할 수있는 방법이 없기 때문에'CanDo'가 항상 거짓임을 확인하는'CombineLatest'를 잠재적으로 만들 것입니다. 아마도'RegisterCanDo'는'IDisposable'을 반환해야하고 내부'CanDo' 상태를 관리하는 메커니즘이 필요합니까? – Enigmativity

1

이것은 위에서 원하는 것입니다. 그러나 스레딩에주의하십시오. 나는 값을 조합 할 때 메시지 소스를 추가하고 제거하기 위해 스레드 세이프로 만들지 않았습니다.

메시지 소스를 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); 
    } 
} 
+1

원본 게시물은 여기에서 볼 수 있습니다. http://stackoverflow.com/revisions/13152784/1 – Emil

+0

@elshev C#으로 다시 작성해 주셔서 감사합니다. 나는 코드베이스에 따라 vb.net과 C# 사이를 끊임없이 전환하고있다. 나는 왜 MS가 VB.net을 사용하여 모든 목적으로 C#과 동일한지 궁금해하는데, ID가 제출 될 때까지 id가 vb.net 코드를 게시 한 것을 알지 못했습니다.) – bradgonesurfing

+1

하지만 스레드 안전에 대한 경고가 삭제되었습니다.내가 코드 combinelatest 코드를 확인하지 않은하지만 ID가 위의 코드는 않습니다 버퍼에 일종의 잠금을 할 것 같아요 – bradgonesurfing

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); 
    } 
} 
+0

내가 원했기 때문에 : 1. SomeCommand는 SomeProp과 SomeOtherProp에 동시에 의존합니다. 2. SomeCommand가 A 클래스의 SomeProp2, SomeProp3 등에 의존하게되면 B 클래스는 신경 쓰지 않아도됩니다. –

+0

업데이트 된 것 같습니다. –

관련 문제