首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在avalonia中使用属性的WhenActivated

如何在avalonia中使用属性的WhenActivated
EN

Stack Overflow用户
提问于 2020-12-04 16:05:52
回答 1查看 1.2K关注 0票数 3

我试图和Avalonia一起使用ReactiveUI。由于Avalonia 0.10预览中的初始化顺序,以下代码失败:

代码语言:javascript
复制
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合作?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-04 18:42:06

选项#1

允许您解决问题的最明显的模式是使用空合并运算符。通过使用这个操作符,您可以通过调整代码来达到预期的行为,如下所示:

代码语言:javascript
复制
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%的情况下,编写这样的代码是一个很好的方法:

代码语言:javascript
复制
private readonly ObservableAsPropertyHelper<TValue> _myProperty;
public TValue MyProperty => _myProperty.Value;

// In the view model constructor:
_myProperty = obs.ToProperty(this, x => x.MyProperty);

如果您实际上是订阅外部服务(例如通过构造函数注入视图模型),则可以将MyProperty转换为具有私有设置器的读-写属性,并编写以下代码:

代码语言:javascript
复制
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将为您管理订阅的生命周期。所以在视图模型中这样做:

代码语言:javascript
复制
class ViewModel : IActivatableViewModel
{
    public ViewModel()
    {
        MyProperty =
          this.WhenAnyValue(x => x.AnotherProperty)
              .Select(value => $"Hello, {value}!");
    }

    public IObservable<TValue> MyProperty { get; }
    
    // lines omitted for brevity
}

在视图中,编写以下代码:

代码语言:javascript
复制
<TextBlock Text="{Binding MyProperty^}"/>

OAPHs是为那些不会做这种把戏的平台而发明的,但是Avalonia非常擅长聪明的标记扩展。因此,如果您是针对多个UI框架和编写与框架无关的视图模型,那么OAPHs是最好的选择。但是,如果您只针对Avalonia,那么只需使用{Binding ^}

选项#4

或者,如果您更喜欢使用代码背后的ReactiveUI绑定,则将选项3中的视图模型代码与xaml.cs文件中视图侧的以下代码结合起来:

代码语言:javascript
复制
this.WhenActivated(cleanup => {
    this.WhenAnyObservable(x => x.ViewModel.MyProperty)
        .BindTo(this, x => x.NamedTextBox.Text)
        .DisposeWith(cleanup);
});

这里我们假设xaml文件如下所示:

代码语言:javascript
复制
<TextBlock x:Name="NamedTextBox" />

我们现在有了一个源发生器,它可以帮助生成x:Name引用。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65146860

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档