【问题标题】:Async method call on ComboBox selection with MVVM使用 MVVM 对 ComboBox 选择进行异步方法调用
【发布时间】:2018-01-16 06:15:52
【问题描述】:

在没有锁定 UI 的情况下显示由ComboBox 选择过滤的图表时遇到问题。统计过滤相当繁重,需要运行async。在我尝试从 Property setter 调用 FilterStatisticsAsyncMonthSelectionChanged 之前,这一切都很好。有没有人有关于如何解决或解决这个问题的好建议?

XAML 如下所示:

        <ComboBox x:Name="cmbMonth"
                  ItemsSource="{Binding Months}" 
                  SelectedItem="{Binding SelectedMonth }"
                  IsEditable="True"
                  IsReadOnly="True"

ViewModel 属性设置器是这样的:

    public string SelectedMonth
    {
        get { return _selectedMonth; }
        set { SetProperty(ref _selectedMonth, value); LoadStatisticsAsync(); MonthSelectionChanged(); }
    }

SetProperty 派生自一个封装 INPC 的基类,如下所示:

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void SetProperty<T>(ref T member, T value, [CallerMemberName] string propertyName = null)
        {
            if (Equals(member, value))
                return;

            member = value;
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

【问题讨论】:

  • 创建一个事件处理程序并在值更改时引发它。在处理程序中,您可以使用异步而不阻塞 UI 线程
  • 只是添加到 Nkosi 的答案中,创建一个返回 void 并调用所需方法的异步事件处理程序
  • 您是否尝试过在 ItemsSource 绑定(和其他长时间运行的属性调用)中使用 IsAsync=True 并让您的 ComboBox 使用虚拟化?确保在主 UI 调度程序中引发每个 PropertyChanged,否则它可能会中断。
  • 有趣的是,您有一个 UI 阻塞问题。如果 LoadStatisticsAsync 是真正的异步,顾名思义,UI 不应该阻塞。假设 MonthSelectionChanged 和它的所有处理程序都没有做奇怪的工作人员,那么这里就不对了。尝试首先确定 UI 阻塞的真正原因。
  • 尴尬的是,小果答对了。查看代码后,我在 LoadStatisticsAsync 中找到了一个块。感谢您的宝贵意见。

标签: c# wpf asynchronous mvvm async-await


【解决方案1】:

我会这样做:

    public class AsyncProperty<T> : INotifyPropertyChanged
    {
        public async Task UpdateAsync(Task<T> updateAction)
        {
            LastException = null;
            IsUpdating = true;

            try
            {
                Value = await updateAction.ConfigureAwait(false);
            }
            catch (Exception e)
            {
                LastException = e;
                Value = default(T);
            }

            IsUpdating = false;
        }

        private T _value;

        public T Value
        {
            get { return _value; }
            set
            {
                if (Equals(value, _value)) return;
                _value = value;
                OnPropertyChanged();
            }
        }

        private bool _isUpdating;

        public bool IsUpdating
        {
            get { return _isUpdating; }
            set
            {
                if (value == _isUpdating) return;
                _isUpdating = value;
                OnPropertyChanged();
            }
        }

        private Exception _lastException;

        public Exception LastException
        {
            get { return _lastException; }
            set
            {
                if (Equals(value, _lastException)) return;
                _lastException = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

属性的定义

 public AsyncProperty<string> SelectedMonth { get; } = new AsyncProperty<string>();

代码中的其他地方:

SelectedMonth.UpdateAsync(Task.Run(() => whateveryourbackground work is));

xaml 中的绑定:

SelectedItem="{Binding SelectedMonth.Value }"

请注意,属性应反映当前状态,而不是触发可能需要无限时间的进程。因此,需要以不同于分配属性的方式更新属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多