2013年09月12日

WPFサンプル:ListBoxで複数項目を選択可にし選択状態をモデル側に通知する

   このエントリーをはてなブックマークに追加 Clip to Evernote
ListBoxで複数項目を選択可にし選択状態をモデル側に通知する次のようなアプリケーションの作成を考えてみましょう。

2つのListBoxを用意し、左側には、いくつかの項目が表示されています。
この項目を選択(複数選択可)すると、選択された項目が、右側のListBoxに表示されるようにします。
すべての項目が選択されたら、左側のListBoxには新たな項目をひとつ追加します(変な要件ですが)

MultiSelectListBox1

MultiSelectListBox2

ListBoxには、SelectedItemsというプロパティがありますから、これをバインドすれば、比較的簡単に実現できますが、ここでは、モデル側でどの項目が選択されたのかを把握できるようにし、このモデル側のデータを右側のListBoxにバインドしたいと思います。

まずは、モデル側のクラスの定義です。
ListBoxに表示される項目である、MyDataクラスを定義します。

public class MyData {
    public string Name { get; set; }

    public bool? IsSelected { get; set; }
}

Nameプロパティが、ListBoxに表示される項目で、 IsSelectedプロパティが選択されているかどうかを把握するプロパティです。
このクラスについては、INotifyPropertyChanged は、実装しません。
なくても、上記要求は満たせます。

次に、このMyDateのコレクションクラスを定義します。

public class MyDataList : ObservableCollection<MyData> {
    public MyDataList() {
        AddNewItem();
        AddNewItem();
        AddNewItem();
        AddNewItem();
    }

    private int lastYear = 2010;

    public MyData AddNewItem() {
        var data = new MyData { Name = string.Format("{0}年", lastYear) };
        this.Add(data);
        lastYear++;
        return data;
    }

    public List<MyData> SelectedItems { get; private set; }

    public void UpdateSelectedItems() {
        var q = Items.Where(x => x.IsSelected.HasValue && (x.IsSelected.Value == true));
        SelectedItems = new List<MyData>(q);
        this.OnPropertyChanged(new PropertyChangedEventArgs("SelectedItems"));
    }
}

こちらは、ObservableCollection を継承したクラスとします、 こうすることで、コレクションへの追加、削除の変更が通知されるようになります。

SelectedItemsプロパティは、選択されている項目一覧が格納されているコレクションです。

UpdateSelectedItemsメソッドで、SelectedItemsを最新の状態に更新し、継承元で定義されている OnPropertyChangedメソッドで、SelectedItemsプロパティが更新されたことを通知します。

これで、モデル側の準備ができましたので、XAMLを定義します。

<Window x:Class="MultiSelectionListBoxSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:MultiSelectionListBoxSample"
        Title="MainWindow" Height="220" Width="300">
    <Window.DataContext>
        <my:MyDataList />
    </Window.DataContext>
    <Window.Resources>
        <Style x:Key="listBoxItemStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ListBox Height="140" HorizontalAlignment="Center"
                 Name="listBox" VerticalAlignment="Center" Width="120"
                 SelectionMode="Multiple"
                 ItemsSource="{Binding}"  DisplayMemberPath="Name"
                 ItemContainerStyle="{StaticResource listBoxItemStyle}"
                 SelectionChanged="listBox1_SelectionChanged" />
        <ListBox Grid.Column="1"
                 Height="140" HorizontalAlignment="Center"
                 Name="listBox2" VerticalAlignment="Center" Width="116"
                 ItemsSource="{Binding SelectedItems}" DisplayMemberPath="Name"  />
    </Grid>
</Window>

左側のListBoxでは、ItemContainerStyleを設定し、その中で、ListBoxの各項目を表すクラスであるListBoxItemの IsSelectedプロパティと、モデル側の MyData の IsSelectedプロパティをバインドさせています。
こうすることで、選択状態が変わるたびに、MyDataオブジェクトのIsSelectedプロパティも 同期することになります。

右側のListBoxは、MyDataListオブジェクトのSelectedItemsプロパティとバインドさせています。

この状態で、プログラムを実行した場合、左側のListBoxの選択状態を変更しても、 右側のListBoxは何も変化しません。
MyDataListのUpdateSelectedItemsメソッドがどこでも呼び出されていませんので、 SelectedItemsプロパティの変更通知がされないからです。

この部分は、C#のコードビハインド側で記述することにします。

SelectionChangedイベントハンドラで、UpdateSelectedItems を呼び出すようにします。
また、最後の要件である「すべての項目が選択されたら、左側のListBoxには新たな項目をひとつ追加します」 のコードもここに記述します。

以下、C#のコードです。

using System.Windows;
using System.Windows.Controls;

namespace MultiSelectionListBoxSample {

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

        private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            var myList = (this.DataContext as MyDataList);
            myList.UpdateSelectedItems();
            if (myList.SelectedItems.Count == myList.Count) {
                myList.AddNewItem();
            }
        }
    }
}    



WPFサンプル・目次


 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/52333865