2013-01-24 20 views
10

假設我有以下看法型號:是否有一種模式可以用Reactive UI訂閱分層屬性更改?

public class AddressViewModel : ReactiveObject 
{ 
    private string line; 

    public string Line 
    { 
     get { return this.line; } 
     set { this.RaiseAndSetIfChanged(x => x.Line, ref this.line, value); } 
    } 
} 

public class EmployeeViewModel : ReactiveObject 
{ 
    private AddressViewModel address; 

    public AddressViewModel Address 
    { 
     get { return this.address; } 
     set { this.RaiseAndSetIfChanged(x => x.Address, ref this.address, value); } 
    } 
} 

現在假設在EmployeeViewModel我要揭露與Address.Line最新值的屬性:

public EmployeeViewModel() 
{ 
    this.changes = this.ObservableForProperty(x => x.Address) 
     .Select(x => x.Value.Line) 
     .ToProperty(this, x => x.Changes); 
} 

private readonly ObservableAsPropertyHelper<string> changes; 
public string Changes 
{ 
    get { return this.changes.Value; } 
} 

這隻會打勾時更改產生Address屬性,但在Address內發生更改爲Line時不會發生。如果我不是這樣做:

public EmployeeViewModel() 
{ 
    this.changes = this.Address.Changed 
     .Where(x => x.PropertyName == "Line") 
     .Select(x => this.Address.Line)  // x.Value is null here, for some reason, so I use this.Address.Line instead 
     .ToProperty(this, x => x.Changes); 
} 

當電流AddressViewModel內的變化Line發生這樣只會蜱,但沒有考慮設置新AddressViewModel共(也不適應nullAddress)。

我試圖讓我的頭繞着正確的方法來解決這個問題。我是RxUI的新手,所以我可能會錯過一些明顯的東西。我可能手動掛鉤到地址的變化,並設置二次訂閱,但這似乎醜陋和容易出錯。

有沒有一個標準模式或幫手,我應該用來實現這一目標?

下面是一些代碼,可以複製/粘貼嘗試了這一點:

ViewModels.cs

namespace RxUITest 
{ 
    using System; 
    using System.Reactive.Linq; 
    using System.Threading; 
    using System.Windows.Input; 
    using ReactiveUI; 
    using ReactiveUI.Xaml; 

    public class AddressViewModel : ReactiveObject 
    { 
     private string line1; 

     public string Line1 
     { 
      get { return this.line1; } 
      set { this.RaiseAndSetIfChanged(x => x.Line1, ref this.line1, value); } 
     } 
    } 

    public class EmployeeViewModel : ReactiveObject 
    { 
     private readonly ReactiveCommand changeAddressCommand; 
     private readonly ReactiveCommand changeAddressLineCommand; 
     private readonly ObservableAsPropertyHelper<string> changes; 
     private AddressViewModel address; 
     private int changeCount; 

     public EmployeeViewModel() 
     { 
      this.changeAddressCommand = new ReactiveCommand(); 
      this.changeAddressLineCommand = new ReactiveCommand(); 

      this.changeAddressCommand.Subscribe(x => this.Address = new AddressViewModel() { Line1 = "Line " + Interlocked.Increment(ref this.changeCount) }); 
      this.changeAddressLineCommand.Subscribe(x => this.Address.Line1 = "Line " + Interlocked.Increment(ref this.changeCount)); 

      this.Address = new AddressViewModel() { Line1 = "Default" }; 

      // Address-only changes 
      this.changes = this.ObservableForProperty(x => x.Address) 
       .Select(x => x.Value.Line1 + " CHANGE") 
       .ToProperty(this, x => x.Changes); 

      // Address.Line1-only changes 
      //this.changes = this.Address.Changed 
      // .Where(x => x.PropertyName == "Line1") 
      // .Select(x => this.Address.Line1 + " CHANGE")  // x.Value is null here, for some reason, so I use this.Address.Line1 instead 
      // .ToProperty(this, x => x.Changes); 
     } 

     public ICommand ChangeAddressCommand 
     { 
      get { return this.changeAddressCommand; } 
     } 

     public ICommand ChangeAddressLineCommand 
     { 
      get { return this.changeAddressLineCommand; } 
     } 

     public AddressViewModel Address 
     { 
      get { return this.address; } 
      set { this.RaiseAndSetIfChanged(x => x.Address, ref this.address, value); } 
     } 

     public string Changes 
     { 
      get { return this.changes.Value; } 
     } 
    } 
} 

MainWindow.cs

using System.Windows; 

namespace RxUITest 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      this.DataContext = new EmployeeViewModel(); 
     } 
    } 
} 

MainWindow.xaml

<Window x:Class="RxUITest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <StackPanel> 
     <TextBlock Text="{Binding Changes}"/> 
     <Button Command="{Binding ChangeAddressCommand}">Change Address</Button> 
     <Button Command="{Binding ChangeAddressLineCommand}">Change Address.Line1</Button> 
    </StackPanel> 
</Window> 

回答

15

不,有這樣做的方式更簡單的方法:

this.WhenAny(x => x.Address.Line, x => x.Value) 
    .Subscribe(x => Console.WriteLine("Either Address or Address.Line changed!")); 
+2

太精彩了!剛剛嘗試過,它工作。我誤解了'WhenAny'操作符觀察任何數量的「尾部」屬性(上例中的'Line')。我沒有意識到它也有智慧觀察連鎖店中的任何財產。謝謝保羅。 –

+0

is this.RaiseAndSetIfChanged(x => x.Address,ref this.address,value);語法仍然支持?我只能得到以下語法; this.RaiseAndSetIfChanged(ref this.address,value); – Elangesh

+1

不,舊的語法已過時,現在只應使用新的語法。 –

0

如果你認爲你的地址可能是線條的簡單集合,即

class AddressViewModel 
: ObservableCollection<LineViewModel>, INotifyPropertyChanged 

,那麼你很可能會附加一個事件處理程序來傳播完成變更

this.CollectionChanged += this.NotifyPropertyChanged(...); 

所以我會做對於ReactiveObject但是使用Rx的同類事物

public class EmployeeViewModel : ReactiveObject 
{ 
private AddressViewModel address; 
private IDisposable addressSubscription; 

public AddressViewModel Address 
{ 
    get { return this.address; } 
    set 
    { 
     if (addressSubscription != null) 
     { 
      addressSubscription.Dispose(); 
      addressSubscription =null; 
     } 
     if (value != null) 
     { 
      addressSubscription = value.Subscribe(...); 
     } 
     this.RaiseAndSetIfChanged(x => x.Address, ref this.address, value); 
    } 
} 

}

+0

這是我現有的代碼庫採取那種方法。但是,在我整合RxUI的時候,我認爲必須有一個更清潔的方法才能做到這一點。 –

+0

讓我知道,如果你找到:-) – AlSki

+4

永遠不會在ReactiveUI中寫入任何東西,除了RaiseAndSetIfChanged - 如果是的話,你絕對是做錯了™ –

相關問題