简体   繁体   English

MVVM和异步属性

[英]MVVM and async properties

I have a ViewModel with the following property 我有一个具有以下属性的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();
            }
        }

The line 线

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

Contains a call to a method that I'd like to make asynchronous. 包含对我想进行异步处理的方法的调用。 I don't know how I can do this as SelectedEmployee is a bound property. 我不知道该怎么做,因为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>

Since it's a property, I can't make its type Task<Employee> 由于它是一个属性,因此无法使其类型为Task<Employee>

What's the recommended approach to async bound MVVM properties? 异步绑定MVVM属性的推荐方法是什么? Or is it just not possible? 还是只是不可能?

it's technically not the idea approach to have a property perform Asynchronous Tasks as they need to be returning a value. 从技术上讲,让属性执行异步任务不是想法方法,因为它们需要返回值。

Why? 为什么? let's look at it from another point of view: 让我们从另一个角度来看它:

Asynchronous means that the UI thread will not stop (UI won't hang while processing) while the main processing thread would wait for the operation to finish and get the results (considering you're wana be using an await ). 异步意味着UI线程将不会停止(UI在处理时不会挂起),而主处理线程将等待操作完成并获得结果(考虑到您正在使用await )。

When you define a binding, the UI is directly dependant on the property for data which is using await that means the UI will have to wait (which cannot happen with async operations) and hence properties can't be made async and because of which you can't use await . 当您定义绑定时,UI直接依赖于使用await的数据的属性,这意味着UI必须等待( async操作无法发生),因此无法使属性async ,因此您不能使用await

What all work arounds I have used uptil now: 我到目前为止一直使用的所有变通方法是:

  1. There is a property of an async Task returning method called as .Result . 有一个名为.Result的异步Task返回方法的属性。 I have tried it, it doesn't really provide with the best outputs. 我已经尝试过了,它实际上并没有提供最好的输出。
  2. You can make your method return an IAsyncOperation<> instead of Task<> . 您可以使方法返回IAsyncOperation<>而不是Task<> This will give you the advantage of calling the the .GetResults() method which will get you the results and will not freeze your UI thread. 这将为您提供调用.GetResults()方法的优势,该方法将为您提供结果,并且不会冻结UI线程。 More information on it here . 有关更多信息,请参见此处 Use this if you don't have any post result fetching operation to perform. 如果您没有任何后处理结果提取操作要执行,请使用此选项。
  3. Finally, this option is my most used one, I create an async method returning Task<> . 最后,此选项是我最常用的选项,我创建了一个异步方法,返回Task<> I perform all my operations there and verify results. 我在那里执行所有操作并验证结果。 Once I have the results I need, I simply update the property and call RaisePropertyChanged . 获得所需结果后,只需更新属性并调用RaisePropertyChanged This ways I can handle other view processing or intermediate loading screens as well without having to be limited with not being able to stop the processing thread. 这样,我也可以处理其他视图处理或中间加载屏幕,而不必因无法停止处理线程而受到限制。

Bottom Line 底线

Use either of the three methods I defined on top. 使用我在上面定义的三种方法之一。 they're all good. 他们都很好。 below are the summary points for use cases considering as an example getting a score form server of a game: 以下是一些用例的要点,以获取游戏的得分表服务器为例:

  1. Use IAsyncOperation<> when you don't want to stop any threads and just define the source of the data for the property. 当您不想停止任何线程而只为该属性定义数据源时,请使用IAsyncOperation<> The Code processing will not stop or wait for the process to get the results. 代码处理不会停止,也不会等待过程获取结果。 eg: you just wana fetch the score and display it. 例如:您只是想获取分数并显示出来。
  2. Use Task<> returning method if you want the code to wait until the task has been completed to perform other operations on a result. 如果您希望代码等待任务完成以对结果执行其他操作,请使用Task<>返回方法。 eg: you want to convert the integer value to a percentage once the score is calculated. 例如:计算分数后,您要将整数值转换为百分比。
  3. Use .Result but it hasn't worked for me ever. 使用.Result但它对我从未奏效。

If there is anything else feel free to mention in the comments 如果还有其他意见,请在评论中提及

Contains a call to a method that I'd like to make asynchronous. 包含对我想进行异步处理的方法的调用。

I have an article on async MVVM data binding . 我有一篇关于异步MVVM数据绑定的文章。 Essentially, what I usually do is create a data-bindable Task - what I call NotifyTaskCompletion in the article, shortened to NotifyTask in my updated library . 从本质上讲,我通常做的是建立一个数据绑定的Task -我称之为NotifyTaskCompletion文章,缩短到NotifyTask在我更新的库

You can use it by changing the type of StaffHolidaysViewModel.HolidayAllowance from whatever it is (I'll call it THolidayAllowance ) to a NotifyTask<T> wrapper (eg, NotifyTask<THolidayAllowance> ). 您可以通过将StaffHolidaysViewModel.HolidayAllowance的类型从它的类型(我将其称为THolidayAllowance )更改为NotifyTask<T>包装器(例如, NotifyTask<THolidayAllowance> )来使用它。

Then, you can set it synchronously: 然后,您可以同步设置它:

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

What you're doing here is starting EmployeeHolidayAllowanceAsync and then wrapping its Task with a NotifyTask . 您在这里要做的是启动 EmployeeHolidayAllowanceAsync ,然后用NotifyTask包装其Task This is done synchronously, so it's possible to do this in a property setter. 这是同步完成的,因此可以在属性设置器中执行此操作。

Then, your data binding also needs to be updated. 然后,您的数据绑定也需要更新。 NotifyTask<T> has several properties that you can use . NotifyTask<T>具有几个可以使用的属性 The most obvious is Result , which will eventually hold the result of EmployeeHolidayAllowanceAsync (it returns a default value until EmployeeHolidayAllowanceAsync completes). 最明显的是Result ,它将最终保存EmployeeHolidayAllowanceAsync的结果(它返回默认值,直到EmployeeHolidayAllowanceAsync完成)。 There's also IsNotCompleted and IsFaulted , which you'll want to use to indicate to the user that the data hasn't arrived yet or that there was some error. 还有IsNotCompletedIsFaulted ,您将使用它们来向用户指示数据尚未到达或存在一些错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM