テキストファイルを読み込み、Excelなどの外部アプリに自動入力するフォームアプリを作成する

## 最初に
テキストファイルの数値データを読み込み、Excelなど他のソフトに自動で入力してくれるプログラムを組んでみました。
![最初.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/f4f31ef6-876a-b5d2-a72a-72ff04f01fea.png)

## 今回の目的
VisualStudio2019のWindowsフォームアプリケーション(.NET Framework)を作成して、あるフォルダーの中のテキストファイルを読み込み、Excelなどの外部アプリに自動入力するフォームアプリを作成します。
今回はテキストファイルから「X」、「Y」を判別し文字列から数字を抽出後、Excelに「X」の数字は左の列に、「Y」の数字は右の列に入力します。手動でExcelに数字を入力する場合、操作は「Xの数字」→「TAB」→「Yの数字」→「ENTER」→「次のXの数字」→...と入力していくのですが、この操作を自動でするプログラムを作ります。
![メモ帳excel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/098487e0-e228-3e44-e851-38b8117f4d99.png)

### 完成したフォームアプリはこんな感じ
フォルダーを参照、ファイル名を入力して、入力開始ボタンを押すとテキストファイルの数字をExcelに自動で打ち込んでくれます。


![フォームアプリ完成系.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/0a751b4a-0e60-9b72-1e5a-149eb178788f.png)

## 開発環境
Visual Studio 2019

## プロジェクトの作成
Visual Studio 2019を開き、新しいプロジェクトの作成します。
![プロジェクト作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/b3bc5f10-2865-e728-1954-03ff476a2a26.png)

Windowsフォームアプリケーション(.NET Framework)を選択して次へ。
![プロジェクト作成2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/0c0b9028-b2fc-61e6-5851-2ef20c0fca19.png)

プロジェクト名を入力後、プロジェクトの保存場所を任意の場所にして、作成をクリック。
プロジェクト名は自分のわかりやすい名前であればなんでもいいです。
![プロジェクト作成3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/d92f8348-1b46-a411-52e7-c6f0f5c5f55d.png)

## フォームの作成
ボタンやテキストボックス、ラベルなどを配置します。
### ・画面の設定
画面のサイズは赤丸のところで調整できます。右下のプロパティ画面でも調整でき、細かい調整ができます。
ウインドウの名前がForm1になっていますが、変更したい場合はこれも右下のプロパティ画面で変更できます。Textの欄がForm1となっているので変えたい名前に変更しましょう。
![UI作成1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/fa8097ce-8fbf-c39b-0e1d-72a547cfed93.png)

### ・ボタンなどの配置
画面左にツールボックスがあるので使いたいものをドラックアンドドロップすることで追加できます。Buttonを追加します。
ツールボックスがない場合は上のバーの「表示」にツールボックスがあるのでそこをクリックすると出てきます。
![UI作成2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/b65eaedf-a68c-0da4-8c92-88da0e62ad1e.png)

プロパティで背景の色やフォントのサイズ、字体、ボタンのテキストなど設定できます。
![UI作成4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/bce8a5e6-41c4-08ed-c0b9-5a48f643a98a.png)

同様にしてButton、TextBox、Labelを配置します。ツールボックスの検索機能を使うと便利です。
こんな感じに配置↓
![UI作成5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/4d9c070e-6f0e-c73d-888c-f95502f97e94.png)

### ・フォルダーの参照
「フォルダの選択」ダイアログを表示するのにFolderBrowserDialogを追加します。これで下準備はOKです。

![UI作成6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/6c1b8321-c9f5-c636-35aa-2bd27297954f.png)


## コードの記述
ボタンをダブルクリックします。
![プログラム1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/b89d374c-7bf0-8539-7a85-6b55ec25de99.png)

コード編集画面が開き、Clickイベントハンドラ(button1_Click メソッド)が作成されます。
このbutton1_Click メソッド内にボタンクリック時に実行するコードを記述します。

```c#:Form1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TextFileInputOutput
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

        }
    }
}
};
```

フォルダーの参照ボタンのClickイベントハンドラも同様に作成しましょう。

#### ・イベントハンドラメソッドの名前について
メソッドには任意の名前を付けることができます。
例えば、今回のフォルダーの参照のbutton2というボタンのClickイベントのイベントハンドラーメソッドには button2_Click()という名前が付けられます。詳しくは[こちら](https://docs.microsoft.com/ja-JP/visualstudio/ide/step-6-name-your-button-controls?view=vs-2022)を参考にしてください。


![プログラム2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/b0463649-73ad-1817-9d44-c73c34e94ffd.png)

また、イベントハンドラーメソッドを作成した後にやっぱりそのメソッドを削除したいと思ったときは、フォームデザインの画面に戻り、イベントのアクションのClickを消してから削除するようにしてください。先にコードに記述してあるメソッドを消してしまうとエラーが出てしまいます。

![プログラム3_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/9f939d8c-1c91-45c3-781c-365b0355ecfb.png)

↓イベントのアクションのClickの名前を削除してからコードのほうを消す。（button2_Clickメソッドを削除したい場合）
![プログラム4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/845299fe-798c-96b5-c94b-212d04c2fb00.png)


### ・usingディレクティブ追加
まず、次のusingディレクティブを使うので追加しておきましょう。
using System.Threading;
using System.IO;


```c#:Form1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.IO;

namespace TextFileInputOutput
{//省略
}

```

### ・フォルダーの参照のコードの記述
テキストボックスにフォルダー参照ダイアログボックスで選択したフォルダのパスを表示します。

```c#:Form1
private void button2_Click(object sender, EventArgs e)
        {
            DialogResult dr = folderBrowserDialog1.ShowDialog();
            if (dr == System.Windows.Forms.DialogResult.OK)
            {
                textBox1.Text = folderBrowserDialog1.SelectedPath;
            }
        }
```

下記のコードでフォルダー参照ダイアログを表示します。

```c#
DialogResult dr = folderBrowserDialog1.ShowDialog();     
```

ダイアログボックスが[OK]ボタンで閉じられた場合、テキストボックスにフォルダー参照ダイアログボックスで選択したフォルダのパスを表示します。

```c#
if (dr == System.Windows.Forms.DialogResult.OK)
            {
                textBox1.Text = folderBrowserDialog1.SelectedPath;
            }
```

### ・自動入力のコードの記述



 ```c#:Form1
private void button1_Click(object sender, EventArgs e)
        {
                Microsoft.VisualBasic.Interaction.AppActivate("Excel");
                string filePass = $@"{textBox1.Text}\{textBox2.Text}.txt";
                var lines = File.ReadAllLines(filePass);
                Thread.Sleep(1000);
                foreach (var line in lines)
                {
                    if (line.Contains("X"))
                    {
                        string xNum = line.Replace("X=", "").Replace(" ", "");

                        SendKeys.Send(xNum);
                        SendKeys.Send("{TAB}");
                    }
                    if (line.Contains("Y"))
                    {
                        string yNum = line.Replace("Y=", "").Replace(" ", "");
                        SendKeys.Send(yNum);
                        SendKeys.Send("{ENTER}");
                    }
                    Thread.Sleep(100);
                }
        }
```

「型または名前空間の名前'Interaction'が名前空間'Microsoft.VisualBasic'に存在しません。」と出てきた場合は、ソリューションエクスプローラーから参照の追加でMicrosoft.VisualBasicにチェックマークを入れます。
![プログラム5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/93abb855-2374-f933-9261-34d25c2175bc.png)

参照を右クリックし、「参照の追加」
![プログラム6-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/69f87801-8915-b463-d4e5-e880abe67f27.png)

Microsoft.VisualBasicにチェックを入れてOK。
![プログラム6-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/a5b63560-49e3-4a94-c1f9-170aebc45731.png)

### コードの解説

 ```c#
Microsoft.VisualBasic.Interaction.AppActivate("Excel");
 ```
アクティブにしたいウィンドウのタイトルを指定して呼び出し、そのウィンドウをアクティブ状態にします。（今回はExcel）

 ```c#
string filePass = $@"{textBox1.Text}\{textBox2.Text}.txt";
```
テキストボックス（textBox1、textBox2）の値を取得し、文字列として定義します。
![テキストボックス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/4a77e024-ab46-268a-3e2c-4a213ddd6f52.png)

```c#
var lines = File.ReadAllLines(filePass);
```

System.IO.Fileクラスの[ReadAllLinesメソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.io.file.readalllines?view=net-5.0)を使います。
引数に定義した文字列を渡し、そのテキストファイルを開き、ファイルのすべての行を読み取った後、ファイルを閉じます。



```c#
foreach (var line in lines)
                {
                    if (line.Contains("X"))
                    {
                        string xNum = line.Replace("X=", "").Replace(" ", "");

                        SendKeys.Send(xNum);
                        SendKeys.Send("{TAB}");
                    }
                    if (line.Contains("Y"))
                    {
                        string yNum = line.Replace("Y=", "").Replace(" ", "");
                        SendKeys.Send(yNum);
                        SendKeys.Send("{ENTER}");
                    }
                    Thread.Sleep(100);
                }
```

foreach文で読み込んだ文字列を１つずつ取り出し、if文でその文字列に「X」が含まれていた場合、「Y」が含まれていた場合で処理を分け、この処理をある文字列の分繰り返します。
if文の中の処理では、まず[Replaceメソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.string.replace?view=net-5.0)で文字列を置換し、文字列を数字のみにします。次にその数字をSendKeys.Send() メソッドでキーボードから入力したかのように、1つ以上のキーボード操作をアクティブなウィンドウに送信します。その後、「X」の場合はTABの入力操作、「Y」の場合はENTERの入力操作を送信します。
最後に[Thread.Sleepメソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.thread.sleep?view=net-5.0)で一定時間待機するために指定した時間の長さ（単位はミリ秒）、現在のスレッドを中断します。
今回は100ミリ秒(0.1秒)ごとに処理を繰り返すことになります。

キー操作には他にも様々な操作があるため、他に加えたいキー操作がある場合は[こちら](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/sendkeys-statement)を参考にしてみてください。

#### ・try～catchで例外処理を行う。

```c#
private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                Microsoft.VisualBasic.Interaction.AppActivate("Excel");
                //省略
                Thread.Sleep(100);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }
```
try～catchで例外処理を行います。入力開始ボタン(button1)を押したときに、処理に問題が発生すると、エラーのメッセージボックスが開きます。



例1）Excelが起動していない時
![エラーメッセージ１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/47817db3-863b-e705-08fc-15e83e5f26f4.png)

例2）ファイルの名前が見つからない時
![エラーメッセージ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/86fd57e4-e764-6a65-8957-5f1125cc9269.png)



## 全コード
usingディレクティブの使わないものは消していいです。

```c#:Form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.IO;

namespace TextFileInputOutput
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                Microsoft.VisualBasic.Interaction.AppActivate("Excel");
                string filePass = $@"{textBox1.Text}\{textBox2.Text}.txt";
                var lines = File.ReadAllLines(filePass);
                Thread.Sleep(1000);
                foreach (var line in lines)
                {
                    if (line.Contains("X"))
                    {
                        string xNum = line.Replace("X=", "").Replace(" ", "");

                        SendKeys.Send(xNum);
                        SendKeys.Send("{TAB}");
                    }
                    if (line.Contains("Y"))
                    {
                        string yNum = line.Replace("Y=", "").Replace(" ", "");
                        SendKeys.Send(yNum);
                        SendKeys.Send("{ENTER}");
                    }
                    Thread.Sleep(100);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }

        private void button2_Click(object sender, EventArgs e)
        {
            DialogResult dr = folderBrowserDialog1.ShowDialog();
            if (dr == System.Windows.Forms.DialogResult.OK)
            {
                textBox1.Text = folderBrowserDialog1.SelectedPath;
            }
        }
    }
}
```


## 実行する

![デバックなしで開始.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477630/e924dcf1-d7ed-459f-a17a-02229c059b0d.png)
フォルダーを参照(テキストボックスに直接フォルダーの場所を入力でもOK)、ファイル名を入力して、入力開始ボタンを押すとテキストファイルの数字をExcelに自動で打ち込んでくれます。


## <font color="Red">追記(2023/10/5)</font>
非同期処理の導入や処理の中断機能の追加などをしました。
→[テキストファイルを読み込み、外部アプリに自動入力するフォームアプリの機能の追加と変更](https://qiita.com/Miyakepp_098/items/dffa3fb7d1a446b102b7)

## 参考文献
・ [ボタン コントロールの名前を設定する](https://docs.microsoft.com/ja-JP/visualstudio/ide/step-6-name-your-button-controls?view=vs-2022)
・ [File.ReadAllLines メソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.io.file.readalllines?view=net-5.0)
・ [SendKeys](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/sendkeys-statement)
・ [Thread.Sleep メソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.thread.sleep?view=net-5.0)
・ [Application.SendKeys メソッド ](https://docs.microsoft.com/ja-jp/office/vba/api/excel.application.sendkeys)
・ [.NET TIPS［フォルダの参照］ダイアログを使用するには？](https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/056folderdlg/folderdlg.html)
・ [外部アプリケーションのウィンドウをアクティブにする](https://dobon.net/vb/dotnet/process/appactivate.html)