1

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

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

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

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

4

5 回答 5

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";
}
于 2013-04-10T16:37:11.620 回答
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不是第一次触发事件,而是第二次使用私有整数字段来开始长时间运行的任务。原因是CompositionTarget.Rendering事件在 Silverlight UI 呈现引擎在应用程序的显示表面上绘制新帧之前触发,这意味着在第一次触发事件时,TextBlock控件的文本尚未更新。不过会第二次更新。

于 2013-04-16T07:48:22.293 回答
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;
        }
    }
}
于 2013-04-11T08:14:23.863 回答
0

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

您可以做的是在文本框上附加一个已侦听的方法,该方法仅在文本更新后才被调用(可能是 textChanged?),然后调用您的密集操作。

不过,这对我来说似乎很骇人听闻......

于 2013-04-10T20:47:23.107 回答
0

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

此外,请参阅另一个 StackOverflow 问题,了解使用 Dispatcher 的更全面的场景:了解 Silverlight Dispatcher

于 2013-04-10T20:05:03.387 回答