Supponiamo che io sono i seguenti modelli di vista:Esiste uno schema per la sottoscrizione alle modifiche di proprietà gerarchiche con l'interfaccia utente reattiva?
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); }
}
}
Supponiamo ora che nel EmployeeViewModel
voglio esporre una proprietà con l'ultimo valore di 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; }
}
questo sarà spuntare solo quando un cambiamento la proprietà Address
viene creata, ma non quando si verifica una modifica a Line
all'interno di Address
. Se io invece faccio questo:
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);
}
Questo sarà spuntare solo quando si verifica un cambiamento Line
all'interno della corrente AddressViewModel
, ma non tiene conto stabilendo un nuovo AddressViewModel
del tutto (né ospitare un null
Address
).
Sto cercando di capire come affrontare correttamente questo problema. Sono nuovo di RxUI quindi potrei mancare qualcosa di ovvio. I potrebbe corrispondere manualmente alle modifiche dell'indirizzo e impostare un abbonamento secondario, ma questo sembra brutto e soggetto a errori.
Esiste uno schema o un helper standard che dovrei utilizzare per ottenere questo risultato?
Ecco po 'di codice che può essere la copia/incollato di provare questo:
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>
È geniale! Ho appena provato e ha funzionato. Avevo frainteso l'operatore di "When Any" osservando un numero qualsiasi di proprietà "tail" ('Line' nell'esempio sopra). Non avevo capito che aveva anche l'intelligenza di osservare qualsiasi proprietà nella catena. Grazie Paolo. –
è this.RaiseAndSetIfChanged (x => x.Address, ref this.address, value); la sintassi è ancora supportata? Sono in grado di ottenere solo la seguente sintassi; this.RaiseAndSetIfChanged (ref this.address, value); – Elangesh
No, la vecchia sintassi è deprecata, dovresti usare solo la nuova sintassi ora. –