2019-06-24に投稿

【WPF】ProgressRingを使って時間のかかる処理を分かりやすくする

読了目安:12分

WEBなんかでよく見かける、処理中にローディング画像を表示するってのをやってみます。

重い処理なんかで、ローディング表示がない場合はユーザーが動いているのかどうか心配になり、挙句の果てに強制終了してしまったりしますので、大事ですよね。
体感速度も違ってきますし。

プログラムは前回のものを利用します。
【WPF】MahApps.Metro で見た目をスタイリッシュにしてみる

スタイルの追加

StyleDic.xaml

宣言の追加

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:Mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
                    xmlns:local="clr-namespace:WpfApp1.Style">

スタイルの追加

    <!-- プログレスリング -->
    <Style x:Key="pgr-normal" TargetType="{x:Type Mah:ProgressRing}" >
        <Setter Property="Foreground" Value="#33adff" />
        <Setter Property="IsActive" Value="False" />
        <Setter Property="Width" Value="100" />
        <Setter Property="Height" Value="100" />
        <Setter Property="Panel.ZIndex" Value="100" />
    </Style>
    <!-- プログレスオーバーレイ-->
    <Style x:Key="rec-overlay" TargetType="{x:Type Rectangle}" >
        <Setter Property="Fill" Value="#000000" />
        <Setter Property="Opacity" Value="0.2" />
        <Setter Property="Panel.ZIndex" Value="1000" />
        <Setter Property="Margin" Value="0,0,0,30" />
        <Setter Property="Visibility" Value="Collapsed" />
    </Style>

画面の修正

MainWindow.xaml

抜粋

<!-- Gridの高さと幅を設定 -->
<Grid Height="350" Width="530">

<!-- プログレスリングとオーバーレイを追加 -->
        <Rectangle x:Name="rec_overlay" Width="530" Height="330" Style="{StaticResource rec-overlay}" />
        <Mah:ProgressRing x:Name="loading_image" Style="{StaticResource pgr-normal}"/>

 

全体

<Mah:MetroWindow x:Class="WpfApp1.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:WpfApp1"
        xmlns:Mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
        mc:Ignorable="d"
        Title="一覧" Height="350" Width="530"
        GlowBrush="{DynamicResource AccentColorBrush}"
        BorderThickness="1"
        >
    <Window.Resources>
        <ResourceDictionary Source="/Style/StyleDic.xaml"/>
    </Window.Resources>

    <Grid Height="350" Width="530">
        <Grid.Resources>
            <local:KindConverter x:Key="KindConv"/>
        </Grid.Resources>
        <Rectangle x:Name="rec_overlay" Width="530" Height="330" Style="{StaticResource rec-overlay}" />
        <Mah:ProgressRing x:Name="loading_image" Style="{StaticResource pgr-normal}"/>

        <Label Content="名前:" Margin="10,10,0,0" Style="{StaticResource lb-normal}"/>
        <TextBox x:Name="search_name" Margin="56,12,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"  Style="{StaticResource MetroTextBox}"/>
        <Label Content="種別:" Margin="201,10,0,0" Style="{StaticResource lb-normal}"/>
        <ComboBox x:Name="search_kind" Margin="252,12,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="125"  Style="{StaticResource MetroComboBox}"/>
        <Button x:Name="search_button" Content="検索" HorizontalAlignment="Left" Margin="432,12,0,0" VerticalAlignment="Top" Width="75"  Click="search_button_Click" Style="{DynamicResource SquareButtonStyle}"/>

        <DataGrid Name="dataGrid" HorizontalAlignment="Left" Margin="10,43,0,0" Width="497" Height="225" Style="{StaticResource grid-normal}" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding No}" ClipboardContentBinding="{x:Null}" Header="No" IsReadOnly="True" Width="50"/>
                <DataGridTextColumn Binding="{Binding Name}" ClipboardContentBinding="{x:Null}" Header="名前" IsReadOnly="True" Width="100"/>
                <DataGridTextColumn Binding="{Binding Sex}" ClipboardContentBinding="{x:Null}" Header="性別" IsReadOnly="True" Width="40"/>
                <DataGridTextColumn Binding="{Binding Age}" ClipboardContentBinding="{x:Null}" Header="年齢" IsReadOnly="True" Width="40"/>
                <DataGridTextColumn Binding="{Binding Kind, Converter={StaticResource KindConv}}" ClipboardContentBinding="{x:Null}" Header="種別" IsReadOnly="True" Width="120"/>
                <DataGridTextColumn Binding="{Binding Favorite}" ClipboardContentBinding="{x:Null}" Header="好物" IsReadOnly="True" Width="*"/>
            </DataGrid.Columns>
        </DataGrid>
        <Button x:Name="add_button" Content="追加" HorizontalAlignment="Left" Margin="10,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="add_button_Click" Style="{DynamicResource AccentedSquareButtonStyle}"/>
        <Button x:Name="upd_button" Content="更新" HorizontalAlignment="Left" Margin="90,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="upd_button_Click" Style="{DynamicResource AccentedSquareButtonStyle}"/>
        <Button x:Name="del_button" Content="削除" HorizontalAlignment="Left" Margin="170,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="del_button_Click" Style="{DynamicResource AccentedSquareButtonStyle}"/>
        <Button x:Name="imp_button" Content="CSV読込" HorizontalAlignment="Left" Margin="250,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="imp_button_Click" Style="{DynamicResource AccentedSquareButtonStyle}"/>
        <Button x:Name="exp_button" Content="CSV出力" HorizontalAlignment="Left" Margin="330,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="exp_button_Click" Style="{DynamicResource AccentedSquareButtonStyle}"/>
        <Button x:Name="fld_button" Content="フォルダ参照" HorizontalAlignment="Left" Margin="410,273,0,0" VerticalAlignment="Top" Width="97" Height="30" Click="fld_button_Click" Style="{DynamicResource AccentedSquareButtonStyle}"/>
    </Grid>
</Mah:MetroWindow>

 

プログラムの修正

ライブラリ使用宣言の追加

MainWindow.xaml.cs

抜粋

using System.ComponentModel;

 

検索処理の修正

検索処理を、BackgroundWorkerを使って非同期処理として分離します。

MainWindow.xaml.cs

        /// <summary>
        /// 検索処理(非同期)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SearchProcess(object sender, DoWorkEventArgs e)
        {
            // 時間のかかるようにする
            System.Threading.Thread.Sleep(3000);

            using (var conn = new MySqlConnection("Database=DB01;Data Source=localhost;User Id=USER01;Password=USER01; sqlservermode=True;"))
            {
                conn.Open();

                // 猫データマスタを取得してコンボボックスに設定する
                using (DataContext con = new DataContext(conn))
                {
                    String searchName = (e.Argument as Object[])[0] as String;
                    String searchKind = (e.Argument as Object[])[1] as String;

                    // データを取得
                    Table<Cat> tblCat = con.GetTable<Cat>();

                    // サンプルなので適当に組み立てる
                    IQueryable<Cat> result;
                    if (searchKind == "")
                    {
                        // 名前は前方一致のため常に条件していしても問題なし
                        result = from x in tblCat
                                 where x.Name.StartsWith(searchName)
                                 orderby x.No
                                 select x;
                    }
                    else
                    {
                        result = from x in tblCat
                                 where x.Name.StartsWith(searchName) &amp; x.Kind == searchKind
                                 orderby x.No
                                 select x;

                    }
                    e.Result = result.ToList();
                }

                conn.Close();
            }
        }

        /// <summary>
        /// 検索完了処理(非同期)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SearchProcessCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.dataGrid.ItemsSource = e.Result as List<Cat>;
            ToggleProgressRing();
        }

        /// <summary>
        /// 処理中イメージの表示/非表示切り替え
        /// 
        /// </summary>
        private void ToggleProgressRing()
        {
            if (this.loading_image.IsActive)
            {
                this.loading_image.IsActive = false;
                this.rec_overlay.Visibility = Visibility.Collapsed;
            }
            else
            {
                this.loading_image.IsActive = true;
                this.rec_overlay.Visibility = Visibility.Visible;
            }
        }

メソッドの修正

検索ボタンをクリックした際に、先ほど追加した非同期処理を呼び出すように修正します。

        /// <summary>
        /// 検索ボタンクリックイベント.
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void search_button_Click(object sender, RoutedEventArgs e)
        {
            logger.Info("検索ボタンクリック");

            Object[] param = new Object[2];
            param[0] = this.search_name.Text;
            param[1] = (this.search_kind.SelectedValue as Kind).KindCd;

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += SearchProcess;
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(SearchProcessCompleted);
            worker.RunWorkerAsync(param);

            ToggleProgressRing();

        }

 

起動してみる

動画
https://www.youtube.com/embed/MOaQGtqEoUA

いい感じですね。

ローディング表示だけだと、処理中にボタンなんかが押せてしまうので図形をかぶせるようにしてみました。

まとめ

簡単な(画面の少ない)アプリであればこんな方法でも十分かなと思います。

ソースはこちら

GitHub

ではでは。

 

Originally published at www.doraxdora.com
ツイッターでシェア
みんなに共有、忘れないようにメモ

doraxdora

IT関係の仕事をしています/1985年生まれの東京在住/便利なサービスやツール漁りや料理などが好き/2017年~ブログやってます/自分でサービスとか作ってリリースしたい/何かありましたらお気軽にDMどうぞ

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント