gpt4 book ai didi

c# - 如何将 WhenActivated 与 avalonia 中的属性一起使用

转载 作者:行者123 更新时间:2023-12-05 02:47:09 26 4
gpt4 key购买 nike

我正在尝试将 ReactiveUI 与 Avalonia 一起使用。由于 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 在 View 绑定(bind)到 viewModel 之后被调用(因此 _myProperty 为 null)。

我认为没有简单的解决方法需要大量 hack、手动提升属性等。

那么问题是:

如何在 Avalonia 中使用 OAPH 和 WhenActivated?

最佳答案

选项#1

可让您解决此问题的最明显模式是使用空合并运算符。通过使用此运算符,您可以通过将代码调整为如下所示来实现所需的行为:

private ObservableAsPropertyHelper<TValue>? _myProperty;
public TValue MyProperty => _myProperty?.Value;

在这里,我们使用新的 C# 可空注释将声明的字段显式标记为可空。我们这样做是因为直到 WhenActivated block 被调用,_myProperty字段设置为 null .此外,我们使用 _myProperty?.Value此处的语法为 MyProperty getter 应该返回 null当 View 模型未初始化时。

选项#2

另一个绝对更好的选择是移动ToProperty WhenActivated以外的订阅阻止并标记ObservableAsPropertyHelper<T>字段为 readonly .如果您的计算属性不订阅比 View 模型还长的外部服务,那么您不需要处理返回的订阅通过 ToProperty .在 90% 的情况下你不需要保留 ToProperty内部电话 WhenActivated .查看When should I bother disposing of IDisposable objects?文档页面以获取更多信息。另见 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);

如果您实际上订阅了外部服务,例如通过构造函数注入(inject)到 View 模型中,然后你可以转换 MyProperty写入具有私有(private) 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 支持 binding to Tasks and Observables .这是一项非常有用的功能,我强烈建议您尝试一下。这意味着,在 Avalonia 中,您可以简单地将计算属性声明为 IObservable<TValue>。 Avalonia 将为您管理订阅的生命周期。所以在 View 模型中这样做:

class ViewModel : IActivatableViewModel
{
public ViewModel()
{
MyProperty =
this.WhenAnyValue(x => x.AnotherProperty)
.Select(value => $"Hello, {value}!");
}

public IObservable<TValue> MyProperty { get; }

// lines omitted for brevity
}

然后在 View 中,编写如下代码:

<TextBlock Text="{Binding MyProperty^}"/>

OAPH 是为无法实现此类技巧的平台而发明的,但 Avalonia 非常擅长巧妙的标记扩展。因此,如果您的目标是多个 UI 框架并编写与框架无关的 View 模型,那么 OAPH 是不错的选择。但是,如果您仅针对 Avalonia,则只需使用 {Binding ^} .

选项 #4

或者,如果您更喜欢使用 code-behind ReactiveUI bindings ,将选项 3 中的 View 模型代码与 xaml.cs 中 View 侧的以下代码隐藏相结合文件:

this.WhenActivated(cleanup => {
this.WhenAnyObservable(x => x.ViewModel.MyProperty)
.BindTo(this, x => x.NamedTextBox.Text)
.DisposeWith(cleanup);
});

这里我们假设 xaml文件看起来像这样:

<TextBlock x:Name="NamedTextBox" />

我们现在有一个 source generator这可能有助于生成 x:Name顺便说一句。

关于c# - 如何将 WhenActivated 与 avalonia 中的属性一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65146860/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com