我正在使用async
和await
个关键字来启用应用响应
在View
中引发属性时,我在ViewModel
中有以下代码。
查看:按钮点击活动
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
// Property Change
_viewModel.Words = await Task.Factory.StartNew(() => File.ReadAllLines("Words.txt").ToList()); // takes time to read about 3 - 4 seconds
switch (_viewModel.RadioButtonWordOrderSelection)
{
case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
await Task.Factory.StartNew(() =>
{
var words = _viewModel.Words.ToList();
words.Reverse();
// Property Change
_viewModel.Words = words;
});
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
await Task.Factory.StartNew(() =>
{
// Property Change
_viewModel.Words = _viewModel.Words.Shuffle().ToList();
});
break;
}
await Task.Factory.StartNew(() => DownloadSomething(_viewModel.Words)); // takes time to read about 30 - 40 seconds
button1.IsEnabled = true;
}
_viewModel.Progress
在View
查看:私有方法需要30 - 40秒才能完成
private void DownloadSomething(IEnumerable<string> words)
{
// Property Change
_viewModel.Progress = 0;
foreach (var word in words)
{
// Property Change
_viewModel.Word = word;
try
{
// Some code WebClient download code here
}
catch (Exception e)
{
//Trace.WriteLine(e.Message);
}
// Property Change
_viewModel.Progress++;
}
}
查看:处理了财产变更事件
void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
switch(e.PropertyName)
{
case "Progress":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Value on a ProgressBar Control.
// This will work as its using the dispatcher
// The following works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<double>(SetProgressValue),
// _viewModel.Progress);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
progress1.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
progress1.Value = _viewModel.Progress;
})
);
// This will throw an exception
// (it's on the wrong thread)
//progress1.Value = _viewModel.Progress;
break;
case "Words":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Max Value on a ProgressBar Control.
// This will work as its using the dispatcher
// The following Works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<double>(SetProgressMaxValue),
// _viewModel.Words.Count);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
progress1.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
progress1.Maximum = _viewModel.Words.Count;
})
);
// This will throw an exception
// (it's on the wrong thread)
//progress1.Maximum = _viewModel.Words.Count;
break;
case "Word":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Contant on a Label Control.
// This will work as its using the dispatcher
// The following Works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<string>(SetLastWordValue),
// _viewModel.Word);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
labelLastWord.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
labelLastWord.Content = _viewModel.Word;
})
);
// This will throw an exception
// (it's on the wrong thread)
//labelLastWord.Content = _viewModel.Word;
break;
case "RadioButtonWordOrderSelection":
break;
default:
throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
}
}
UI为progressBar1和labelLastWord完美更新!我遇到了一个问题,当progressBar1和labelLastWord正在更新时,其余的UI被冻结。
有解决方法吗?
我非常感谢任何帮助!
答案 0 :(得分:1)
我强烈建议您遵循Task-based Async Programming document中的指南。这比通过StartNew
将工作分流到线程池要好得多。如果做具有CPU绑定操作,则可以使用Task.Run
,但对于其他所有操作,请使用现有的async
- 就绪端点。
此外,我发现将整个VM视为UI上下文非常有用。所以PropertyChanged
总是在UI上下文中引发。数据绑定尤其取决于此。
private async Task<List<string>> ReadAllLinesAsync(string file)
{
var ret = new List<string>();
using (var reader = new StreamReader(file))
{
string str = await reader.ReadLineAsync();
while (str != null)
{
ret.Add(str);
str = await reader.ReadLineAsync();
}
}
return ret;
}
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
_viewModel.Words = await ReadAllLinesAsync("Words.txt");
List<string> words;
switch (_viewModel.RadioButtonWordOrderSelection)
{
case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
await Task.Run(() =>
{
words = _viewModel.Words.ToList();
words.Reverse();
});
_viewModel.Words = words;
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
await Task.Run(() =>
{
words = _viewModel.Words.Shuffle().ToList();
});
_viewModel.Words = words;
break;
}
await DownloadSomething(_viewModel.Words);
button1.IsEnabled = true;
}
private async Task DownloadSomething(IEnumerable<string> words)
{
_viewModel.Progress = 0;
foreach (var word in words)
{
_viewModel.Word = word;
try
{
await ...; // async WebClient/HttpClient code here
}
catch (Exception e)
{
//Trace.WriteLine(e.Message);
}
_viewModel.Progress++;
}
}
void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
switch(e.PropertyName)
{
case "Progress":
progress1.Value = _viewModel.Progress;
break;
case "Words":
progress1.Maximum = _viewModel.Words.Count;
break;
case "Word":
labelLastWord.Content = _viewModel.Word;
break;
case "RadioButtonWordOrderSelection":
break;
default:
throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
}
}
作为结束语,我建议您购买并仔细阅读Josh Smith's MVVM book。您正在使用“View”和“ViewModel”之类的术语,但是您使用这些组件的方式是完全,从而避免了MVVM模式的所有优势。