【问题标题】:Multiple asynchronous UI updates in SilverlightSilverlight 中的多个异步 UI 更新
【发布时间】:2012-02-17 07:09:42
【问题描述】:

如何通过 Silverlight 回调执行多个 UI 更新?

例如,我希望用户单击一个按钮,让 UI 进行更改,做一些工作,然后进行另一项更改...相反,用户单击按钮,回调似乎在后台执行然后所有的 UI 变化都在我眼前一闪而过。

MainPage.xaml:

<Grid x:Name="LayoutRoot" Background="White" >
<TextBlock Height="23" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top"/>
<Button Click="button1_Click" Content="Button" Height="23" Name="button1" Width="75"  />
</Grid>

MainPage.xaml.cs:

private void button1_Click(object sender, RoutedEventArgs e)
{
    textBlock1.Text = "1";
    // also tried Thread.Sleep(5000);
    Dispatcher.BeginInvoke(() => Thread.Sleep(5000));
    textBlock1.Text = "2";
}

【问题讨论】:

  • 您可以在工作完成后触发一个事件,而不是在工作完成期间让线程休眠。然后,该事件的侦听器将进行第二次 UI 更改。
  • @iimpact 睡眠电话只是填补工作。问题是,为什么第一次 UI 更新不发生在 Sleep 调用之前?

标签: c# silverlight callback


【解决方案1】:

BeginInvoke 异步运行,因此将返回并更改文本。

直接来自 MSDN 文档:“BeginInvoke 是异步的;因此,控制在调用后立即返回到调用对象。”

而是在 BeginInvoke 中添加您想要执行的处理。所以你做 Thread.Sleep 然后添加更改文本框。

BackgroundWorker 也可以绑定到事件中。因此,当 UI 更新(即 BackGroundWorker 完成事件)时,您可以启动下一个项目。这就是为什么你应该使用BgroundWorker

BackgroundWorker w = new BackgroundWorker();
w.DoWork += (sender, args) => 
{
    Thread.Sleep(5000);
    Dispatcher.BeginInvoke(() => {
    //your code here
    }); 
};
w.RunWorkerAsync();

【讨论】:

  • 看来Silverlight Dispatcher 不包含Invoke 方法,所以只能异步使用Dispatcher。即便如此,无论如何,我仍然不明白为什么第一个更改不会立即可见。
  • @jordanpg 是的,抱歉,但是,如果 Invoke 仍然存在,我将其编辑为更好的答案。
  • 这看起来像我已经得到的......更神秘的是,如果我尝试: private void button1_Click(object sender, RoutedEventArgs e) { textBlock1.Text = "1"; Dispatcher.BeginInvoke(() => { Thread.Sleep(5000); textBlock1.Text = "2"; }); textBlock1.Text = "3"; } 最后一次 UI 更新甚至没有发生!
  • 这是因为 text 变成了 1。BeginInvoke 被调用并返回。它将文本设置为 3。大约 5 秒后,文本变为 2。屏幕更新速度不够快,因此您看不到。一种解决方案是使用事件或 Change BeginInvokes。我认为你并不完全理解这种异步风格的编程。
  • 重点是,即使删除了 BeginInvoke 调用,文本也不会更改为 1。所以即使我这样做:{ tb1.Text="1";线程.睡眠(1000); tb1.text="3"; },我仍然只看到一个 3。
【解决方案2】:

Silverlight 使用工作项队列来处理 UI 线程上的渲染和逻辑。由于您的逻辑也在 UI 线程上运行(在您的 button_Click 处理程序中),因此在您的方法执行完毕并且内部消息循环开始绘制屏幕之前,渲染器没有机会绘制屏幕。

您的BeginInvoke 立即将您的函数放在 Silverlight 工作队列中以在 UI 线程上执行(并立即返回)。 Silverlight 处理这个的顺序可能是这样的:

  • 用户点击按钮,在工作队列中放置一个点击事件
  • Silverlight 看到点击事件并调用 button_Click(在工作队列中放置一个新操作)
  • 处理下一个工作队列项,这可能是您的 Thread.Sleep 操作,暂停 UI 线程 5 秒
  • Silverlight 终于开始绘制屏幕了

你要做的是启动一个新线程来完成你的工作,然后BeginInvoke返回到 UI 线程:

var thread = new Thread(() => {
  Thread.Sleep(5000);
  Dispatcher.BeginInvoke(() => { 
    // update UI 
    textBox.Text = "2";
  });
});

textBox.Text = "1";
thread.Start();

我应该补充一点,Silverlight 5 添加了一个合成线程,它可以执行某些操作来更新 UI,而不会阻塞 UI 线程,例如播放动画和在可能的情况下进行 GPU 加速渲染。

【讨论】:

  • 谢谢。为什么我不能用类似的东西强制更新:{ tb1.Text="1";更新布局();线程.睡眠(1000); tb2.Text="2"; } ?
  • UpdateLayout 只执行布局过程的measure and arrange 步骤。绘图仍然像其他所有事情一样通过工作队列安排。据我所知,Silverlight 中没有即时模式屏幕渲染。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 1970-01-01
  • 2014-02-16
  • 2015-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多