假设我有以下视图模型:
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
属性进行更改时才会勾选,但在Line
内发生Address
更改时则不会。如果我这样做:
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);
}
仅当当前Line
内的AddressViewModel
发生更改时才会勾选,但不会考虑完全设置新的AddressViewModel
(也不会容纳null
1}} Address
)。
我正试图找到解决这个问题的正确方法。我是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>
答案 0 :(得分:15)
不,有一种更简单的方法可以做到这一点:
this.WhenAny(x => x.Address.Line, x => x.Value)
.Subscribe(x => Console.WriteLine("Either Address or Address.Line changed!"));
答案 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);
}
}
}