如何从属性设置器调用异步函数?

编程入门 行业动态 更新时间:2024-10-23 11:21:17
本文介绍了如何从属性设置器调用异步函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我有一个wpf应用程序,其中的 TextBox 绑定到了VM中的 ActualPageNumber 属性.我还有绑定到显示给定页面的 ObservableCollection 的 DataGrid .数据存储在DB中.当我更改 ActualPageNumber 时,设置器访问数据库的速度可能会很慢.这就是为什么我想要一个异步setter来保持gui响应的原因.

我了解没有异步设置器: blog.stephencleary/2013/01/async-oop-3-properties.html

我还发现了诸如 stackoverflow/a/9343733/5852947 , stackoverflow/a/13735418/5852947 ,"> nmilcoff/2017/07/10/stop-toggling-isbusy-with-notifytask/

我仍然为如何处理此案而苦苦挣扎.AsyncEx库可以是解决方案,一个例子就很好.

我只想通知用户该页面实际上正在加载.如果可以从设置器中调用异步,则可以,但是我仍然不能在设置器中使用 await ,因为它不是 async .

解决方案

1)对于 DataGrid 的响应能力,此绑定属性可能会有所帮助: IsAsync= True

< DataGrid ItemsSource =" {Binding MyCollection,IsAsync = True}

还要查看以下DataGrid属性:

VirtualizingPanel.IsVirtualizingVirtualizingPanel.VirtualizationMode(您可能需要回收)VirtualizingPanel.IsVirtualizingWhenGroupingEnableRowVirtualizationEnableColumnVirtualization

但是要小心,虚拟化会给您带来麻烦.例如,我有一个RowHeader(带有行号),并且启用虚拟化后,这些值变得混乱了.

2)关于用于数据绑定的异步设置器:我正在使用 IAsyncCommand 的自定义版本(请参阅斯蒂芬·克雷里(Stephen Cleary)的例子).

我以两种方式使用了该命令: a)从视图绑定到它(完全避免使用异步设置程序)或 b)从设置程序启动它(不是很好).

示例:我创建了一个 UpdateCommand 作为 AsyncCommand ,并异步放置了我需要做的所有事情(例如从数据库中获取值).此命令中的所有内容都包裹在进行中"类控件的显示+隐藏中-在我的情况下,是带有微调框的透明盖+请稍候...",以防止其他用户动作(在执行任务时可见屏幕").精简样本:

....公共MainWindowViewModel(){UpdateCommand = AsyncCommand.Create(Update);//我们自己的AsyncCommand自定义实现}....公共AsyncCommand UpdateCommand {get;}内部异步任务更新(对象arg){等待SafeWrapWithWaitingScreenAsync(async()=>{var value =(int)arg;//或ActualPageNumber(如果从a1中使用)var data = await GetDataFromDb(value).ConfigureAwait(false);...//使用数据填写MyCollection(这是DataGrid的ItemsSource)OnPropertyChanged(nameof(MyCollection));//如果仍然需要}).ConfigureAwait(false);}....公共异步任务SafeWrapWithWaitingScreenAsync(Func< Task>操作){DisplayWaitingScreen = true;//等待屏幕"的可见性绑定到这个尝试{等待action().ConfigureAwait(false);}抓住(前例外){HandleException(ex);//显示/登录ex}最后{DisplayWaitingScreen = false;}}

a)从视图绑定到命令,并且

命令正文中的

a1) 使用 ActualPageNumber 属性而不是 arg 值

或 a2) 传递一个 CommandParameter ,该参数绑定到与 TextBox.Text 相同的属性.示例(可能会丢失一些东西,因为不是真正的代码):

< TextBox Text =" {Binding ActualPageNumber,Mode = TwoWay,UpdateSourceTrigger = PropertyChanged}>< TextBox.InputBindings>< KeyBinding Key =" Return"Command ="{Binding UpdateCommand}"CommandParameter =" {Binding ActualPageNumber}"/>< KeyBinding Key =" Enter"Command ="{Binding UpdateCommand}"CommandParameter =" {Binding ActualPageNumber}"/></TextBox.InputBindings></TextBox>

b)不确定是否正确,但是在看到斯蒂芬的方法与 NotifyTaskCompletion< TResult> (我可能会在未来),对于二传手,我启动了类似以下命令:

private int actualPageNumber;public int ActualPageNumber{得到=>ActualPageNumber;放{ActualPageNumber =值;OnPropertyChanged();//同步方式UpdateCommand.Execute(值);}}

I have a wpf application with a TextBox bound to ActualPageNumber property in the VM. I also have a DataGrid bound to an ObservableCollection which displays the given page. The data are stored in DB. When I change the ActualPageNumber, the setter accesses the db which can be slow. That is why I wanted an async setter, to keep the gui responsive.

I understand there is no async setter: blog.stephencleary/2013/01/async-oop-3-properties.html

I also found useful stuff like stackoverflow/a/9343733/5852947, stackoverflow/a/13735418/5852947, nmilcoff/2017/07/10/stop-toggling-isbusy-with-notifytask/

Still I struggle how to go on this case. AsyncEx library can be the solution, an example would be nice.

I just would like to notify the user that the page is actually loading. If I could call async from the setter I could do it, but then I still can not use await in the setter because it is not async.

解决方案

1) For the responsiveness of the DataGrid, this binding property might help: IsAsync=True

<DataGrid ItemsSource="{Binding MyCollection, IsAsync=True}"

also look into these DataGrid properties:

VirtualizingPanel.IsVirtualizing VirtualizingPanel.VirtualizationMode (you'll probably need Recycling) VirtualizingPanel.IsVirtualizingWhenGrouping EnableRowVirtualization EnableColumnVirtualization

But be careful, virtualization can play tricks on you. For example, I had a RowHeader (with the row number) and the values got scrambled when virtualization was on.

2) About the async setter for data binding: I was using a custom version of IAsyncCommand (see Stephen Cleary's example).

I used the command in 2 ways: a) binding to it from the view (avoiding the async setter altogether) or b) launching it from the setter (not nice).

Example: I created an UpdateCommand as an AsyncCommand and placed everything I needed done asynchronously (like getting the values from the DB). Everything in this command is wrapped within a display+hide of a "in progress"-like control - in my case, a transparent cover with a spinner + "please wait...", to prevent other user actions (the "screen" is visible while the task is performed). Stripped down sample:

.... public MainWindowViewModel() { UpdateCommand = AsyncCommand.Create(Update); // our own custom implementation of AsyncCommand } .... public AsyncCommand UpdateCommand { get; } internal async Task Update(object arg) { await SafeWrapWithWaitingScreenAsync(async () => { var value = (int)arg; // or the ActualPageNumber, if used from a1) var data = await GetDataFromDb(value).ConfigureAwait(false); ...// fill in MyCollection (which is the DataGrid's ItemsSource) using the data OnPropertyChanged(nameof(MyCollection));// if still needed }).ConfigureAwait(false); } .... public async Task SafeWrapWithWaitingScreenAsync(Func<Task> action) { DisplayWaitingScreen = true; //Visibility of the "Waiting screen" binds to this try { await action().ConfigureAwait(false); } catch (Exception ex) { HandleException(ex); // display/log ex } finally { DisplayWaitingScreen = false; } }

a) Binding to the command from the view and

a1) in the command's body use ActualPageNumber property instead of the arg value

or a2) passing a CommandParameter which binds to the same property as TextBox.Text does. Example (could be missing something, couse is not the real code):

<TextBox Text="{Binding ActualPageNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <TextBox.InputBindings> <KeyBinding Key="Return" Command="{Binding UpdateCommand}" CommandParameter="{Binding ActualPageNumber}" /> <KeyBinding Key="Enter" Command="{Binding UpdateCommand}" CommandParameter="{Binding ActualPageNumber}" /> </TextBox.InputBindings> </TextBox>

b) Not sure this is right, but before seeing Stephen's approach with NotifyTaskCompletion<TResult> (which I will probably use in the future), for the setter, I launched the command something like:

private int actualPageNumber; public int ActualPageNumber { get => actualPageNumber; set { actualPageNumber = value; OnPropertyChanged(); //the sync way UpdateCommand.Execute(value); } }

更多推荐

如何从属性设置器调用异步函数?

本文发布于:2023-08-02 11:44:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1277530.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:从属性   函数

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!