用WPF实现“等待”小转盘

我们在开发过程中常常会遇到一些比较耗时的操作,比如一次较慢的网络访问,或者复制一个较大的文件等,如果不使用多线程,而是直接用UI线程去做这些动作,那么就会让用户界面失去响应,带来很不好的用户体验。较好的做法是在界面上呈现出一个小动画,用户看到有东西在动,就不认为程序已经死掉,例如这个:

 当然了,更好的做法是实现一个进度条,但很多时候我们根本不能获取到进度,所以只能显示这么一个小转盘,小转盘效果的比较简单的实现方法是显示一个小小的gif,但WPF默认的Image控件并不直接支持gif动画效果,所幸的是我们可以通过一个叫“WpfAnimatedGif”第三方的库来很方便地把这个动画效果显示出来,这个库可以通过NuGet获取到,我提供的完整代码下载里也有。这是我的Demo的效果图:

小转盘出现的同时,我们希望能够暂时阻挡用户的操作,我最早想到的办法是把窗口Disable掉,也就是把它的IsEnabled属性设为False,但这样做可能达不到我们想要的效果,因为这样只能把窗口的客户区Disabled掉,而非客户区却仍然可以操作,比如标题栏上的最小化,最大化和关闭按钮,所以比较好的做法是用一个模态对话框。这是我设计的模态对话框:

<Window x:Class="WaitingDemo.WaitingDlg"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:gif="http://wpfanimatedgif.codeplex.com"
        Title="WaitingDlg" Height="100" Width="400" WindowStyle="None" Background="Transparent" AllowsTransparency="True"
        WindowStartupLocation="CenterOwner" Closing="Window_Closing" Loaded="Window_Loaded"
        TextOptions.TextFormattingMode="Display">
    <Grid>
        <Border CornerRadius="5" Height="40" BorderBrush="Black" BorderThickness="1" Background="White" Width="350">
            <Border.Effect>
                <DropShadowEffect Color="Black"></DropShadowEffect>
            </Border.Effect>
            <Grid VerticalAlignment="Center">
                <Image gif:ImageBehavior.AnimatedSource="loading.gif" Width="28" Height="28" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,0,0,0"/>
                <TextBlock Name="tbPrompt" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.ColumnSpan="2" Margin="0,7">任务执行中...</TextBlock>
            </Grid>
        </Border>
    </Grid>
</Window>

这是几个要注意的属性设置:

  • WindowStyle - 设置为None来使得这个模态对话框没有标题栏,当然也就没有了最小化、最大化和关闭按钮
  • Background - 设置为Transparent来让这个模态对话框背景透明
  • AllowsTransparency - 如果不把这个属性设置为True,那模态对话框的依然是不透明的,你将看到一个黑框
  • WindowStartupLocation - 设置为CenterOwner让模态对话框默认居中显示

细心的你也许还发觉了:只是不显示关闭按钮还是不够的,用户还是可以通过<Alt>+<F4>来关闭这个对话框,所以需要处理Closing事件,判断这个关闭动作是否我们的程序提出来的。

对于处理任务,我创建了一个接口:

    public interface ILongTimeTask
    {
        void Start(WaitingDlg dlg);
    }

将WaitingDlg传入的原因是想让工作线程结束的时候关闭掉这个模态对话框:

        public void TaskEnd(Object result)
        {
            m_taskResult = result; //用于返回执行的结果(也可以为null)
            m_bCloseByMe = true; //这个标志表示“关闭”动作由我们的程序提出
            Dispatcher.BeginInvoke(new CloseMethod(Close)); //工作线程对界面元素的操作必须用这种调用方式
        }

完整代码:下载

posted @ 2013-03-02 00:14  guogangj  阅读(7157)  评论(0编辑  收藏  举报