Showing a text indicator before freezing the Silverlight UI thread

≡放荡痞女 提交于 2020-02-06 04:08:12

问题


At some point in my Silverlight application I need to perform a heavy operation which freezes the UI thread for about 4 seconds. Before actually performing the operation I am trying to display a simple text indicator via a TextBlock control.

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

The problem is that the UI thread freezes before the text of the TextBlock control gets updated. How can I get the notification text shown before the operation begins?

Also, taking the heavy operation to a background thread is not an option for me, as it deals with UI objects (it switches the visual root of the application) and should be executed on the UI thread.


回答1:


My suggestion is to take it off UI thread and use background thread...

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";
}



回答2:


I found a solution with the help of Jeff Prosise's blog post: http://www.wintellect.com/cs/blogs/jprosise/archive/2008/10/25/cool-silverlight-trick-5.aspx

The idea is to delay the call performing a long running task till a Silverlight UI rendering event fires. For this I used the CompositionTarget.Rendering event. I subscribed to it in the constructor of the user control:

CompositionTarget.Rendering += this.CompositionTargetRendering;

After I update the text of a TextBlock control I set a private flag, which indicates that some processing should be made in the event handler:

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

And here is the code of the handler:

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;
        }
    }
}

An important point to mention here is that I use a private integer field renderingEventCounter to begin the long running task not the first time the event fires, but the second. The reason for this is that the CompositionTarget.Rendering event is fired just before the Silverlight UI rendering engine draws a new frame on the application's display surface, which means that at the first time the event fires the text of the TextBlock control is not yet updated. But it will be updated the second time.




回答3:


I think you should implement the BackgroundWorker thread is tsiom's answer, but use the Dispatcher.BeginInvoke to operate on the UI objects, here is a MSDN article on how to use the method: http://msdn.microsoft.com/en-us/library/cc190824%28v=vs.95%29.aspx

Also, see another StackOverflow question for a more comprehensive scenario using the Dispatcher: Understanding the Silverlight Dispatcher




回答4:


I just ran into this situation myself. The problem (I think) is that before the text gets updated you have already begun the intensive operation, so you have to wait.

What you can do is to attach a listened to some method on the textbox that only gets called once the text is updated (textChanged perhaps?) and THEN call your intensive operation.

This seems hackish to me though...




回答5:


This is ugly but it works. By delaying the initiliazation of the long running operation using a DispatcherTimer we can allow the UI to be updated before the operation is started.

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>

Code-behind:

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;
        }
    }
}


来源:https://stackoverflow.com/questions/15931032/showing-a-text-indicator-before-freezing-the-silverlight-ui-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!