Observerデザインパターンと非同期処理の結合でロジック処理中に、リアルタイムに画面を更新する方法（C#）

# はじめに

皆さん、こんにちは。

株式会社コズムのNGUYEN（グエン）と申します。

.Net Windows Formアプリケーションを開発する際、ロジック処理中の画面にリアルタイムの更新が必要な時があります。
例えば、ファイルのダウンロードを行うような.Net Windows Formプログラムを作成する際、画面に更新対象データのダウンロード済みパーセントを表示する。などです。

今回、このサンプルの一つの実装方法としてC#でObserverデザインパターンと非同期処理を結合する方法を紹介していきます。

# **記事の対象者**


- .Net Windows Formアプリケーションの開発者
- Observerデザインパターンに対する知見がある方
- 非同期処理（Thread）に対する知見がある方

# 詳細

.Net Windows Formアプリケーションでは基本的に、画面の項目を表示する処理（例：Labelでテキストの内容を設定処理）がUIスレッドで実装されます。ロジック実行中は画面項目の表示が変更されず、ロジックの実行が完了した後に画面が変更されます。

例えば、以下のようなコードがあるとすると、

![Screenshot 2024-08-15 at 10.12.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/46cd121c-3419-d132-9ba4-57fabcb378c2.png)


btnExecuteLogic_Click関数の処理の実行中は lblStatusのテキストの更新はできず、ボタンの処理が終わった後に「処理完了」と表示することになります。

もしリアルタイムで画面更新したい場合、 ロジック処理のThreadと画面処理（UIスレッド）を分ける必要があります。

そこで、ロジック処理のThread中に画面更新通知できるようにするため、Observerデザインパターンを適用します。

# 実装詳細

.Net Windows Formアプリケーションを作成します。このアプリの処理は下記の通りです。

- 指定されたURL（仮のURL）のデータダンロード（zipファイル）
- ダウンロード中に、ステータスとダウンロード済みパーセント表示する画面

# 実装


## 1.クラス図

![Screenshot 2024-08-15 at 9.54.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/62222230-7ffb-aeb7-2daf-9343747040fc.png)

### 1-1 ApplicationManagerクラス

- Observers：オブザーバーオブジェクトリスト管理、ロジック処理で本クラス経由で画面更新通知
- AddObserverメソッド：オブザーバーオブジェクト追加
- NotifyStatusChanged：ロジック処理中で画面のステータス更新通知
- NotifyDownloadPercentageChanged：ロジック処理のダウンロード処理中で画面のダウンロード済みのパーセントを通知

### 1-2. IObserverインタフェース

オブザーバーオブジェクトインタフェース

- OnStatusChanged：ロジックのステータス更新通知のハンドル
- OnDownloadding：ダウンロード済みパーセント更新通知のハンドル

### 1-3. MainLogicクラス

メインロジック実行

- StartLogicAsync：非同期でロジック処理開始
- DoLogic：ロジックのメイン処理

### 1-4. MainFormフォーム

アプリケーションのメインフォーム

- IObserverインタフェースを実装

## 2.シーケンス図

![Screenshot 2024-08-14 at 21.00.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/0e53e149-c24d-b39c-97b8-f0c4abbcff36.png)

## 3.処理詳細

### 3-1 フォーム表示

- フォーム自体（IObserver実装）をApplicationManagerのオブザーバーリスト管理に登録

    ![Screenshot 2024-08-15 at 9.56.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/74873ad8-ff7e-e1f2-9017-41a13f7b3ecb.png)
    
- ApplicationManagerのオブザーバー登録の処理

    ![Screenshot 2024-08-15 at 9.57.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/f7588ae2-f747-f18b-7760-7e0c802599f6.png)
    

### 3-2. ロジック実行

- MainFormのボタンからMainLogicの非同期処理開始

    ![Screenshot 2024-08-15 at 9.58.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/03b9b61c-9b28-e381-bf66-3401cb0494e1.png)
    
- MainLogicの非同期処理
    - 非同期処理開始

    ![Screenshot 2024-08-15 at 10.00.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/2fc252cf-8b27-a5e9-e229-ca931fac1544.png)
    
    - メイン処理

    ![Screenshot 2024-08-15 at 10.07.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/49f2348e-8710-81e3-8d57-45472a689ad8.png)
    

### 3-3. 通知処理

- ステータス更新通知

    ![Screenshot 2024-08-15 at 10.21.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/9ac9e0d5-2782-0e4c-f608-d56406314ce0.png)
    

- ダウンロード進捗通知

    ![Screenshot 2024-08-15 at 10.21.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/fe793eec-0c24-a354-341c-9f2e8feb0997.png)
    

### 3-4. MainForm（オブザーバー）の通知ハンドル

- ステータス更新通知のハンドル

    ![Screenshot 2024-08-15 at 10.09.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/aea22555-389f-9a39-406c-8f47852c31a8.png)

    
- ダンロード進捗通知のハンドル

    ![Screenshot 2024-08-15 at 10.08.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862244/11d4a0de-4aaa-39cb-a857-e7c0c2598cd4.png)
    

# 終わりに


この記事で、Observerデザインパターンと非同期処理（スレッド）の結合によるロジック実行中でも画面更新できるようなサンプルを紹介しました。

簡単なサンプルですので、一つのForm（オブザーバー）を使っていますが、もし必要であればオブザーバーオブジェクトも追加できます。

その場合、ロジック処理で通知すると、複数の画面（またはコンポーネント）の表示を更新できます！

ご質問やご意見がございましたら、ぜひご気軽にコメントください！

それでは、快適な開発ライフをお過ごしください。