在冻结Silverlight UI线程之前显示文本指示符

时间:2013-04-10 16:09:51

标签: silverlight ui-thread

在我的Silverlight应用程序的某些时候,我需要执行一个繁重的操作,冻结UI线程大约4秒钟。在实际执行操作之前,我试图通过TextBlock控件显示简单的文本指示符。

StatusTextBlock.Text = "Performing Some Operation...";
System.Threading.Thread.Sleep(4000); // Just as an example

问题是UI线程在TextBlock控件的文本更新之前冻结。如何在操作开始前显示通知文本?

此外,对于我来说,将繁重的操作转移到后台线程不是一个选项,因为它处理UI对象(它切换应用程序的可视根)并且应该在UI线程上执行。

5 个答案:

答案 0 :(得分:1)

我的建议是将其从UI线程中删除并使用后台线程......

StatusTextBox.Text = "Before Sleep";
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    System.Threading.Thread.Sleep(8000);}


void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    StatusTextBox.Text = "after Sleep";
}

答案 1 :(得分:1)

我在Jeff Prosise的博客文章http://www.wintellect.com/cs/blogs/jprosise/archive/2008/10/25/cool-silverlight-trick-5.aspx

的帮助下找到了解决方案

这个想法是延迟调用执行长时间运行的任务,直到Silverlight UI呈现事件触发。为此,我使用了CompositionTarget.Rendering事件。我在用户控件的构造函数中订阅了它:

CompositionTarget.Rendering += this.CompositionTargetRendering;

更新TextBlock控件的文本后,我设置了一个私有标志,表示应该在事件处理程序中进行一些处理:

StatusTextBlock.Text = "Performing Some Operation...";
this.processRenderingEvent = true;

这是处理程序的代码:

private void CompositionTargetRendering(Object sender, EventArgs e)
{
    if (this.processRenderingEvent)
    {
        if (++this.renderingEventCounter == 2)
        {
            System.Threading.Thread.Sleep(4000); // Example of long running task
            this.processRenderingEvent = false;
        }
    }
}

这里要提到的重点是我使用私有整数字段renderingEventCounter来开始长时间运行的任务,而不是第一次触发事件,而第二次。这样做的原因是,在Silverlight UI呈现引擎在应用程序的显示表面上绘制新帧之前触发了CompositionTarget.Rendering事件,这意味着在事件第一次触发{{1}的文本时}控件尚未更新。但它会第二次更新。

答案 2 :(得分:0)

我认为你应该实现BackgroundWorker线程是tsiom的答案,但是使用Dispatcher.BeginInvoke来操作UI对象,这里有一篇关于如何使用该方法的MSDN文章:http://msdn.microsoft.com/en-us/library/cc190824%28v=vs.95%29.aspx

另外,请参阅另一个StackOverflow问题,了解使用Dispatcher的更全面的方案:Understanding the Silverlight Dispatcher

答案 3 :(得分:0)

我自己也遇到过这种情况。问题(我认为)是在文本更新之前你已经开始密集操作,所以你必须等待。

你可以做的是在文本框上附加一个只听到文本更新后调用的方法(也许是textChanged?),然后调用你的密集操作。

虽然这对我来说似乎很苛刻......

答案 4 :(得分:0)

这很难看,但确实有效。通过使用DispatcherTimer延迟长时间运行操作的初始化,我们可以允许在操作开始之前更新UI。

XAML:

<UserControl x:Class="SilverlightApplication13.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="400">

    <Grid x:Name="LayoutRoot"
          Background="White">

        <StackPanel>

            <Border x:Name="Brd01"
                    Visibility="Collapsed"
                    Background="Red">
                <TextBlock VerticalAlignment="Center"
                           Margin="30">Sleeping for 4 seconds...</TextBlock>
            </Border>

            <Border x:Name="Brd02"
                    Visibility="Collapsed"
                    Background="Lime">
                <TextBlock VerticalAlignment="Center"
                           Margin="30">Done!</TextBlock>
            </Border>

            <Button Content="Start Operation"
                    Click="Button_Click_1"></Button>
        </StackPanel>

    </Grid>
</UserControl>

代码隐藏:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace SilverlightApplication13
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            //Show the "working..." message
            Brd01.Visibility = System.Windows.Visibility.Visible;

            //Initialize a timer with a delay of 0.1 seconds
            var timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(100);
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            //Start the long running operation
            Thread.Sleep(4000);

            Brd01.Visibility = System.Windows.Visibility.Collapsed;
            Brd02.Visibility = System.Windows.Visibility.Visible;

            //Kill the timer so it will only run once. 
            (sender as DispatcherTimer).Stop();
            (sender as DispatcherTimer).Tick -= Timer_Tick;
        }
    }
}