GrapeCity.devlog

グレープシティ株式会社のDeveloper Tools〈開発支援ツール〉の、製品のTIPSや発売などに関する最新情報をお届けします。

SPREADでデータ読み込み中のインジケータを表示する

普段Webサイトを閲覧していると、画像が大量だったり、情報量が多かったり、読み込みに時間のかかるページでは、以下のようにローディング中のアイコンがグルグル回って表示されるのをよく見かけます。

ローディングインジケーターの例
ローディングインジケーターの例

デスクトップアプリケーションでも時間のかかる処理を実行中にこういったローディング中のUIを表示させたい、ということがあると思いますが、2019年2月7日に公開されたSPREAD for WPF 2.0JのSP2の新機能「ローディングUI」を使えば、このようなローディング中のUIを簡単に実装することができます。

今回は、このローディングUIを使ったサンプルを作成して実際にその機能を体感してみましょう。

  1. ローディングUIとは?
  2. ローディングUIを実装するときの制約
  3. 大量データの読み込み時にローディングUIを表示する
  4. ローディングUIをちょっとだけカスタマイズ

1.ローディングUIとは?

その名前からある程度の推測はつきますが、「ローディングUI」とは、現在処理中であることを示すインジケータを表示する機能です。

百聞は一見に如かずということで、今回作成するサンプルをVisual Studioでデバッグ実行したときの様子を以下にご紹介いたします。

ここでは、500列×10000行のXMLファイルのデータをGcSpreadGridコントロールに非同期に読み込むときにローディングUIを表示しています。

ローディングUIの動作
ローディングUIの動作

実装方法も非常に簡単で、ローディングUIを表示させたい処理の前後で、IsLoadingUIVisibleプロパティのtrueとfalseを切り替えるだけです。

// ローディングUIの開始
gcSpreadGrid1.IsLoadingUIVisible = true;

// ローディングUIの停止
gcSpreadGrid1.IsLoadingUIVisible = false;

2. ローディングUIを実装するときの制約

このローディングUIはUIスレッドで表示されるため、UIスレッドがブロックされないように実装する必要があります。言い換えると「同期実行ではUIスレッドがブロックされるので、対象となる(時間のかかる)処理を非同期で実行する必要があるということです」

参考情報:製品ヘルプ「ローディングUI」

例えば、下のコードは、データXMLファイルを同期実行で読み込む例です。この場合、ローディングUIの機能は働きません。

動作しないサンプルコード
private void Button_Click(object sender, RoutedEventArgs e)
{
    // ローディングUIの開始
    gcSpreadGrid1.IsLoadingUIVisible = true;

    // データ取得用DataSetの作成
    System.Data.DataSet ds = new System.Data.DataSet();

    // データXMLファイルの読み込み:同期実行
    ds.ReadXml("TestData.xml");

    // データソースの連結
    gcSpreadGrid1.ItemsSource = ds.Tables[0].DefaultView;

    // ローディングUIの停止
    gcSpreadGrid1.IsLoadingUIVisible = false;
}

ローディングUIの機能を有効にするには、次のように非同期実行にする必要があります。ここでは、Taskクラスの機能を使って処理を非同期実行しています。また、Button_Clickの宣言にasync修飾子を追加してprivate voidではなくprivate async voidになっていることに注意してください。

動作するサンプルコード
private async void Button_Click(object sender, RoutedEventArgs e)
{
    // ローディングUIの開始
    gcSpreadGrid1.IsLoadingUIVisible = true;

    // データ取得用DataSetの作成
    System.Data.DataSet ds = new System.Data.DataSet();

    // データXMLファイルの読み込み:非同期実行
    await Task.Yield();
    await Task.Run(() =>
    {
        ds.ReadXml("TestData.xml");
    });

    // データソースの連結
    gcSpreadGrid1.ItemsSource = ds.Tables[0].DefaultView;

    // ローディングUIの停止
    gcSpreadGrid1.IsLoadingUIVisible = false;
}

3. 大量データの読み込み時にローディングUIを表示する

さあ、いよいよサンプルの作成です。次の手順でVisual Studioの新規プロジェクトにButtonコントロールとGcSpreadGridコントロールを貼りつけてコードを記述します。

なお、さきほども少し触れましたがprivate void Button_Clickでは非同期動作を実行できないのでprivate async void Button_Clickのようにasync修飾子を追加するのを忘れないでください。

作成手順
  1. Visual Studioを起動して新しいプロジェクトとしてWPFアプリ(.NET Framework)を選択する。
  2. [MainWindow.xaml]タブでWindowにButtonコントロールとGcSpreadGridコントロールを貼り付ける。
  3. ButtonコントロールのNameを"button1"に設定して、位置を調節する。
  4. GcSpreadGridコントロールのNameを"gcSpreadGrid1"に設定して、位置とサイズを調節する。
  5. ButtonコントロールをダブルクリックしてButton_Clickイベントを自動生成する。
  6. [MainWindow.xaml.cs]タブで下記のサンプルコードを記述する
  7. プロジェクトをデバッグ実行する。
  8. Buttonコントロールをクリックして動作を確認する。

Visual Studioでのコントロールの配置
Visual Studioでのコントロールの配置

今回作成するサンプルのコードビハインドの全体は次のとおりです。

using System;
using System.Threading.Tasks;
using System.Windows;

namespace SpreadWPF_LoadingUI
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // データXMLファイルの作成
            MakeDataFile();
        }

        private void MakeDataFile()
        {
            // DataSetの作成
            System.Data.DataSet ds = new System.Data.DataSet();
            System.Data.DataTable dt = ds.Tables.Add("Test");
            for (var i = 0; i < 500; i++)
            {
                dt.Columns.Add(string.Format("Col{0}", i + 1), typeof(String));
            }
            for (var i = 0; i < 10000; i++)
            {
                System.Data.DataRow dr = dt.NewRow();
                for (var j = 0; j < dt.Columns.Count; j++)
                {
                    dr[j] = string.Format("R{0}C{1}", i, j);
                }
                dt.Rows.Add(dr);
            }
            ds.AcceptChanges();

            // 作成したデータのXMLファイルへの保存
            ds.WriteXml("TestData.xml", System.Data.XmlWriteMode.WriteSchema);
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            // ローディングUIの開始
            gcSpreadGrid1.IsLoadingUIVisible = true;

            // データ取得用DataSetの作成
            System.Data.DataSet ds = new System.Data.DataSet();

            // データXMLファイルの読み込み:非同期実行
            await Task.Yield();
            await Task.Run(() =>
            {
                ds.ReadXml("TestData.xml");
            });

            // データソースの連結
            gcSpreadGrid1.ItemsSource = ds.Tables[0].DefaultView;

            // ローディングUIの停止
            gcSpreadGrid1.IsLoadingUIVisible = false;
        }
    }
}

Windowのコンストラクタ内で呼び出しているMakeDataFileメソッドでテスト用のXMLデータファイルを作成します。そして、Button_ClickイベントでそのXMLファイルを非同期にDataSetに読み込んでGcSpreadGridのItemsSourceプロパティに設定します。

このとき、ItemsSourceプロパティに設定できるのは、IEnumerable インターフェイスを実装したコレクションに限定されているため、DataSetそのものではなく、DataSetに含まれているDataTableDefaultViewを使います。

4. ローディングUIをちょっとだけカスタマイズ

ローディングUIで表示するインジケータは、製品ヘルプで解説しているようにControlTemplateの機能を使って独自のものに変更できます。とても簡単なカスタマイズ例として、今回はGcSpreadGridの組み込みのインジケータの色を変更する方法をご紹介します。

実は、GcSpreadGridに組み込まれているインジケータの内容がデモアプリケーションや製品付属サンプルで公開されているので、その一部を書き換えるだけでインジケータの色を簡単に変更することができるのです。

製品付属サンプルの「Functions」ソリューションの[Pages]-[Appearance]-[uAppearanceLoadAnimation.xaml]にある「Key="default"」の<ControlTemplate>タグを使って、GcSpreadGridに組み込まれているローディングUIのインジケータの色を変更してみましょう。

インジケータの色を変更するには、上記で作成したサンプルのXAMLの<Window>タグの次に下記のような<Window.Resources>タグを追加しておきます。

  <Window.Resources>
    <Style TargetType="{x:Type sg:LoadingUI}">
      <Setter Property="Template">
        <Setter.Value>
          <!--ここにControlTemplateを挿入します-->
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>

次に、製品付属サンプルの「Functions」ソリューションの[Pages]-[Appearance]-[uAppearanceLoadAnimation.xaml]にある「Key="default"」の<ControlTemplate>タグをコピーして、上記のXMLの<Setter.Value>タグの中に挿入します。

そして、<ControlTemplate>タグ内の<Canvas>タグの中の<Ellipse>タグのFill属性を「#FF000000」から「#FF4169E1」へ上書きします。これだけで、組み込みのローディングUIのインジケータの色を変更することができます。

また、コントロール全体を覆う矩形の透明度と色を変更したいときには、ヘルプに記載しているように<Grid Background="#3F7F7F7F" Cursor="Wait">の「3F7F7F7F」の部分を変更してください。

ControlTemplateで<Ellipse>タグのFill属性を 変更した後は次のようになります。

<ControlTemplate TargetType="{x:Type sg:LoadingUI}">
  <Grid Background="#3F7F7F7F" Cursor="Wait">
    <Grid.Resources>
      <!--この部分は省略しています-->
    </Grid.Resources>
    <Canvas x:Name="LayoutRoot" RenderTransformOrigin="0.5,0.5" Width="120" 
     Height="120" HorizontalAlignment="Center" VerticalAlignment="Center">
      <Ellipse Name="C0" Width="20" Height="20" Canvas.Left="50" 
        Canvas.Top="0" Stretch="Fill" Fill="#FF4169E1" Opacity="0.2" />
      <!--この部分は省略しています-->
      <!--C1からC8のEllipseタグについても同様にFill部分を変更します-->
      <Ellipse Name="C9" Width="20" Height="20" Canvas.Left="79.3892626146236" 
        Canvas.Top="9.54915028125262" Stretch="Fill" Fill="#FF4169E1" Opacity="0.2" />
      <Canvas.Triggers>
        <!--この部分は省略しています-->
      </Canvas.Triggers>
    </Canvas>
  </Grid>
</ControlTemplate>

※省略していない完全版のXAMLのコードはこちらからダウンロードできます。

XAMLを修正してサンプルを実行すると、「1.ローディングUIとは?」のスクリーンショットのように色がカスタマイズされたローディングUIが表示されます。

終わりに

製品Webサイトでは、SPREAD for WPF 2.0Jのデモアプリケーションを公開しています。グリッド系コントロールに欠かせない基本機能から、今回紹介したローディングUIのようにちょっと便利なものまで、数多くの機能が用意されていますので、以下より是非お試しください。

  • グレープシティ株式会社のDeveloper Tools〈開発支援ツール〉ではエンジニア経験者を幅広く募集しています。
  • グレープシティ株式会社のDeveloper Tools〈開発支援ツール〉の製品のデモアプリケーションをお試しください。