20
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ListBoxのMVVMを意識した使い方

Posted at

#はじめに

 MVVMデザインパターンでListBoxを操作する場合、ListBoxSelectedIndexSelectedItemを操作する事例が多いですが、ViewとViewModelの関係をもっと疎結合にし、簡潔に扱えないかなという事で上記2プロパティを使わない方法を紹介します。

 今回もMVVMフレームワークにはLivetを使います。

#View側のコード

MainWinodw.xaml
<Grid>
    <ListBox ItemsSource="{Binding PersonListView}" />
</Grid>

ItemsSourceを指定するだけです

#ViewModel側のコード
##選択された項目を得る
 ListBoxのバインドターゲットとしては、ListCollectionView型を指定します。ListCollectionViewは、System.Windows.Dataネームスペースにあります。

Livetのスニペットである「lpropn」を利用して作成します。

MainWindowViewModel.cs
#region PersonListView 個人一覧(ListBox)
private ListCollectionView _PersonListView ;
/// <summary>
/// 個人一覧(ListBox)
/// </summary>
public ListCollectionView PersonListView 
{
    get
    { return _PersonListView ; }
    set
    { 
        if (_PersonListView  == value)
            return;
        _PersonListView  = value;
        RaisePropertyChanged();
    }
}

/// <summary>
/// 個人リスト
/// </summary>
private List<string> PersonList;
#endregion

 ListCollectionViewはこれ自体にListBoxの中身のリストは含まれていません。リストの実体を並べ替えて表示したり、フィルターで絞ったりする事ができます。

 PersonListListBoxに表示するリストの実体です。

 さて、コード部を以下のように実装します。肝の部分のみの説明なので、エラー処理、解放処理などは省略しています。

MainWindowViewModel.cs
/// <summary>
/// 初期化
/// </summary>
public void Initialize()
{
    this.PersonList = new List<string>() 
    {
        "佐藤","高橋","渡辺","山本","小林"
    };

    this.PersonListView = new ListCollectionView(this.PersonList);
    this.PersonListView.CurrentChanged += PersonListView_CurrentChanged;
}

/// <summary>
/// リストの位置が変わった
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void PersonListView_CurrentChanged(object sender, EventArgs e)
{
    var lv = sender as ICollectionView;
    if (lv.CurrentPosition < 0)
    {
        System.Diagnostics.Trace.WriteLine("選択無し");
        return;
    }
    var name = lv.CurrentItem as string;
    System.Diagnostics.Trace.WriteLine(
        string.Format("CurrentChanged:位置={0},名前={1}",
        lv.CurrentPosition,
        name));
}

 Initalize()の中で、PersonListを初期化した後、ListCollectionViewを作成し、PersonListを渡しています。
 その後、PersonListViewCurrentChangedにイベントハンドラを設定しています。
 ListBoxへバインドを行うと、リストボックスから発生するIndexの変更がこのハンドラへ伝わります。
 CurrentChangedイベントのライフ管理を十分に行えば、ListCollectionViewを使った方が楽だと思います。

##ViewModel側から項目を選択する
 ViewModel側からの項目選択は簡単です。

this.PersonListView.MoveCurrentToPosition(index);

indexは先頭を0とした番号です
またこのようにインスタンスを指定した設定ができます。

var item = this.PersonList[2];
this.PersonListView.MoveCurrentTo(item);

他にも有用なメソッドで移動ができます。
ICollectionView のメソッドを調べてみて下さい。

#さいごに
 使う上での注意事項として、ViewModel側でListCollectionViewのインスタンスを作成した直後はCurrentPositionが-1ですが、ListBoxへバインドすると、自動的にCurrentPositionが0(先頭に位置付く)になります。
 UnitTestではこの動作を分かってないと、少し痛い目に遭います(笑)。
 同時に、イベントハンドラのライフ管理さえしっかりと行えば、かなり使い物になると思います。

20
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?