WPF Loading

背景:WPF项目中,经常会处理一个或者多个耗时很久的任务,比如调用服务的数据查询然后把N条数据加载到列表控件。这种情况下如果采用一般的方式同步处理那么WPF的UI就会失去响应,卡死在那个地方,整个系统可能都无法操作,这对用户来说简直就是太不友好了,也得傻傻的等待任务完成才能干其他事件......

这个问题的解决方法都是采用多线程来处理,一般是开起一个后台线程去完成这些任务,这样UI线程仍然可以响应用户的其它操作,等待后台把任务处理完毕了在通知UI、通知用户。这样不仅提高了效率、也让系统的体验更好。

WPF的WPFToolKit、WPFToolKitExtended里面给我提供了一个BusyIndicator的控件(具体用法这里不讲了,有兴趣的朋友自己去查吧)。使用它可以很好的提示用户系统正在处理的任务和进度等。。。但是它的样式外观个人觉得不大好,还是比较喜欢那种转圈的。于是自己模仿它重新写了一个控件,用法和BusyIndicator一样,在这里分享一下,希望对大家有用。下面是效果图:

 

下面贴出控件后台代码及其实现动画的代码仅供参考: 

后台代码及动画实现
  1  using System;
  2  using System.Windows;
  3  using System.Windows.Controls;
  4  using System.Windows.Shapes;
  5  using System.Windows.Threading;
  6  using System.Windows.Media;
  7  using System.Windows.Media.Animation;
  8  
  9  namespace BusyIndicator
 10  {
 11      /// <summary>
 12      /// A control to provide a visual indicator when an application is busy.
 13      /// </summary>
 14      [TemplateVisualState(Name = VisualStates.StateIdle, GroupName = VisualStates.GroupBusyStatus)]
 15      [TemplateVisualState(Name = VisualStates.StateBusy, GroupName = VisualStates.GroupBusyStatus)]
 16      [TemplateVisualState(Name = VisualStates.StateVisible, GroupName = VisualStates.GroupVisibility)]
 17      [TemplateVisualState(Name = VisualStates.StateHidden, GroupName = VisualStates.GroupVisibility)]
 18      [StyleTypedProperty(Property = "OverlayStyle", StyleTargetType = typeof(Rectangle))]
 19      [StyleTypedProperty(Property = "ProgressBarStyle", StyleTargetType = typeof(ProgressBar))]
 20      public class BusyIndicator : ContentControl
 21      {
 22          /// <summary>
 23          /// Gets or sets a value indicating whether the BusyContent is visible.
 24          /// </summary>
 25          protected bool IsContentVisible { get; set; }
 26  
 27          /// <summary>
 28          /// Timer used to delay the initial display and avoid flickering.
 29          /// </summary>
 30          private DispatcherTimer _displayAfterTimer;
 31  
 32          /// <summary>
 33          /// Instantiates a new instance of the BusyIndicator control.
 34          /// </summary>
 35          public BusyIndicator()
 36          {
 37              DefaultStyleKey = typeof(BusyIndicator);
 38              _displayAfterTimer = new DispatcherTimer();
 39              _displayAfterTimer.Tick += new EventHandler(DisplayAfterTimerElapsed);
 40              this.BusyContent = InitBusyContent();
 41          }
 42  
 43          /// <summary>
 44          /// Overrides the OnApplyTemplate method.
 45          /// </summary>
 46          public override void OnApplyTemplate()
 47          {
 48              base.OnApplyTemplate();
 49              ChangeVisualState(false);
 50          }
 51  
 52          /// <summary>
 53          /// Handler for the DisplayAfterTimer.
 54          /// </summary>
 55          /// <param name="sender">Event sender.</param>
 56          /// <param name="e">Event arguments.</param>
 57          private void DisplayAfterTimerElapsed(object sender, EventArgs e)
 58          {
 59              _displayAfterTimer.Stop();
 60              IsContentVisible = true;
 61              ChangeVisualState(true);
 62          }
 63  
 64          /// <summary>
 65          /// Changes the control's visual state(s).
 66          /// </summary>
 67          /// <param name="useTransitions">True if state transitions should be used.</param>
 68          protected virtual void ChangeVisualState(bool useTransitions)
 69          {
 70              VisualStateManager.GoToState(this, IsBusy ? VisualStates.StateBusy : VisualStates.StateIdle, useTransitions);
 71              VisualStateManager.GoToState(this, IsContentVisible ? VisualStates.StateVisible : VisualStates.StateHidden, useTransitions);
 72          }
 73  
 74          /// <summary>
 75          /// Gets or sets a value indicating whether the busy indicator should show.
 76          /// </summary>
 77          public bool IsBusy
 78          {
 79              get { return (bool)GetValue(IsBusyProperty); }
 80              set { SetValue(IsBusyProperty, value); }
 81          }
 82  
 83          /// <summary>
 84          /// Identifies the IsBusy dependency property.
 85          /// </summary>
 86          public static readonly DependencyProperty IsBusyProperty = DependencyProperty.Register(
 87              "IsBusy",
 88              typeof(bool),
 89              typeof(BusyIndicator),
 90              new PropertyMetadata(false, new PropertyChangedCallback(OnIsBusyChanged)));
 91  
 92          /// <summary>
 93          /// IsBusyProperty property changed handler.
 94          /// </summary>
 95          /// <param name="d">BusyIndicator that changed its IsBusy.</param>
 96          /// <param name="e">Event arguments.</param>
 97          private static void OnIsBusyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 98          {
 99              ((BusyIndicator)d).OnIsBusyChanged(e);
100          }
101  
102          /// <summary>
103          /// IsBusyProperty property changed handler.
104          /// </summary>
105          /// <param name="e">Event arguments.</param>
106          protected virtual void OnIsBusyChanged(DependencyPropertyChangedEventArgs e)
107          {
108              if (IsBusy)
109              {
110                  if (DisplayAfter.Equals(TimeSpan.Zero))
111                  {
112                      // Go visible now
113                      IsContentVisible = true;
114                  }
115                  else
116                  {
117                      // Set a timer to go visible
118                      _displayAfterTimer.Interval = DisplayAfter;
119                      _displayAfterTimer.Start();
120                  }
121              }
122              else
123              {
124                  // No longer visible
125                  _displayAfterTimer.Stop();
126                  IsContentVisible = false;
127              }
128              ChangeVisualState(true);
129          }
130  
131          /// <summary>
132          /// Gets or sets a value indicating the busy content to display to the user.
133          /// </summary>
134          public object BusyContent
135          {
136              get { return (object)GetValue(BusyContentProperty); }
137              set { SetValue(BusyContentProperty, value); }
138          }
139  
140          /// <summary>
141          /// Identifies the BusyContent dependency property.
142          /// </summary>
143          public static readonly DependencyProperty BusyContentProperty = DependencyProperty.Register(
144              "BusyContent",
145              typeof(object),
146              typeof(BusyIndicator),
147              new PropertyMetadata(null));
148  
149          /// <summary>
150          /// Gets or sets a value indicating the template to use for displaying the busy content to the user.
151          /// </summary>
152          public DataTemplate BusyContentTemplate
153          {
154              get { return (DataTemplate)GetValue(BusyContentTemplateProperty); }
155              set { SetValue(BusyContentTemplateProperty, value); }
156          }
157  
158          /// <summary>
159          /// Identifies the BusyTemplate dependency property.
160          /// </summary>
161          public static readonly DependencyProperty BusyContentTemplateProperty = DependencyProperty.Register(
162              "BusyContentTemplate",
163              typeof(DataTemplate),
164              typeof(BusyIndicator),
165              new PropertyMetadata(null));
166  
167          /// <summary>
168          /// Gets or sets a value indicating how long to delay before displaying the busy content.
169          /// </summary>
170          public TimeSpan DisplayAfter
171          {
172              get { return (TimeSpan)GetValue(DisplayAfterProperty); }
173              set { SetValue(DisplayAfterProperty, value); }
174          }
175  
176          /// <summary>
177          /// Identifies the DisplayAfter dependency property.
178          /// </summary>
179          public static readonly DependencyProperty DisplayAfterProperty = DependencyProperty.Register(
180              "DisplayAfter",
181              typeof(TimeSpan),
182              typeof(BusyIndicator),
183              new PropertyMetadata(TimeSpan.FromSeconds(0.1)));
184  
185          /// <summary>
186          /// Gets or sets a value indicating the style to use for the overlay.
187          /// </summary>
188          public Style OverlayStyle
189          {
190              get { return (Style)GetValue(OverlayStyleProperty); }
191              set { SetValue(OverlayStyleProperty, value); }
192          }
193  
194          /// <summary>
195          /// Identifies the OverlayStyle dependency property.
196          /// </summary>
197          public static readonly DependencyProperty OverlayStyleProperty = DependencyProperty.Register(
198              "OverlayStyle",
199              typeof(Style),
200              typeof(BusyIndicator),
201              new PropertyMetadata(null));
202  
203          /// <summary>
204          /// Gets or sets a value indicating the style to use for the progress bar.
205          /// </summary>
206          public Style ProgressBarStyle
207          {
208              get { return (Style)GetValue(ProgressBarStyleProperty); }
209              set { SetValue(ProgressBarStyleProperty, value); }
210          }
211  
212          /// <summary>
213          /// Identifies the ProgressBarStyle dependency property.
214          /// </summary>
215          public static readonly DependencyProperty ProgressBarStyleProperty = DependencyProperty.Register(
216              "ProgressBarStyle",
217              typeof(Style),
218              typeof(BusyIndicator),
219              new PropertyMetadata(null));
220  
221          #region BusyContent
222  
223          private Grid InitBusyContent()
224          {
225              var g = new Grid();  
226              #region AddPaths
227              int counter = 12;
228              for (int i = 0; i < counter; i++)
229              {
230                  Path path = new Path();
231                  path.Data = Geometry.Parse("M 0,0 L -1,0 L -1,-12 L 0,-13 L 1,-12 L 1,0 Z");
232                  path.Stroke = new SolidColorBrush(Colors.SkyBlue);
233                  path.Fill = new SolidColorBrush(Colors.SkyBlue);
234                  path.Opacity = 0.1;
235  
236                  TransformGroup tg = new TransformGroup();
237                  path.RenderTransform = tg;
238                  TranslateTransform tt = new TranslateTransform();
239                  tt.Y = -8; tt.X = 1;
240  
241                  RotateTransform rt = new RotateTransform();
242                  rt.Angle = i * 30;
243                  tg.Children.Add(tt);
244                  tg.Children.Add(rt);  
245  
246                  DoubleAnimation da = new DoubleAnimation();
247                  da.From = 1.0;
248                  da.To = 0.1;
249                  da.Duration = new Duration(TimeSpan.FromSeconds(1));
250  
251                  da.BeginTime = TimeSpan.FromMilliseconds(i * 1000 / counter);
252                  da.RepeatBehavior = RepeatBehavior.Forever;
253  
254                  path.VerticalAlignment = System.Windows.VerticalAlignment.Center;
255                  path.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
256                  g.Children.Add(path); 
257                  path.BeginAnimation(Path.OpacityProperty, da);
258              }
259              return g;
260  
261              #endregion
262          }
263  
264          #endregion
265      }
266  }

 

 下面是实现的整个工程代码:

/Files/rpoplar/BusyIndicator.zip 

 

posted @ 2012-10-27 23:38  rpoplar  阅读(2218)  评论(3编辑  收藏  举报