如何在双向绑定组合框(WPF)上调用异步操作

Posted

技术标签:

【中文标题】如何在双向绑定组合框(WPF)上调用异步操作【英文标题】:How to invoke async-operation on two-way bound Combobox (WPF) 【发布时间】:2018-07-03 13:45:33 【问题描述】:

当从双向绑定控件(例如组合框(wpf 数据绑定))中选择项目时,处理运行异步操作的适当方法是什么?

当我有一个双向绑定属性(例如 ComboBox 上的 SelectedValue)时,我认为我不能使用 Stephen Cleary's NotifyTaskCompletion,因为当用户从下拉列表中选择一个值时,ComboBox 本身需要修改绑定的 Result属性,即Task的结果。

我想出的唯一可行的解​​决方案是从数据绑定设置器调用异步 Task 方法而不等待结果。这应该没问题,只要异步方法为正在执行的任何与 ui 相关的事情触发属性更改事件,并且任何异常都会相应地被拾取并传播到 ui,对吧?

我认为这将是异步 WPF 应用程序中的常见情况。你们是怎么处理这个问题的?

到目前为止我的解决方案:

<ComboBox 
        ItemsSource="Binding PossibleItems"
        DisplayMemberPath="Name"
        SelectedValue="Binding SelectedItem"/>

...

public Item SelectedItem

    get  return m_selectedItem; 
    set
    
        m_selectedItem = value;
        OnPropertyChanged();

        InitializeAsyncAndFirePropertyChanged();   // async Task method not awaited - gives compiler warning CS4014
    


public async Task InitializeAsyncAndFirePropertyChanged(ObservableCollection<RFEnvironment> possibleRfEnvironments)

    //should check this method for exceptions and propagate them to the UI via databinding
    OtherDataBoundProperty = await GetSomeStringFromWebAsync(); 


public string OtherDataBoundProperty

    get  return m_otherDataBoundProperty; 
    set
    
        m_otherDataBoundProperty = value;
        OnPropertyChanged();
    

注意:我发现有人问过类似的问题,但没有一个涉及到控件(例如 Combobox)上的双向绑定。

【问题讨论】:

我遇到了类似的问题,但是不等待就运行异步是不够的。例外情况如何?但还没有找到更好的解决方案。 【参考方案1】:

在使用 WCF 数据绑定时,我在属性设置器中调用 async 时遇到了类似的问题。 我的解决方案稍微好一点,因为在您的情况下,当InitializeAsyncAndFirePropertyChanged 发生异常时,不会抛出或捕获异常。修改后的代码如下。它使用任务延续来引发异常并引发OnPropertyChangedOnPropertyChanged电话可以留在原地,这取决于你的需要。

public class MyViewModel: INotifyPropertyChanged


    private readonly TaskScheduler _uiScheduler;

    public MyViewModel()
    
        _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    

    public Item SelectedItem
    
        get  return m_selectedItem; 
        set
        
            m_selectedItem = value;
            InitializeAsyncAndFirePropertyChanged()
                .ContinueWith(t =>
                
                    if (t.Exception != null)
                    
                        throw t.Exception;
                    

                    OnPropertyChanged();
                , _uiScheduler);
        
    

    public async Task InitializeAsyncAndFirePropertyChanged(ObservableCollection<RFEnvironment> possibleRfEnvironments)
    
        //should check this method for exceptions and propagate them to the UI via databinding
        OtherDataBoundProperty = await GetSomeStringFromWebAsync();
    

    public string OtherDataBoundProperty
    
        get  return m_otherDataBoundProperty; 
        set
        
            m_otherDataBoundProperty = value;
            OnPropertyChanged();
        
    

    .... other code to support INotifyPropertyChanged

【讨论】:

【参考方案2】:

如果您使用的是功能性反应式 MVVM 框架,例如 ReactiveUI,您只需观察 SelectedItem 属性并在设置该属性时启动您想要的任何操作。例如:

this.WhenAnyValue(x => x.SelectedItem)
    .Subscribe(async _ => await InitializeAsyncAndFirePropertyChanged());

属性本身应该启动后台操作,但视图模型可能会在设置属性时执行此操作。

更多信息请参考文档:https://reactiveui.net/docs/concepts/。

【讨论】:

这看起来很有趣,我可能会研究一下响应式UI,但现在我正在寻找一种使用普通 MVVM 和 WPF 解决这个问题的方法。 究竟解决什么问题?属性不能是异步的。 当用户从组合框下拉菜单中选择一个值时,如何最好地启动异步操作。我越看它,我认为没有神奇的美丽解决方案。我只需要确保 setter 立即返回并以某种方式触发异步方法。线索是确保异步方法在完成时调用适当的 propertyChange-calls,并处理可能由自身触发的任何错误,触发 propertyChange-calls 绑定到 UI 错误指示器的错误属性。 我已经回答过了;查看函数式编程并在视图模型中进行设置,而不是在属性本身中进行设置。另一种选择是简单地触发属性设置器中的异步操作,然后要么忘记结果,要么连接到异步方法完成后引发的某个事件。你是对的,这无论如何都不漂亮 :) ReactiveUI 和类似的库是有原因的。

以上是关于如何在双向绑定组合框(WPF)上调用异步操作的主要内容,如果未能解决你的问题,请参考以下文章

extjs 组合框 afterrender() 一起调用导致异步 ajax 调用

element穿梭框点击后异步

WPF ObservableCollection 异步调用问题

wpf中mvvm的Command绑定后,如何在点击按钮的时候在viewmodel里面异步执行方法。

WPF 数据绑定:如何使用 XAML 将枚举数据绑定到组合框? [复制]

TabControl WPF 问题中与 SelectedItem 的异步绑定