Silverlight及WPF中實現自定義BusyIndicator

雲霏霏發表於2015-02-26

  在開發Silverlight或者WPF專案時,當我們呼叫Web服務來載入一些資料時,由於資料量比較大需要較長的時間,需要使用者等待,為了給使用者友好的提示和避免使用者在載入資料過程中進行重複操作,我們通常使用BusyIndicator這個控制元件來鎖定當前頁面。然而,有時候BusyIndicator這個控制元件的風格和我們的介面風格並不搭配,而且修改起來也比較麻煩,今天我們就來自己寫一個BusyIndicator控制元件,實現自定義的忙碌提示。

後面會提供原始碼下載。

 

 一、實現基本原理及最終效果

   我們先來看下面這段程式碼,如圖:

我們新增了三個矩形在Grid中,我們設定了矩形的寬度和高度,那麼矩形會怎麼顯示呢?從上到下順序顯示嗎?NO!不是這樣的,我們來看看顯示結果:

三個矩形層疊在了一起,按照程式碼的順序,依次從下往上顯示,程式碼中放在最後的矩形顯示在最頂層。這是Grid的一個特性,當然在Canvas中也可以層疊顯示,不過不是居中顯示,是右上腳對齊顯示的。如果是StackPanel則是從上到下一次顯示。

根據這個特性,我們知道在Grid中後新增的UI元素會顯示在其他元素的最頂層,所以我們可以在執行時通過程式碼來動態的向Grid中新增元素,並且這個元素處於最頂層,從而可以遮擋其他頁面元素。

下面我們來看看最終的實現效果:

是不是有一種中國風的味道啊!下面我們來詳細說明實現方法。

 

 二、自定義BusyIndicator的具體實現

   這裡我是用Silverlight來演示,用WPF也是一樣的。首先新建專案,新增一個Silverlight user Control,這裡我起的名字就叫Load,頁面XAML程式碼如下:

<UserControl x:Class="SilverlightBusy.Load"
    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"
    xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
    mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
   <UserControl.Resources>
      <Storyboard x:Name="fadeStoryboard">
         <DoubleAnimation x:Name="fadeAnimation" BeginTime="00:00:00" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Opacity" From="0.9" To="0" Duration="0:0:01">
         </DoubleAnimation>
      </Storyboard>
   </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot" Background="#cccccc" Opacity="0.9">
      <Grid.Resources>
         <Storyboard x:Name="fadeImgStoryboard">
            <DoubleAnimation x:Name="fadeImgAnimation" BeginTime="00:00:00" Storyboard.TargetName="LoadImg" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:01">
            </DoubleAnimation>
         </Storyboard>
      </Grid.Resources>
      <Image x:Name="LoadImg" Source="/SilverlightBusy;component/Images/Loading.png" Width="128" Height="128">
      </Image>
      <TextBlock x:Name="txtLoading" Width="60" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Loading..."/>
   </Grid>
</UserControl>

其實這個頁面很簡單,就是一個Grid、一個Image和一個TextBlock,新增兩個Storyboard是為了有個過渡效果,這樣看起來更加平滑一下,直接隱藏掉Image和TextBlock的話,會讓人感覺閃一下。其設計檢視如圖:

其後臺程式碼如下:

public partial class Load : UserControl
   {
      private int _counter = 0;
      private DispatcherTimer timer = new DispatcherTimer();
      private RotateTransform rt = new RotateTransform();
      private bool _isBusy = false;
      public bool IsBusy
      {
         get
         {
            return _isBusy;
         }
         set
         {
            _isBusy = value;
            if (value)
            {
               this.LayoutRoot.Visibility = Visibility.Visible;
               this.LayoutRoot.Opacity = 0.9;
               timer.Start();
            }
            else
            {
               //首先隱藏圖片,圖片隱藏後隱藏掉透明的背景層。
               fadeImgStoryboard.Begin();
               fadeImgStoryboard.Completed += (sender, e) =>
               {
                  txtLoading.Visibility = Visibility.Collapsed;
                  fadeStoryboard.Begin();
               };
               fadeStoryboard.Completed += (sender, e) =>
               {
                  timer.Stop();
                  this.LayoutRoot.Visibility = Visibility.Collapsed;
               };
            }
         }
      }

      public Load()
      {
         InitializeComponent();
         timer.Interval = new TimeSpan(200000);
         timer.Tick += new EventHandler(timer_Tick);
         timer.Start();
      }

      void timer_Tick(object sender, EventArgs e)
      {
         _counter++;
         //設定旋轉中心點,根據圖片大小設定,值為圖片尺寸/2.
         rt.CenterX = 64;
         rt.CenterY = 64;
         rt.Angle -= 10; //旋轉圖片,每次旋轉10度,可自定義旋轉方向
         LoadImg.RenderTransform = rt;

         //讓Loading後面的點閃的不要太快
         if (_counter % 8 == 0)
         {
            if (txtLoading.Text.Equals("Loading..."))
            {
               txtLoading.Text = "Loading.";
            }
            else if (txtLoading.Text.Equals("Loading."))
            {
               txtLoading.Text = "Loading..";
            }
            else if (txtLoading.Text.Equals("Loading.."))
            {
               txtLoading.Text = "Loading...";
            }
         }
      }
   }

後臺程式碼主要控制圖片旋轉動畫和問題動畫,還有就是資料載入完畢時,隱藏頁面元素。

 

 三、在頁面中呼叫

 在MainPage中新增一個按鈕,新增click事件,程式碼如下:

public partial class MainPage : UserControl
   {
      private Load load = null;
      public MainPage()
      {
         InitializeComponent();
      }

      private void button1_Click(object sender, RoutedEventArgs e)
      {
         load = new Load();
         LayoutRoot.Children.Add(load);//將Load新增到頁面,會顯示在最頂層
         load.IsBusy = true;
         //線上程中呼叫,否則會造成UI執行緒阻塞。
         new System.Threading.Thread(() =>
         {
            for (int i = 0; i < 10; i++)
            {
               System.Threading.Thread.Sleep(500);
            }
            this.Dispatcher.BeginInvoke(() => 
            {
               load.IsBusy = false;
            });
         }).Start();
      }
   }

新增完成後按F5執行,我們在開啟的頁面中單擊按鈕,就可以看到效果了。

當然,這裡只是實現了和BusyIndicator一樣的效果,如果想像使用BusyIndicator那樣的話,我們還要進一步的進行封裝。

 

點選下載原始碼

 

 作者:雲霏霏

QQ交流群:243633526

 部落格地址:http://www.cnblogs.com/yunfeifei/

 宣告:本部落格原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未授權,貼子請以現狀保留,轉載時必須保留此段宣告,且在文章頁面明顯位置給出原文連線。

如果大家感覺我的博文對大家有幫助,請推薦支援一把,給我寫作的動力。

 

相關文章