2013-01-24 23 views
10

Giả sử tôi có các mô hình điểm sau:Có mẫu để đăng ký thay đổi thuộc tính phân cấp với UI phản ứng không?

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

Bây giờ giả sử rằng trong EmployeeViewModel Tôi muốn để lộ một tài sản với giá trị mới nhất của 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; } 
} 

này sẽ chỉ đánh dấu khi một sự thay đổi các tài sản Address được thực hiện, nhưng không phải khi một sự thay đổi để Line trong vòng Address xảy ra. Nếu tôi thay vì làm điều này:

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

này sẽ chỉ đánh dấu khi một sự thay đổi để Line trong hiện tại AddressViewModel xảy ra, nhưng không đưa vào tài khoản thiết lập một mới AddressViewModel hoàn toàn (và cũng không phù hợp với một nullAddress).

Tôi đang cố gắng giải quyết vấn đề này. Tôi mới đến RxUI vì vậy tôi có thể thiếu một cái gì đó hiển nhiên. Tôi có thể móc thủ công vào các thay đổi địa chỉ và thiết lập đăng ký phụ, nhưng điều này có vẻ xấu và dễ xảy ra lỗi.

Có mẫu hoặc trợ giúp chuẩn nào tôi nên sử dụng để đạt được điều này không?

Dưới đây là một số mã có thể được sao chép/dán này để thử ra:

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> 

Trả lời

15

Nope, có cách một cách dễ dàng hơn để làm điều này:

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

Đó là .... rực rỡ! Chỉ cần thử nó và nó đã làm việc. Tôi đã hiểu nhầm toán tử 'WhenAny' khi quan sát bất kỳ thuộc tính" đuôi "nào (' Dòng' trong ví dụ trên). Tôi đã không nhận ra nó cũng có những người thông minh để quan sát bất kỳ tài sản nào trong chuỗi. Cảm ơn Paul. –

+0

là this.RaiseAndSetIfChanged (x => x.Address, ref this.address, value); cú pháp vẫn được hỗ trợ? Tôi chỉ có thể nhận được cú pháp sau đây; this.RaiseAndSetIfChanged (ref this.address, value); – Elangesh

+1

Không, cú pháp cũ không được chấp nhận, bạn chỉ nên sử dụng cú pháp mới ngay bây giờ. –

0

Nếu bạn xem xét Địa chỉ của bạn có thể là một bộ sưu tập đơn giản của dòng tức là

class AddressViewModel 
: ObservableCollection<LineViewModel>, INotifyPropertyChanged 

thì có thể bạn sẽ đính kèm một xử lý sự kiện để tuyên truyền những thay đổi

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

Vì vậy, tôi sẽ làm gì cùng một loại điều cho ReactiveObject nhưng sử dụng 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

Đây là loại phương pháp tiếp cận cơ sở mã hiện tại của tôi mất. Tuy nhiên, khi tôi đang tích hợp RxUI, tôi nghĩ rằng phải có một cách sạch hơn nhiều để làm điều này. –

+0

Hãy cho tôi biết nếu bạn tìm thấy :-) – AlSki

+4

Không bao giờ viết bất cứ điều gì trong setter trong ReactiveUI khác hơn RaiseAndSetIfChanged - nếu bạn đang có, bạn chắc chắn đang làm nó sai –