【问题标题】:MVVM and async propertiesMVVM 和异步属性
【发布时间】:2017-11-01 16:30:17
【问题描述】:

我有一个具有以下属性的 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&lt;Employee&gt;

异步绑定 MVVM 属性的推荐方法是什么?还是根本不可能?

【问题讨论】:

  • 我的理解是这是不可能的。绑定机制要求属性在那个时刻直接返回一个值。 一些可用的模式可以间接处理它。请参阅 Stephen Cleary 的 MSDN 帖子:msdn.microsoft.com/en-us/magazine/…

标签: c# wpf mvvm async-await


【解决方案1】:

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

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

您可以通过将StaffHolidaysViewModel.HolidayAllowance 的类型从它的任何类型(我将其称为THolidayAllowance)更改为NotifyTask&lt;T&gt; 包装器(例如NotifyTask&lt;THolidayAllowance&gt;)来使用它。

那么,就可以同步设置了:

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

您在这里所做的是开始 EmployeeHolidayAllowanceAsync,然后用NotifyTask 包装它的Task。这是同步完成的,因此可以在属性设置器中执行此操作。

然后,您的数据绑定也需要更新。 NotifyTask&lt;T&gt; has several properties that you can use。最明显的是Result,它最终将保存EmployeeHolidayAllowanceAsync 的结果(它返回一个默认值,直到EmployeeHolidayAllowanceAsync 完成)。还有IsNotCompletedIsFaulted,您可以使用它们向用户指示数据尚未到达或出现错误。

【讨论】:

    【解决方案2】:

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

    为什么?让我们换个角度来看:

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

    当您定义绑定时,UI 直接依赖于使用 await 的数据属性,这意味着 UI 必须等待(async 操作不会发生这种情况),因此无法创建属性 @ 987654324@,因此您不能使用await

    到目前为止我使用的所有解决方法:

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

    底线

    使用我在上面定义的三种方法中的任何一种。他们都很好。以下是使用案例的总结要点,以获取游戏的分数表格服务器为例:

    1. 当您不想停止任何线程并只定义属性的数据源时,请使用IAsyncOperation&lt;&gt;。代码处理不会停止或等待进程获得结果。例如:您只是想获取分数并显示它。
    2. 如果您希望代码等到任务完成后再对结果执行其他操作,请使用Task&lt;&gt; 返回方法。例如:您希望在计算分数后将整数值转换为百分比。
    3. 使用.Result,但它从来没有为我工作过。

    如果还有什么可以在 cmets 中提及

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-17
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      • 2015-08-24
      • 2019-04-21
      • 2013-11-02
      • 1970-01-01
      相关资源
      最近更新 更多