[WPF] 自訂Progress Bar

在Front-end Application, 當有request 須要進行較長時間的處理, (例如web API call, 檔案IO等), 通常除了disable UI controls 外, 為了令user 知道處理狀況, 便須要利用Progress bar 表達運作進度.

在WPF中, 與HTML5 一樣, 都可以利用<ProgressBar> 實現. 然而, 在TaskBar 中的圖示, 則須要再作處理. 在示範中, 用了code-behide 作Proof-of-Concept, 相信利用MVVM 都能夠順利使用.

MainWindow.xaml

<Window x:Class="Poc.ProgressBar.MainWindow"
        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:local="clr-namespace:Poc.ProgressBar"
        mc:Ignorable="d"
        Title="MainWindow" Height="130" Width="500">
    <Window.Resources>
        <Style TargetType="ProgressBar">
            <Setter Property="Height" Value="45" />
            <Setter Property="Margin" Value="5" />
            <Style.Triggers>
                <Trigger Property="IsIndeterminate"  Value="False">
                    <Setter Property="Background" Value="#80B5FFA9" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="Button">
            <Setter Property="Width" Value="100" />
            <Setter Property="Margin" Value="5" />
        </Style>
    </Window.Resources>
    
    <Window.TaskbarItemInfo>
        <TaskbarItemInfo ProgressState="Normal" />
    </Window.TaskbarItemInfo>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <ProgressBar Name="progTest" Grid.Row="0"></ProgressBar>
        <StackPanel Orientation="Horizontal"  Grid.Row="1">
            <Button Name="btnPause" Click="btnPause_Click">Pause</Button>
            <Button Name="btnError" Click="btnError_Click">Error</Button>
        </StackPanel>
    </Grid>
</Window>

在Windows 7 後, 透過TaskbarItemInfo, 可以控制Application 狀況的表示. 主要為Normal , Indeterminate, Error.

MainWindow.xaml.cs

public partial class MainWindow : Window
    {
        private const double TASKBAR_PROGRESS_VALUE_MAX = 1.0;
        private const int PROGRESS_BAR_PERCENTAGE_MIN = 0;
        private const int PROGRESS_BAR_PERCENTAGE_MAX = 100;
        private const int THREAD_DEFAULT_SLEEP_MILLIAN_SECOND = 100;

        private BackgroundWorker _testWorker = new BackgroundWorker();
        private bool _isPause = false;
        private bool _isCancel = false;

        public MainWindow()
        {
            InitializeComponent();

            _testWorker.WorkerReportsProgress = true;
            _testWorker.WorkerSupportsCancellation = true;
            _testWorker.DoWork += _testWorker_DoWork;
            _testWorker.ProgressChanged += _testWorker_ProgressChanged;
            _testWorker.RunWorkerCompleted += _testWorker_RunWorkerCompleted;
            _testWorker.RunWorkerAsync();
        }



        private void _testWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int i = 0;
            while ((i <= PROGRESS_BAR_PERCENTAGE_MAX) && (!_isPause) && (!_isCancel))
            {
                i++;
                System.Threading.Thread.Sleep(THREAD_DEFAULT_SLEEP_MILLIAN_SECOND);
                _testWorker.ReportProgress(i);
            }
        }
        private void _testWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progTest.Value = e.ProgressPercentage;
            TaskbarItemInfo.ProgressValue = e.ProgressPercentage / PROGRESS_BAR_PERCENTAGE_MAX;
        }

        private void _testWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (_isCancel)
            {
                
                TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Error;
                if (e.Error != null)
                {
                    MessageBox.Show(e.Error.Message);
                }
                MessageBox.Show("Application Cancel.");
                Application.Current.Shutdown();
            }
            else if (_isPause)
            {
                TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Paused;
                MessageBox.Show("Application Pause.");
            }
            else
            {
                TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Indeterminate;
                if (MessageBox.Show("Load completed, close application now?", "Exit Application", MessageBoxButton.YesNo)
                    .Equals(MessageBoxResult.Yes))
                {
                    Application.Current.Shutdown();
                }
            }
        }

        private void btnPause_Click(object sender, RoutedEventArgs e)
        {
            if (_isPause)
            {
                _isPause = false;
                TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal;
            }
            else
            {
                _isPause = true;
                TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Paused;
            }
        }

        private void btnError_Click(object sender, RoutedEventArgs e)
        {
            TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Error;
            _isCancel = true;
            _testWorker.CancelAsync();

        }
    }

在這裡, 利用BackgroundWorker 作示範, 控制ProgressBar 的運作. ProgressBar 須要留意的如下:

  • ProgressBar value 須要以1-100 代表其百分比; 而TaskBarItemInfo 則須要除以100;
  • TaskBar 狀態 以 enum TaskbarItemProgrssState表示;

Reference

Programming the Task Bar in Windows 7 with WPF 4, Part Four – Progress Bar Icon, MSDN

About C.H. Ling 262 Articles
a .net / Java developer from Hong Kong and currently located in United Kingdom. Thanks for Google because it solve many technical problems so I build this blog as return. Besides coding and trying advance technology, hiking and traveling is other favorite to me, so I will write down something what I see and what I feel during it. Happy reading!!!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.