空気が良い日本の地区ランキング 2023年 で実装したWindowsフォームアプリは、処理中ダイアログをシンプルに実装していて、処理中ダイアログを早く実装したい場合の参考になります。
ソースコードは GitHub で公開しています。
処理中ダイアログ側
処理中ダイアログ側の処理は、StatusTextプロパティに渡された値を、txtStatusテキストボックスに表示しているだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
namespace WinFormsApp1 { public partial class ProcessingDialog : Form { // txtStatusのテキストを設定・取得するためのプロパティ public string StatusText { get { return txtStatus.Text; } set { txtStatus.Text = value; } } public ProcessingDialog() { InitializeComponent(); this.ControlBox = false; // タイトルバーの制御ボックスを非表示に設定 } private void ProcessingDialog_Shown(object sender, EventArgs e) { txtStatus.Text = string.Empty; } } } |
処理中ダイアログを呼び出す側の実装
「CSVファイルをインポート」ボタンのクリックイベント処理から、ファイル操作/DB操作の細かい処理は除外し、確認/完了メッセージボックス表示、Task実行、処理中ダイアログ表示/非表示のみにすると下記になります。
・btnCsvFileImport_Clickボタンクリックイベントハンドラは asyncを加えて非同期メソッドへ変更。
・ファイルインポート/DB処理は、Task.Run()内に実装し、別スレッドで実行。
・別スレッドの task開始後、ProcessingDialog.ShowDialog()で処理中ダイアログを表示し、await task が終わるまで、処理中ダイアログは表示したままにしている。
・インポートしたファイルが増える度に、processingDialog.Invoke()を通して processingDialog.StatusTextプロパティに進行状況テキストを渡している。
・Task.Run()内の最後処理に、processingDialog.Invoke()を通して processingDialog.Close()を実行することで、ファイルインポート/DB処理が終わったら ProcessingDialog処理中ダイアログを閉じている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
private async void btnCsvFileImport_Click(object sender, EventArgs e) { try { if (MessageBox.Show("CSVファイルのインポートを開始しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) return; var processingDialog = new ProcessingDialog(); var task = Task.Run(async () => { try { var csvFiles = Directory.GetFiles(txtWorkFolderPath.Text, "*.csv"); int fileCnt = 0; foreach (var csvFilePath in csvFiles) { fileCnt++; // ファイルインポート処理、DB処理・・・ // processingDialog.Invoke(new Action(() => { processingDialog.StatusText = $"処理済ファイル {fileCnt} / 全ファイル {csvFiles.Length}";// UIを更新 })); } } finally { processingDialog.Invoke(new Action(() => { processingDialog.Close(); // バックグラウンド処理が完了したら閉じる })); } }); processingDialog.ShowDialog(); // 処理中ダイアログを表示 await task; // バックグラウンド処理が完了するまで待機 MessageBox.Show("CSVファイルのインポートが完了しました。", "完了", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"{ex.Message}"); } } |
「CSVファイルをインポート」ボタンのクリックイベント処理から、ファイル操作/DB操作の細かい処理を除外していない、元のソースコードは下記になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
private async void btnCsvFileImport_Click(object sender, EventArgs e) { try { if (MessageBox.Show("CSVファイルのインポートを開始しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) return; var processingDialog = new ProcessingDialog(); var task = Task.Run(async () => { try { using (var conn = new SqlConnection(txtConnectionString.Text)) { await conn.OpenAsync(); // データは消す using (var cmd = new SqlCommand("TRUNCATE TABLE tImport_MeasurementData", conn)) { await cmd?.ExecuteNonQueryAsync(); } _DataTable.Clear(); var csvFiles = Directory.GetFiles(txtWorkFolderPath.Text, "*.csv"); int fileCnt = 0; foreach (var csvFilePath in csvFiles) { fileCnt++; using (var sr = new StreamReader(csvFilePath)) { await sr.ReadLineAsync();// ヘッダ行を読み飛ばす string? line; while ((line = await sr.ReadLineAsync()) != null) { var fields = line.Split(','); // CSVの行をコンマで分割 fields[0] = fields[0].PadLeft(8, '0'); _DataTable.Rows.Add(fields); // _DataTableに行を追加 } } // 100ファイルごとまたは最後のファイルの場合、バッチ処理を実行 if (fileCnt % 10 == 0 || fileCnt == csvFiles.Length) { using (var bulkCopy = new SqlBulkCopy(conn)) { bulkCopy.DestinationTableName = "tImport_MeasurementData"; // 列のマッピングを設定 bulkCopy.ColumnMappings.Add(0, "MeasurementStationCode"); bulkCopy.ColumnMappings.Add(1, "ImportDate"); bulkCopy.ColumnMappings.Add(2, "ImportTime"); bulkCopy.ColumnMappings.Add(3, "SO2_ppm"); bulkCopy.ColumnMappings.Add(4, "NO_ppm"); bulkCopy.ColumnMappings.Add(5, "NO2_ppm"); bulkCopy.ColumnMappings.Add(6, "NOx_ppm"); bulkCopy.ColumnMappings.Add(7, "CO_ppm"); bulkCopy.ColumnMappings.Add(8, "Ox_ppm"); bulkCopy.ColumnMappings.Add(9, "NMHC_ppmC"); bulkCopy.ColumnMappings.Add(10, "CH4_ppmC"); bulkCopy.ColumnMappings.Add(11, "THC_ppmC"); bulkCopy.ColumnMappings.Add(12, "SPM_mg_per_m3"); bulkCopy.ColumnMappings.Add(13, "PM25_ug_per_m3"); bulkCopy.ColumnMappings.Add(14, "SP_mg_per_m3"); bulkCopy.ColumnMappings.Add(15, "WD_16Dir"); bulkCopy.ColumnMappings.Add(16, "WS_m_per_s"); bulkCopy.ColumnMappings.Add(17, "TEMP_C"); bulkCopy.ColumnMappings.Add(18, "HUM_percent"); await bulkCopy.WriteToServerAsync(_DataTable); _DataTable.Clear(); } processingDialog.Invoke(new Action(() => { processingDialog.StatusText = $"処理済ファイル {fileCnt} / 全ファイル {csvFiles.Length}";// UIを更新 })); } } } } finally { processingDialog.Invoke(new Action(() => { processingDialog.Close(); // バックグラウンド処理が完了したら閉じる })); } }); processingDialog.ShowDialog(); // 処理中ダイアログを表示 await task; // バックグラウンド処理が完了するまで待機 MessageBox.Show("CSVファイルのインポートが完了しました。", "完了", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"{ex.Message}"); } } |
コメント