Windows GUIプログラミング入門13 リストビュー(2)、メニュー

# ■はじめに
前回に引き続き、ListViewを使います。
今回はCSVを読み込ませてListViewに表示します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/142879/9a6a9ccd-b62a-ce22-2380-58cabd4b4ea8.png" width="320" height="163"/>

キーワード：ListView, Menu, OpenFileDialog, CSV

[注意]
これまでの回で説明済みの操作方法等は、説明を省略したり簡略化している場合があります。

# ■開発環境
* Windows 10 (バージョン1703)
* Visual Studio Community 2017
* .NET Framework 4.x

# ■作ってみる

## ◇CSVデータ

郵便番号のCSVデータを`ListView`に表示するプログラムを作ります。
今回使うデータはこちら。
[郵便番号データダウンロード - 日本郵便](http://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html)
CSVデータの仕様はこちら。
[郵便番号データの説明 - 日本郵便](http://www.post.japanpost.jp/zipcode/dl/readme.html)

プロジェクトにクラスを追加します。
名前は`ZipRecord.cs`にします。
これがCSV1件分に対応します。

```CSharp:ZipRecord.cs
/// <summary>
/// 郵便番号データ
/// </summary>
public class ZipRecord
{
    /// <summary>
    /// 全国地方公共団体コード
    /// </summary>
    public string Code { get; set; }

    /// <summary>
    /// （旧）郵便番号
    /// </summary>
    public string ZipOld { get; set; }

    /// <summary>
    /// 郵便番号
    /// </summary>
    public string Zip { get; set; }

    /// <summary>
    /// 都道府県名カナ
    /// </summary>
    public string StateKana { get; set; }

    /// <summary>
    /// 市区町村名カナ
    /// </summary>
    public string CityKana { get; set; }

    /// <summary>
    /// 町域名カナ
    /// </summary>
    public string TownKana { get; set; }

    /// <summary>
    /// 都道府県名
    /// </summary>
    public string State { get; set; }

    /// <summary>
    /// 市区町村名
    /// </summary>
    public string City { get; set; }

    /// <summary>
    /// 町域名
    /// </summary>
    public string Town { get; set; }

    /// <summary>
    /// 一町域が二以上の郵便番号で表される場合の表示
    /// </summary>
    /// <remarks>
    /// 「1」は該当、「0」は該当せず
    /// </remarks>
    public string Flag1 { get; set; }

    /// <summary>
    /// 小字毎に番地が起番されている町域の表示
    /// </summary>
    /// <remarks>
    /// 「1」は該当、「0」は該当せず
    /// </remarks>
    public string Flag2 { get; set; }

    /// <summary>
    /// 丁目を有する町域の場合の表示
    /// </summary>
    /// <remarks>
    /// 「1」は該当、「0」は該当せず
    /// </remarks>
    public string Flag3 { get; set; }

    /// <summary>
    /// 一つの郵便番号で二以上の町域を表す場合の表示
    /// </summary>
    /// <remarks>
    /// 「1」は該当、「0」は該当せず
    /// </remarks>
    public string Flag4 { get; set; }

    /// <summary>
    /// 更新の表示
    /// </summary>
    /// <remarks>
    /// 「0」は変更なし、「1」は変更あり、「2」廃止（廃止データのみ使用）
    /// </remarks>
    public string Flag5 { get; set; }

    /// <summary>
    /// 変更理由
    /// </summary>
    /// <remarks>
    /// 「0」は変更なし、「1」市政・区政・町政・分区・政令指定都市施行、
    /// 「2」住居表示の実施、「3」区画整理、「4」郵便区調整等、
    /// 「5」訂正、「6」廃止（廃止データのみ使用）
    /// </remarks>
    public string Flag6 { get; set; }
}
```

## ◇メニュー・リストビュー

`Grid`を真ん中ぐらいで分割して上の段に`Menu`を、下の段に`ListView`を配置します。
![wbg13-01.png](https://qiita-image-store.s3.amazonaws.com/0/142879/4f676c92-6668-2e18-73fc-42ea3b4a3f91.png)


`Menu`のプロパティ、`Items`の「`...`」からコレクションエディターを表示します。
![wbg13-02.png](https://qiita-image-store.s3.amazonaws.com/0/142879/d89dfa22-7ad1-b3e6-2142-b3c702ae50bd.png)


`MenuItem`を2つ作ります。
1つ目の`Header`は「`ファイル(_F)`」、2つ目は「`編集(_E)`」にします。
![wbg13-03.png](https://qiita-image-store.s3.amazonaws.com/0/142879/459e80e4-5f85-dd04-b4a5-b75e4737601d.png)

追加後。
![wbg13-04.png](https://qiita-image-store.s3.amazonaws.com/0/142879/03f6a8a6-f747-1033-7e5c-ec4cf6213fde.png)

「ファイル」の`MenuItem`を選択し、`Items`のコレクションエディターを開きます。
![wbg13-05.png](https://qiita-image-store.s3.amazonaws.com/0/142879/8c6e1f27-faa9-8e90-f166-990e649da9bc.png)


`MenuItem`で「`開く(_O)...`」、
`Separator`、
`MenuItem`で「`終了(_X)`」を追加します。
![wbg13-06.png](https://qiita-image-store.s3.amazonaws.com/0/142879/a4752a15-b70a-07c0-6438-324d8dea306c.png)

追加後。
![wbg13-07.png](https://qiita-image-store.s3.amazonaws.com/0/142879/fdf6cf29-4ad1-dd6a-2c45-cd4e70f2c3bc.png)

「編集」メニューを選択し、今度は「MenuItemの追加」で項目を追加します。
![wbg13-08.png](https://qiita-image-store.s3.amazonaws.com/0/142879/7c2d4176-1eeb-fb76-cf99-4a3d9b5ecaa7.png)

`Header`は「`クリア(_C)`」にします。
![wbg13-09.png](https://qiita-image-store.s3.amazonaws.com/0/142879/3b4b2ffa-cde2-a775-e39d-868424c7f4d5.png)


「開く」メニューに名前を付けます。
![wbg13-10.png](https://qiita-image-store.s3.amazonaws.com/0/142879/4f10a863-7882-9927-1838-efe66290f018.png)


`openMenu`にします。
![wbg13-11.png](https://qiita-image-store.s3.amazonaws.com/0/142879/ba2b7b49-3d54-9417-93e6-ae6cf96b97f9.png)


同様に「終了」メニューは`exitMenu`に、「クリア」メニューは`clearMenu`にします。

```Xml
<MenuItem Header="ファイル(_F)">
    <MenuItem x:Name="openMenu" Header="開く(_O)..."/>
    <Separator/>
    <MenuItem x:Name="exitMenu" Header="終了(_X)"/>
</MenuItem>
<MenuItem Header="編集(_E)">
    <MenuItem x:Name="clearMenu" Header="クリア(_C)"/>
</MenuItem>
```

デザイナで`Grid`の上段を`Auto`に、下段を`Star`に設定（やり方は[第5回](http://qiita.com/Kosen-amai/items/f2451acb2addd221193f "Windows GUIプログラミング入門5 画面レイアウト(2)、画面遷移(1)")参照）し、
`Menu`と`ListView`のレイアウトを「すべてリセット」（やり方は[第7回](http://qiita.com/Kosen-amai/items/3ca00ebdbdee7776b67b "Windows GUIプログラミング入門7 システムカラー、画面レイアウト(3)")参照）します。

## ◇ウィンドウ

`Window`の`Title`を`郵便番号一覧`に、`ResizeMode`を`CanResizeWithGrip`に設定します。
![wbg13-12.png](https://qiita-image-store.s3.amazonaws.com/0/142879/91bb7865-f630-c9c1-c21b-0944d9bd7b86.png)


## ◇表示とデータの関連付け

`ListView`とCSVデータの入れ物を関連付けます。

```MainWindow.xaml.cs
public partial class MainWindow : Window
{
    // 郵便番号データのコレクション
    public System.Collections.ObjectModel.ObservableCollection<ZipRecord> ZipRecords { get; set; }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainWindow()
    {
        InitializeComponent();

        // 郵便番号データのコレクションを初期化
        this.ZipRecords = new System.Collections.ObjectModel.ObservableCollection<ZipRecord>();
        // 関連付け
        listView.DataContext = this.ZipRecords;
    }
}
```

Xamlで`ListView`を選択し、プロパティの`ItemsSource`横の`□`メニューから「データバインドの作成」を選択し、「バインドの種類」に「データコンテキスト」を選択します。

## ◇リストビューの列

`ListView`の列を設定します。

`ListView`の`Columns`のコレクションエディターを開きます。
![wbg13-13.png](https://qiita-image-store.s3.amazonaws.com/0/142879/15d3f9f9-b11d-12c8-586c-ecc331152198.png)


`GridViewColumns`を以下のように設定します。

| DisplayMemberBinding | Header |
|:--|:--|
| Zip  | 郵便番号  |
| State  | 都道府県名  |
| City  |  市区町村名 |
| CityKana  | 市区町村名カナ  |
| Town  | 町域名  |
| TownKana  | 町域名カナ  |

`DisplayMemberBinding`は横の`□`メニューから「データバインドの作成」を選択し、
![wbg13-14.png](https://qiita-image-store.s3.amazonaws.com/0/142879/e009f19f-b300-99e3-3800-7a405ba2b7cd.png)

「バインドの種類」に「データコンテキスト」を選択し、「パス」の欄に表示元データ（ZipRecordクラス）の項目名を入力します。
![wbg13-15.png](https://qiita-image-store.s3.amazonaws.com/0/142879/939ef493-4a11-9256-5866-c0964ff317e3.png)


ここまでで`ListView`は以下のようになります。

```Xml
<ListView x:Name="listView" Grid.Row="1" ItemsSource="{Binding Mode=OneWay}">
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Zip}" Header="郵便番号"/>
            <GridViewColumn DisplayMemberBinding="{Binding State}" Header="都道府県名"/>
            <GridViewColumn DisplayMemberBinding="{Binding City}" Header="市区町村名"/>
            <GridViewColumn DisplayMemberBinding="{Binding CityKana}" Header="市区町村名カナ"/>
            <GridViewColumn DisplayMemberBinding="{Binding Town}" Header="町域名"/>
            <GridViewColumn DisplayMemberBinding="{Binding TownKana}" Header="町域名カナ"/>
        </GridView>
    </ListView.View>
</ListView>
```

![wbg13-16.png](https://qiita-image-store.s3.amazonaws.com/0/142879/3423c136-8263-1f6a-69ec-1f11f2b3f944.png)


## ◇CSV読み込み

CSV読み込みの機能を使うために、参照の追加で`Microsoft.VisualBasic`を追加します。

コードビハインドにCSVを読み込むメソッドを作ります。

```CSharp:MainWindow.xaml.cs
/// <summary>
/// CSVファイル読み込み
/// </summary>
/// <param name="filePath">CSVファイルパス</param>
private void ReadCsv(string filePath)
{
    this.ZipRecords.Clear();

    var parser =
        new Microsoft.VisualBasic.FileIO.TextFieldParser(filePath, Encoding.Default);
    using (parser)
    {
        // 「,」区切りのデータとして処理する
        parser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
        parser.SetDelimiters(",");

        try
        {
            // ファイルの終わりまで繰り返す
            while (parser.EndOfData == false)
            {
                // 一行分読み込み
                string[] buf = parser.ReadFields();

                this.ZipRecords.Add(new ZipRecord
                {
                    Code = buf[0],
                    ZipOld = buf[1],
                    Zip = buf[2],
                    StateKana = buf[3],
                    CityKana = buf[4],
                    TownKana = buf[5],
                    State = buf[6],
                    City = buf[7],
                    Town = buf[8],
                    Flag1 = buf[9],
                    Flag2 = buf[10],
                    Flag3 = buf[11],
                    Flag4 = buf[12],
                    Flag5 = buf[13],
                    Flag6 = buf[14],
                });

            }
        }
        catch
        {
            throw new Exception("CSV読み込みでエラー！");
        }
    }
}
```


## ◇ファイルを開く

メニューから「開く」を選択した時の処理を作ります。
ドキュメントアウトラインで「openMenu」を選択し、画面の「開く」メニューをダブルクリックし、以下を入力します。
![wbg13-17.png](https://qiita-image-store.s3.amazonaws.com/0/142879/c9d6056e-2b26-0949-5ed6-94dd4dcad4e5.png)


```CSharp
/// <summary>
/// メニュー「開く」
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void openMenu_Click(object sender, RoutedEventArgs e)
{
    var dlg = new Microsoft.Win32.OpenFileDialog();

    // フィルタ設定
    dlg.Filter = "CSVファイル(*.csv)|*.csv|テキストファイル(*.txt)|*.txt|全てのファイル(*.*)|*.*";
    // フィルタの1番目(CSV)を選択状態にする
    dlg.FilterIndex = 1;

    // ファイルを開くダイアログ表示
    if (dlg.ShowDialog() == true)
    {
        this.IsEnabled = false;

        // CSV読み込み
        ReadCsv(dlg.FileName);

        this.IsEnabled = true;
    }
}
```

## ◇その他のメニュー処理
メニュー項目の「終了」、「クリア」も同様に処理を作ります。

```CSharp
/// <summary>
/// メニュー「終了」
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void exitMenu_Click(object sender, RoutedEventArgs e)
{
    // プログラム終了
    this.Close();
}

/// <summary>
/// メニュー「クリア」
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void clearMenu_Click(object sender, RoutedEventArgs e)
{
    // 一覧をクリア
    this.ZipRecords.Clear();
}
```

# ■動かしてみる

起動してみます。
![wbg13-18.png](https://qiita-image-store.s3.amazonaws.com/0/142879/10bb7ef7-1557-84f8-cb4f-8ff98fc97494.png)


メニューの「ファイル」 - 「開く」でファイル選択ダイアログを表示します。
拡張子のフィルタの初期選択値はCSVになっています。
![wbg13-19.png](https://qiita-image-store.s3.amazonaws.com/0/142879/5428c6bb-a60b-cd6f-572d-db9cfa0458ef.png)


[こちら](http://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html "郵便番号データダウンロード - 日本郵便")からダウンロードしたCSVを読み込ませてみます。
![wbg13-20.png](https://qiita-image-store.s3.amazonaws.com/0/142879/e4c7e3d7-6fc8-632c-6bd3-2ff69713151b.png)


表示されました。
![wbg13-21.png](https://qiita-image-store.s3.amazonaws.com/0/142879/9a6a9ccd-b62a-ce22-2380-58cabd4b4ea8.png)


メニューの「編集」 - 「クリア」を選択すると一覧がクリアされ、「ファイル」 - 「終了」を選択するとプログラムが終了します。

おしまい

※次回はこのプログラムをもう少し改善します。まだ捨てないでください。

---

[＜＜ 最初の記事](http://qiita.com/Kosen-amai/items/f9e3df2aa80363f5af5b "Windows GUIプログラミング入門1 メッセージボックス表示") 　 [＜ 前の記事](http://qiita.com/Kosen-amai/items/def339ea71cc69eeb9d0 "Windows GUIプログラミング入門12 リストビュー(1)") 　 [次の記事 ＞](http://qiita.com/Kosen-amai/items/58e597c98967ca34ecd1 "Windows GUIプログラミング入門14 処理中表示")