2013-04-10 53 views
1

在我的Silverlight應用程序中的某個位置,我需要執行大量操作,凍結UI線程大約4秒鐘。在實際執行操作之前,我試圖通過TextBlock控件顯示簡單的文本指示符。在凍結Silverlight UI線程之前顯示文本指示燈

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

的問題是,在UI線程凍結TextBlock控件的文本被更新了。如何在操作開始前顯示通知文本?

另外,當以重操作後臺線程不是我的選擇,因爲它與用戶界面對象涉及(它切換應用程序的可視根),並應在UI線程上執行。

+0

是否使用後臺線程不可能?它總是很好的在UI線程上執行長時間運行的操作。 – tsiorn 2013-04-10 16:29:46

+0

感謝您的建議,但我不能使用後臺線程。請看看編輯過的問題。 – Narek 2013-04-10 17:45:16

回答

1

我發現傑夫·普羅西斯的博客文章的幫助下,解決辦法: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事件,這意味着在第一次觸發事件時TextBlock控件的文本尚未更新。但它會第二次更新。

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

感謝您的回覆,但使用後臺線程不是我的選擇,因爲該操作正在處理UI對象,並且應該在UI線程上執行(我編輯了我的問題)。 – Narek 2013-04-10 17:43:23

+0

是的,我不知道如何去做你正在實現的目標(因爲bg工人不是一種選擇),所以我會豎起你的問題......我好奇自己如何讓它工作。如果病人幫助其他人可以使用bg工作人員,我會回答我的問題。 – tsiorn 2013-04-10 19:10:03

0

我想你應該實現BackgroundWorker的線程tsiom的答案,但使用Dispatcher.BeginInvoke對UI操作對象,這裏是關於如何使用該方法的MSDN文章:http://msdn.microsoft.com/en-us/library/cc190824%28v=vs.95%29.aspx

而且,使用Dispatcher看到另一個問題,StackOverflow的一個更全面的情景:Understanding the Silverlight Dispatcher

+0

感謝您的回覆。我已閱讀了一篇關於如何等待UI呈現完成在WCF中使用分派器的文章(http://www.jonathanantoine.com/2011/08/29/update-my-ui-now-how-to-wait-換了,渲染到完成/)。這是通過將DispatcherPriority.ContextIdle優先級分配給Dispatcher執行的操作來實現的,該操作的優先級低於UI渲染的優先級。但不幸的是Silverlight不支持DispatcherPriority,所以我不確定Dispatcher是否可以在這種情況下提供幫助。 – Narek 2013-04-12 03:52:27

0

我只是RA n自己進入這種情況。問題(我認爲)是在文本更新之前,你已經開始了密集的操作,所以你必須等待。

你可以做的就是附加聽取了一旦文本進行更新,只有被調用的文本框的一些方法(可能是框TextChanged?),然後打電話給你的集約化經營。

這似乎的hackish我雖然...

+0

感謝您的回覆。我嘗試了你建議的解決方案,但不幸的是無濟於事。我嘗試過使用所有類型的事件,例如TextBox控件的TextChanged,FrameworkElement的LayoutUpdated等。但事實證明,在執行這些事件的處理程序期間,UI尚未更新。 – Narek 2013-04-12 03:48:08

+0

儘管它正在更新,對吧?假設你不嘗試做密集的過程,對吧?我確定這是事實,但做一次健康檢查總是很好的 – TruthOf42 2013-04-15 12:38:39

+0

只有在密集流程結束後纔會更新,這不是我想要的(不知道我是否回答了您的問題)。 – Narek 2013-04-15 14:45:09

0

這是醜陋的,但它的工作原理。通過使用DispatcherTimer延遲長時間運行操作的initiliazation我們可以允許其在開始操作之前的用戶界面進行更新。

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

我也在想這樣的解決方法,但這不是一個強大的解決方案,因爲您也提到過。無論如何,謝謝你的努力。 – Narek 2013-04-12 03:41:37

+0

你是對的,它不漂亮。但是對於所有的UI編程,我總是發現自己處於這樣一種醜陋的解決方法不可避免的情況。如果你絕對必須在你的情況下顯示一個「正在工作......」的信息(你不能使用後臺線程),這是我能想到的唯一方法。 – 2013-04-12 07:20:23