Silverlight及WPF中實現自定義BusyIndicator


  在開發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/

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM