![](/img/trans.png)
[英]WPF DataGrid with IsAsync data binding don't preserve values when scrolling and sorting
[英]Binding to Task<T> property and seting IsAsync to true
我試圖找出一種將視圖綁定到需要使用async
方法填充的viewmodel屬性的好方法。 我最新得到的就是像這樣使用IsAsync
。
ViewModel.cs
public class ViewModel
{
public Task<string> Name { get; set; }
public ViewModel()
{
Name = GetNameAsync();
}
public async Task<string> GetNameAsync()
{
return await Task.Run(async () =>
{
await Task.Delay(5000);
return "Foo";
});
}
}
View.xaml
<Label Content="{Binding Name.Result, IsAsync=true}" />
它似乎可以工作,但是我不確定是否正在使用IsAsync
,因為應該使用它。 該文檔沒有說明與Task
相關的任何內容,並且在stackoverflow上也有此注釋 ,指出IsAsync
與C# async
不相關。
這樣的實現會出什么問題?
處理這種“一次性”異步屬性的最干凈方法是使用Stephen Cleary的Nito.Asyncex庫(在Nuget中提供)中的NotifyTaskCompletetion。
您的ViewModel會變成
public class ViewModel
{
public INotifyTaskCompletion<string> Name { get; set; }
public ViewModel()
{
Name = NotifyTaskCompletetion.Complete(GetNameAsync());
}
private async Task<string> GetNameAsync()
{
return await Task.Run(async () =>
{
await Task.Delay(5000);
return "Foo";
});
}
}
然后,您將綁定到Name.Result
作為標准綁定,不需要IsAsync。
更多細節在這里 。
更正IsAsync選項與Task無關,它只是告訴UI顯示回退值,而不會在get調用運行時阻塞。
您的代碼可能會出問題。 嗯,我想如果有任何實際錯誤,我必須進行測試以解決問題。 您有很多“壞”的事情。
構造函數中的代碼。 您可以保留任務引用,因此,它並非一無是處,但通常,您不應在構造函數中使用像這樣的代碼
在任務上調用.Result。 這可能導致死鎖
具有異步任務,但不等待它。
使用Task.Run在任務內部? 您可以直接調用await Task.Delay
我將如下重構您的代碼
視圖模型
public class ViewModel : INotifyPropertyChanged
{
private string name
public string Name {
get {return name;}
set {
name = value;
PropertyChanged("Name");
}
public ICommand GetNameAsync;
private void PropertyChanged(string prop)
{
if( PropertyChanged != null )
{
PropertyChanged(this, new PropertyChangedEventArgs(prop);
}
}
public ViewModel()
{
Name="Loading...";
GetNameAsync = new AsyncCommand(async () => {Name = await GetNameAsync()});
}
private async Task<string> GetNameAsync()
{
await nameService.getName();
}
}
XAML
<Window xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding GetNameAsync}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Label Content="{Binding Name}" />
</Window>
現在,您的Xaml將在窗口加載時調用GetName函數,標簽將顯示“ Loading ...”,直到GetName函數完成,然后將其更新為Name。
Unfortuately
WPF沒有AsyncCommand。
您必須自己制作,或使用第三方框架中的一個(請參見下文)
INotifyPropertyChanged令人討厭輸入。
我建議使用Fody及其INotifyPropertyChanged插件https://github.com/Fody
AsyncCommand
https://mike-ward.net/2013/08/09/asynccommand-implementation-in-wpf/
using System;
using System.Threading.Tasks;
using System.Windows.Input;
namespace OpenWeather.Command
{
internal class AsyncCommand : ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
private bool _isExecuting;
public AsyncCommand(Func<Task> execute) : this(execute, () => true)
{
}
public AsyncCommand(Func<Task> execute, Func<bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return !(_isExecuting && _canExecute());
}
public event EventHandler CanExecuteChanged;
public async void Execute(object parameter)
{
_isExecuting = true;
OnCanExecuteChanged();
try
{
await _execute();
}
finally
{
_isExecuting = false;
OnCanExecuteChanged();
}
}
protected virtual void OnCanExecuteChanged()
{
if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.