我通过点击从数据库中获取数据。 我有一个事件处理程序,在触发时应在状态栏中显示“数据检索...”,并应在事件处理程序结束前再次更改为“就绪”。
但文本只更新一次,第二次就绪。一般情况如何?
private void Next_Click(object sender, RoutedEventArgs e){
this.footerText = "Waiting for dataRetreival";
someRandomTimeTakingMethod(); //Gets Data from DB.
this.footerText = "Ready";
}
即使代码执行第2行,视图仅在函数结束时更新,即只有第二个实际工作。
答案 0 :(得分:2)
您应该将数据密集型工作放在后台线程上,以便UI可以正确更新。这提供了最佳的用户体验。
用一些代码详细说明FZysset的答案......
private async void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
await SomeRandomTimeTakingMethodAsync();
IsEnabled = true;
footerText.Text = "Ready";
}
private async Task SomeRandomTimeTakingMethodAsync()
{
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(2, 5)));
// or await Task.Run(() => { ... });
}
上面的示例允许您利用.NET 4.5中引入的await / async。注意它流动得有多好?没有废话!
我们将内容放到后台线程中,以便UI可以保持解除阻塞状态(因此它会显示您对状态栏的更新并允许用户交互。)当然,您必须小心不要在UI上更新任何内容来自你的后台主题。
如果您使用的是旧版本的.NET,则可以在没有异步/等待的情况下使用TPL:
private void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
Task.Factory.StartNew(() =>
{
SomeRandomTimeTakingMethod();
}).ContinueWith(t =>
{
IsEnabled = true;
footerText.Text = "Ready";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void SomeRandomTimeTakingMethod()
{
Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(2, 5)));
}
关于后一个例子需要注意两个重要事项:
TaskScheduler.FromCurrentSynchronizationContext()
调用提供ContinueWith
,否则您将遇到异常,因为延续不在UI线程上。您必须在未在后台线程上运行的方法中获取上下文。Task
中ContinueWith
对象的例外情况。这个例子非常简陋。如果您要通过点击处理程序启动一系列后台操作,那么您需要给自己一些帮助类/服务以使生活更轻松。 (并调查MVVM,如果你正在使用,我无法判断。)
我的一位同事介绍了如何在C#和.NET中使用各种异步模式。您可以在此处查看:https://github.com/mtusk/TplLunchAndLearn
答案 1 :(得分:1)
那是因为你在UI线程上启动了“someRandomTimeTakingMethod”。因此,在完成之前,它不会更新视图。
要解决这个问题,您有以下几种可能:
使您的方法“someRandom ...”与任务异步,并使用await运算符:http://msdn.microsoft.com/en-us/library/hh191443.aspx
将randomTimeTaking方法启动到线程中,并在执行完成后启动事件,以更新页脚文本
对于某些示例,我强烈建议您使用第一个选项:http://msdn.microsoft.com/en-us/library/hh873191.aspx
答案 2 :(得分:0)
您需要异步运行这些行。您可以使用Task
class:
private void Next_Click(object sender, RoutedEventArgs e){
Task.Factory.StartNew(() => footerText = "Waiting for dataRetreival");
someRandomTimeTakingMethod(); //Gets Data from DB.
Task.Factory.StartNew(() => footerText = "Ready");
}
答案 3 :(得分:0)
使用Dispatcher有一种方法可以做到这一点。原帖是here。
代码是: -
private void Next_Click(object sender, RoutedEventArgs e){
UpdateUI("Please wait for data retrieval", delegate() { someRandomTimeTakingMethod(); });
this.footerText = "Ready";
}
public delegate void NoArgsDelegate();
public void UpdateUI(string description, NoArgsDelegate operation)
{
this.FooterText= description;
DispatcherFrame frame = new DispatcherFrame();
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);
dispatcherOperation.Completed += delegate(object sender, EventArgs e)
{
frame.Continue = false;
};
Dispatcher.PushFrame(frame);
}
如果我的理解是对的,那就使用异步编程,而不是不同的线程。该线程将首先更新UI,然后调用someRandomTimeTakingMethod()。