选项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 对象?文档页面了解更多信息。另请参阅Hot and Cold observables文章,该文章也可以阐明该主题。因此,在 90% 的情况下编写这样的代码是一个好方法:
private readonly ObservableAsPropertyHelper<TValue> _myProperty;
public TValue MyProperty => _myProperty.Value;
// In the view model constructor:
_myProperty = obs.ToProperty(this, x => x.MyProperty);
如果您实际上订阅了外部服务,例如通过构造函数注入到视图模型中,那么您可以MyProperty
使用私有 setter 转换为读写属性,并编写以下代码:
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 支持绑定到 Tasks 和 Observables。这是一个非常有用的功能,我强烈建议您尝试一下。这意味着,在 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^}"/>
OAPH 是为无法实现此类技巧的平台而发明的,但 Avalonia 非常擅长巧妙的标记扩展。因此,如果您的目标是多个 UI 框架并编写与框架无关的视图模型,那么 OAPH 是不错的选择。但是,如果您仅针对 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
参考。