假设我有以下视图模型:
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
完全设置一个新的(也不适应 a null
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; }
}
}
}
主窗口.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>