CodeZine(コードジン)

特集ページ一覧

Collection 2題:「WPFにバインドできる辞書」と「重複を許す検索set」

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2012/12/26 11:22

 アプリケーション試作中、"あったらいいな"な2つのコレクション:「WPFにバインドできる辞書」と「重複を許す検索set」を、既存クラスの拡張と拡張メソッドによる対IListアルゴリズムの追加で実現しました。

目次

はじめに

 僕のお仕事は主に開発屋さんたちの後方支援なので、エンドユーザーさんとは直接の関わりが浅く、GUIアプリを書く機会は多くありません。とはいえ開発屋さんからのリクエストで、ちょっとしたGUIツールをこしらえることがたまぁにあります。

 長いことその多くはWindows Formsを使っていたのですが、ようやく(いまさら?)近頃WPFを使うようになりました。お客様の目には触れないアプリケーションだし、なにしろ開発屋さんは僕も含めて基本ワガママですから、使い勝手のよいようにUIの仕様をコロコロ変えてきます。そんな無茶ブリに素早く対応せにゃならんので、View(見てくれ)とModel(本体)とをきっちり分離することが望まれます。

 WPFの強力かつ柔軟なデータ・バインディングのおかげでViewとModelの分離がとても楽になりました。WPFに味をしめた僕は、頼まれたGUIアプリはぜーんぶWPFで書いてやろうと手駒を揃えることにしました。

 そんなわけで、僕が書くアプリのユーザは一般のお客様じゃなく身内の開発屋さんですから、凝ったUIはまずもって不要です。TextBox,ButtonそしてListBox/ListViewあたりが使えれば、あらかたのツールは作れます。ListBoxの小さなサンプルを書いてみますね。

 Viewはこんな感じ、いたってシンプルです。「文字列を追加する」と「選択項目を削除する」のたった2つの機能。XAMLをお見せしましょうか。

list01 MainWindows.xaml
<Window x:Class="ListBox_binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ListBox binding" Height="298" Width="309">
    <Grid>
        <ListBox Height="165" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox" VerticalAlignment="Top" Width="255" ItemsSource="{Binding Path=Items}" />
        <TextBox Height="28" HorizontalAlignment="Left" Margin="12,183,0,0" Name="textBox" VerticalAlignment="Top" Width="176" />
        <Button Content="追加" Height="26" HorizontalAlignment="Left" Margin="219,183,0,0" VerticalAlignment="Top" Width="48" Click="add_Click" />
        <Label Content="を" Height="28" HorizontalAlignment="Left" Margin="194,185,0,0" VerticalAlignment="Top" />
        <Label Content="選択した項目を" Height="28" HorizontalAlignment="Left" Margin="127,219,0,0" VerticalAlignment="Top" />
        <Button Content="削除" Height="26" HorizontalAlignment="Left" Margin="219,0,0,12" VerticalAlignment="Bottom" Width="48" Click="remove_Click" />
    </Grid>
</Window>

 <ListBox>に並ぶ属性のItemsSource="{Binding Path=Items}"でItemsプロパティにバインドしています。

 このViewに反映されるデータを提供するModelであるBindDataがコチラ。

list02 BindData.cs
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace ListBox_binding {

  public class BindData {
    public void add(string str) { data_.Add(str); }
    public void remove_at(int n) { if ( n >= 0 && n < data_.Count ) data_.RemoveAt(n); }
    public ObservableCollection<string> Items { get { return data_; } }

    private ObservableCollection<string> data_ = new ObservableCollection<string>();
  }

}

 XAML側とバインドしたプロパティ:Itemsの型はObservableCollection<string>です。ObservableCollection<T>は、可変長配列List<T>のバリエーションで、要素の追加/変更/削除に応じて画面を更新してくれます。

 Viewに起こるイベント:追加/削除ボタンのクリックに反応するイベント・ハンドラはコード・ビハインドに置きます。

list03 MainWindows.xaml.cs
using System.Windows;
using System.Collections;

namespace ListBox_binding {

  public partial class MainWindow : Window {
    public MainWindow() {
      InitializeComponent();
      DataContext = data_;
    }

    private void add_Click(object sender, RoutedEventArgs e) {
      data_.add(textBox.Text);
    }

    private void remove_Click(object sender, RoutedEventArgs e) {
      data_.remove_at(listBox.SelectedIndex);
    }

    private BindData data_ = new BindData();
  }

}

 ModelであるBindDataのインスタンスを1つ用意し、コンストラクト時にDataContextに与えています。追加/削除ボタンのハンドラではそれぞれModelのadd/remove_atを呼ぶだけ。これだけで動いてくれます。楽ですねー♪

 ListViewで表を描くのもListBoxと大差ありません。

 表のカラムごとに表示するデータをバインドします。名前(Name)と連絡先(Contact)の表であれば、1つのレコードを表現するクラス:Entryは

list04 Entry.cs
namespace ListView_binding {
  public class Entry {
    public Entry(string k, string v) { Name = k; Contact = v; }
    public string Name { get; set; }
    public string Contact { get; set; }
  }
}

 XAML上のListViewに対応するEntryの集合はOvservableCollection<Entry>となります。ListViewのItemsSource属性でEntry集合とバインドし、加えて各カラムにEntryのプロパティ:Name,Contactをバインドします。

list05 MainWindows.xaml
<Window x:Class="ListView_binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ListView binding" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="382" SizeToContent="WidthAndHeight" d:DesignWidth="444">
    <Grid>
        <ListView ItemsSource="{Binding Path=Items}" Height="212" HorizontalAlignment="Left" Margin="16,16,0,0" Name="lstEntry" VerticalAlignment="Top" Width="360" >
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="名前" DisplayMemberBinding="{Binding Path=Name}" />
                    <GridViewColumn Header="連絡先" DisplayMemberBinding="{Binding Path=Contact}" />
                </GridView>
            </ListView.View>
        </ListView>
        <TextBox Height="24" HorizontalAlignment="Left" Margin="56,244,0,0" Name="txtName" VerticalAlignment="Top" Width="80" />
        <TextBox Height="24" HorizontalAlignment="Left" Margin="190,245,0,0" Name="txtContact" VerticalAlignment="Top" Width="79" />
        <Button Content="追加" Height="23" HorizontalAlignment="Left" Margin="301,245,0,0" VerticalAlignment="Top" Width="75" Click="add_Click" />
        <Label Content="名前" Height="24" HorizontalAlignment="Left" Margin="16,246,0,0" Name="label1" VerticalAlignment="Top" />
        <Label Content="連絡先" Height="24" HorizontalAlignment="Left" Margin="142,246,0,0" Name="label2" VerticalAlignment="Top" />
        <Label Content="を" Height="24" HorizontalAlignment="Left"  Margin="275,244,0,0" Name="label3" VerticalAlignment="Top" />
        <Button Content="削除" Height="23" HorizontalAlignment="Left" Margin="301,282,0,0" VerticalAlignment="Top" Width="75" Click="remove_Click" />
        <Label Content="選択した項目を" Height="24" HorizontalAlignment="Left" IsEnabled="True" Margin="208,281,0,0" Name="label4" VerticalAlignment="Top" />
    </Grid>
</Window>

 Model.コード・ビハインドはそれぞれ以下のとおり。

list06 BindData.cs
using System.Collections.ObjectModel;

namespace ListView_binding {

  public class BindData {
    public void addPair(string k, string v) { data_.Add(new Entry(k,v)); }
    public void remove_at(int n) { if ( n >= 0 && n < data_.Count ) data_.RemoveAt(n); }
    public ObservableCollection<Entry> Items { get { return data_; } }

    private ObservableCollection<Entry> data_= new ObservableCollection<Entry>();
  }

}
list07 MainWindows.xaml.cs
using System.Windows;

namespace ListBox_binding {

  public partial class MainWindow : Window {
    public MainWindow() {
      InitializeComponent();
      DataContext = data_;
    }

    private void add_Click(object sender, RoutedEventArgs e) {
      data_.add(textBox.Text);
    }

    private void remove_Click(object sender, RoutedEventArgs e) {
      data_.remove_at(listBox.SelectedIndex);
    }

    private BindData data_ = new BindData();
  }

}

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • επιστημη(エピステーメー)

    C++に首まで浸かったプログラマ。 Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"り わんくま同盟でたまにセッションスピーカやったり 中国茶淹れてにわか茶人を気取ってたり、 あと Facebook とか。 著書: - STL標準...

All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5