MVVM和异步属性

时间:2017-05-31 18:18:01

标签: c# wpf mvvm async-await

我有一个具有以下属性的ViewModel

public Employee SelectedEmployee
        {
            get { return _selectedEmployee; }
            set
            {
                if (value == _selectedEmployee) return;
                _selectedEmployee = value;

                if (_selectedEmployee != null)
                {
                    StaffHolidaysViewModel.HolidayAllowance = _staffDataService.EmployeeHolidayAllowance(_selectedEmployee.Id);
                    FireEmployeeSelectedMessage(SelectedEmployee.Id);
                }

                RaisePropertyChanged();
                RaisePropertyChanged(nameof(Allowance));
                RaisePropertyChanged(nameof(Taken));
                RaisePropertyChanged(nameof(Remaining));
                RaisePropertyChanged(nameof(TotalAbsences));
                RaisePropertyChanged(nameof(TotalSick));
                RaisePropertyChanged(nameof(TotalNonSickAbsences));
                RaisePropertyChanged(nameof(SelectedEmployeeLeavingDate));
                UpdateCanExecuteChanged();
            }
        }

该行

StaffHolidaysViewModel.HolidayAllowance = _staffDataService.EmployeeHolidayAllowance(_selectedEmployee.Id);

包含对我想要异步的方法的调用。我不知道如何做到这一点,因为SelectedEmployee是一个绑定属性。

<ComboBox Name="StaffMembers" ItemsSource="{Binding FilteredEmployees}" SelectedItem="{Binding SelectedEmployee}" Width="200" BorderThickness="1" BorderBrush="DimGray">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>

由于它是属性,我无法进行类型Task<Employee>

异步绑定MVVM属性的推荐方法是什么?或者这是不可能的?

2 个答案:

答案 0 :(得分:3)

从技术上讲,不需要让属性执行异步任务的想法是因为它们需要返回一个值。

为什么?让我们从另一个角度来看待它:

异步意味着UI线程不会停止(UI在处理时不会挂起),而主处理线程会等待操作完成并获得结果(考虑到你是否正在使用await)。

当您定义绑定时,UI直接依赖于使用等待的数据的属性,这意味着UI必须等待(这不会发生在async操作中),因此属性可以&#39;不能async,因此您无法使用await

我现在使用的所有解决方法:

  1. 有一个名为.Result的异步任务返回方法的属性。我试过了,它并没有提供最好的输出。
  2. 您可以让方法返回IAsyncOperation<>而不是Task<>。这将为您提供调用.GetResults()方法的优势,该方法将为您提供结果,并且不会冻结您的UI线程。有关它的更多信息here。如果您没有要执行的任何结果提取操作,请使用此选项。
  3. 最后,这个选项是我最常用的选项,我创建了一个返回Task<>的异步方法。我在那里执行所有操作并验证结果。获得我需要的结果后,我只需更新属性并调用RaisePropertyChanged即可。这种方式我也可以处理其他视图处理或中间加载屏幕,而不必受限于不能停止处理线程。
  4. 底线

    使用我在顶部定义的三种方法中的任何一种。他们一切都很好。以下是用例来获取游戏的得分表单服务器的用例摘要:

    1. 当您不想停止任何线程并仅定义属性的数据源时,请使用IAsyncOperation<>。代码处理不会停止或等待进程获得结果。例如:你只需获取分数并显示它。
    2. 如果希望代码等到任务完成后对结果执行其他操作,请使用Task<>返回方法。例如:您希望在计算得分后将整数值转换为百分比。
    3. 使用.Result但它对我来说并不适用。
    4. 如果还有其他任何内容可以在评论中随意提及

答案 1 :(得分:3)

  

包含对我想要异步的方法的调用。

我有一篇关于async MVVM data binding的文章。基本上,我通常做的是创建一个可绑定数据的Task - 我在文章中称之为NotifyTaskCompletion,缩写为NotifyTask in my updated library

您可以通过将StaffHolidaysViewModel.HolidayAllowance的类型从它(我将其称之为THolidayAllowance)更改为NotifyTask<T>包装器(例如NotifyTask<THolidayAllowance>来使用它})。

然后,您可以同步设置它:

StaffHolidaysViewModel.HolidayAllowance = NotifyTask.Create(
    _staffDataService.EmployeeHolidayAllowanceAsync(_selectedEmployee.Id));

您在此处所做的是开始 EmployeeHolidayAllowanceAsync,然后使用Task将其NotifyTask包裹起来。这是同步完成的,因此可以在属性设置器中执行此操作。

然后,您的数据绑定也需要更新。 NotifyTask<T> has several properties that you can use。最明显的是Result,它最终会保存EmployeeHolidayAllowanceAsync的结果(它会返回默认值,直到EmployeeHolidayAllowanceAsync完成)。还有IsNotCompletedIsFaulted,您希望用它来向用户表明数据尚未到达或存在某些错误。< / p>