我试图和Avalonia一起使用ReactiveUI。由于Avalonia 0.10预览中的初始化顺序,以下代码失败:
class ViewModel : IActivatableViewModel
{
public ViewModel(){
this.WhenActivated(disposables => {
_myProperty = observable.ToProperty(this, nameof(MyProperty)).DisposeWith(disposables).
});
}
private ObservableAsPropertyHelper<object> _myProperty = null!;
public object MyProperty => _myProperty.Value;
}因为WhenActivated是在视图绑定到viewModel之后调用的(因此_myProperty为null)。
我看不出任何简单的解决办法,需要大量的黑客,手动提升属性等。
所以问题是:
如何在Avalonia与OAPH和WhenActivated合作?
发布于 2020-12-04 18:42:06
选项#1
允许您解决问题的最明显的模式是使用空合并运算符。通过使用这个操作符,您可以通过调整代码来达到预期的行为,如下所示:
private ObservableAsPropertyHelper<TValue>? _myProperty;
public TValue MyProperty => _myProperty?.Value;在这里,我们使用新的C#可空注释显式地将声明的字段标记为可空。我们这样做是因为在调用WhenActivated块之前,_myProperty字段被设置为null。此外,我们在这里使用_myProperty?.Value语法,因为当视图模型未初始化时,MyProperty getter应该返回null。
选项2
另一个更好的选择是,将ToProperty订阅移到WhenActivated块之外,并将ObservableAsPropertyHelper<T>字段标记为readonly。如果您的计算属性没有订阅超过视图模型的外部服务,那么您就不需要释放ToProperty返回的订阅。在90%的情况下,您不需要将ToProperty调用保存在WhenActivated中。有关更多信息,请参见我什么时候该费心处理IDisposable对象?文档页面。还请参阅冷热观测文章,这篇文章也可以说明这个主题。因此,在90%的情况下,编写这样的代码是一个很好的方法:
private readonly ObservableAsPropertyHelper<TValue> _myProperty;
public TValue MyProperty => _myProperty.Value;
// In the view model constructor:
_myProperty = obs.ToProperty(this, x => x.MyProperty);如果您实际上是订阅外部服务(例如通过构造函数注入视图模型),则可以将MyProperty转换为具有私有设置器的读-写属性,并编写以下代码:
class ViewModel : IActivatableViewModel
{
public ViewModel(IDependency dependency)
{
this.WhenActivated(disposables =>
{
// We are using 'DisposeWith' here as we are
// subscribing to an external dependency that
// could potentially outlive the view model. So
// we need to dispose the subscription in order
// to avoid the potential for a memory leak.
dependency
.ExternalHotObservable
.Subscribe(value => MyProperty = value)
.DisposeWith(disposables);
});
}
private TValue _myProperty;
public TValue MyProperty
{
get => _myProperty;
private set => this.RaiseAndSetIfChanged(ref _myProperty, value);
}
}此外,如果ReactiveUI.Fody语法对您来说过于冗长,请看一看RaiseAndSetIfChanged。
选项3(我建议使用此选项)
值得注意的是,Avalonia支持绑定到任务和可观察性。这是一个非常有用的特性,我强烈建议您尝试一下。这意味着,在Avalonia中,您可以简单地将计算的属性声明为IObservable<TValue>,而Avalonia将为您管理订阅的生命周期。所以在视图模型中这样做:
class ViewModel : IActivatableViewModel
{
public ViewModel()
{
MyProperty =
this.WhenAnyValue(x => x.AnotherProperty)
.Select(value => $"Hello, {value}!");
}
public IObservable<TValue> MyProperty { get; }
// lines omitted for brevity
}在视图中,编写以下代码:
<TextBlock Text="{Binding MyProperty^}"/>OAPHs是为那些不会做这种把戏的平台而发明的,但是Avalonia非常擅长聪明的标记扩展。因此,如果您是针对多个UI框架和编写与框架无关的视图模型,那么OAPHs是最好的选择。但是,如果您只针对Avalonia,那么只需使用{Binding ^}。
选项#4
或者,如果您更喜欢使用代码背后的ReactiveUI绑定,则将选项3中的视图模型代码与xaml.cs文件中视图侧的以下代码结合起来:
this.WhenActivated(cleanup => {
this.WhenAnyObservable(x => x.ViewModel.MyProperty)
.BindTo(this, x => x.NamedTextBox.Text)
.DisposeWith(cleanup);
});这里我们假设xaml文件如下所示:
<TextBlock x:Name="NamedTextBox" />我们现在有了一个源发生器,它可以帮助生成x:Name引用。
https://stackoverflow.com/questions/65146860
复制相似问题