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/

 声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

如果大家感觉我的博文对大家有帮助,请推荐支持一把,给我写作的动力。

 

时间: 2024-11-09 00:00:39

Silverlight及WPF中实现自定义BusyIndicator的相关文章

Silverlight与WPF中BeginInvoke的差异

Silverlight/WPF中,如果要在多线程中对界面控件值做修改,用Dispatcher对象的BeginInvoke方法无疑是最方便的办法 ,见:温故而知新:WinForm/Silverlight多线程编程中如何更新UI控件的值 但今天发现WPF中的BeginInvoke却无法自动将匿名方法/Lambda表达式转变成Delegate类型(注:对委托,匿名方法,Lambda感到陌生的朋友先阅读温故而知新:Delegate,Action,Func,匿名方法,匿名委托,事件) silverligh

在WPF中自定义你的绘制(五)

将我们的绘制转变为画刷 WPF中的画刷比GDI+中要强大得多,除了常用的实心画刷.渐变画刷外,还支持更多的平铺画刷,这包括DrawingBrush.ImageBrush.VisualBrush,其中DrawingBrush使得我们可以将自定义的绘制用于任何可以使用普通画刷的地方. 比如下面的代码,我们将自定义一个椭圆和一个矩形,然后将它们合并成一个图形并将 改图形用作窗口的背景画刷: public Window1() { InitializeComponent(); //background E

在WPF中自定义你的绘制(一)

在传统的Windows窗体编程中,如果我们需要打造一些比较个性化的控件,那么我们常常需要自定义控件的绘制(重写OnPaint等),即需要经常用到Graphics对象.而在WPF中,我们可以使用Xaml轻松编写出很有特色的界面元素,似乎与以前的以前利用Graphics对象手写代码绘制控件的日子越来越远了.其实在WPF中,如果我们需要低级别的自定义绘制同样是可以的,那么我们就需要一个名DrawingContext的类.与OnPaint方法相对应的是OnRender方法(当然,你也可以在其他地方进行绘

在WPF中自定义你的绘制(四)

原文:在WPF中自定义你的绘制(四)                                   在WPF中自定义你的绘制(四)                                                              周银辉 1,利用路径绘制图形(PathGeometry)有时我们需要绘制的图形可能很复杂而显得不是那么的规则,这时我们就需要将图形分解成若干小的部分(分解成线段.圆弧.贝塞尔曲线等等),然后将这些小部分使用PathGeometry组合在一起

扩展ArcGIS API for Silverlight/WPF 中的TextSymbol支持角度标注

原文 http://blog.csdn.net/esricd/article/details/7587136 在ArcGIS API for Silverlight/WPF中原版的TextSymbol只能支持文字正向显示.在很多实际项目中,往往需要文字标注有一些角度甚至是沿线标注,下面 我们来看一下原装的TextSymbol和扩展后的TextSymbol的比较和实现思路. 要实现右图的效果只需要从TextSymbol继承一个Symbol并增加Rotation属性,并加载相应的控件模板就行了. 以

WPF中自定义漂亮的进度条

wpf中自带的进度条是这个样子德. 在2003中这个进度条的样子就会变得非常难看. 在wpf中您可以自己制作任意样式的进度条.如下图: UserControl.xaml 用户控件 <Grid x:Name="LayoutRoot" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center"> <Grid.Re

MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件

原文  MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件 UI 前沿技术 WPF 中的多点触控操作事件 Charles Petzold 下载代码示例 就在过去几年,多点触控还只是科幻电影中表现未来主义的一种重要手法,现在俨然已经成为主流的用户界面技术. 多点触控显示屏现在成了新型智能手机和 Tablet 计算机的标准显示屏. 此外,它还可能在公共场所的计算机上普及,例如 Microsoft Surface 率先开发的网亭或桌面计算机. 实际存在的唯一不确定因素是多点触控在常规台式

WPF中对三维模型的控制

原文:WPF中对三维模型的控制 (以下选自南开大学出版社出版的<WPF和Silverlight教程>) 3Dmax中的建模模型可以导出为obj文件格式,将此文件导入WPF项目中,由WPF完成对三维造型的贴图和控制设计.本例在3Dmax中设计了1个双翼开瓶器模型,将"开瓶器.obj"和贴图材质文件都添加到项目中("素材"文件夹).图2-206 的左侧是"开瓶器.obj"文件拖入到[设计面板]后,在[对象和时间线]面板中看到的结构,右侧是

UI前沿技术-WPF中的多点触控操作事件

就在过去几年,多点触控还只是科幻电影中表现未来主义的一种重要手法,现在俨然已经成为主流的用户界面技术.多点触控显示屏现在成了新型智能手机和 Tablet 计算机的标准显示屏.此外,它还可能在公共场所的计算机上普及,例如 Microsoft Surface 率先开发的网亭或桌面计算机. 实际存在的唯一不确定因素是多点触控在常规台式计算机上的普及.这种普及的最大障碍或许是长时间在垂直屏幕上移动手指所产生的疲劳(称为"大猩猩手臂").我个人希望多点触控的强大功能将切实推进桌面显示屏的重新设计