Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
56
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

WPF コンボボックスやリストボックスの ItemsSourceは何処に書くべきか?

WPFにて、コンボボックスやリストボックスの ItemsSourceは何処に書くべきでしょうか?

パッと思いついた四つのパターン

  1. XAML内で定義する
  2. Viewのコードビハインドで設定する
  3. ViewModelのプロパティとして実装する
  4. 独自のクラスを定義する

それぞれのパターンについて考えてみます。

1. XAML内で定義する

XAML内で、

<ListBox Name="list1"  >
    <ListBoxItem Content="item1"/>
    <ListBoxItem Content="item2"/>
    <ListBoxItem Content="item3"/>
</ListBox>

みたいな感じで定義します。
利点はXAML内で完結すること。難点は動的に定義できない(?)ことでしょうか?

2. Viewのコードビハインドで設定する

Viewのコードビハインドにて、ListBoxなどのコントロールのItemsSouceプロパティに直接データを設定します。

list2.ItemsSource = new string[] { "item1", "item2", "item3" };

利点はView内で完結すること。難点はViewにコードを書くのは一般的にお行儀がよろしくないとされています。

3. ViewModelのプロパティとして実装する

ViewModelにデータバインディング可能なプロパティとして公開します。

public List<string> ItemsSource
{
    get { return GetDataBindItem<List<string>>("ItemsSource").Value; }
    set { GetDataBindItem<List<string>>("ItemsSource").Value = value; }
}

public MainViewModel()
{
    CreateDataBindItem<List<string>>("ItemsSource", new List<string> { "item1", "item2", "item3" });
}

利点としては動的に変更される場合などに対処しやすいということでしょうか? ある項目の値がAの場合はこう、Bの場合はこうといったケースも容易に書けます。

難点は、ViewModel側で特定のコントロール用の属性に依存する項目を持つことの是非でしょうか?
ViewModelでは本来、ある項目がViewでリストボックスやコンボボックスで実装されるのか、テキストボックスで実装されるのか考える必要はありません。したがって、ViewModelでリストボックスやコンボボックスの選択肢であるItemsSourceを保持する必要もない筈です。

ただ、実際にはViewModelで保持した方が記述が簡潔になるとか多々あると思うので一概にはいえないと思いますが。

4. 独自のクラスを定義する

ItemsSourceだけを提供するクラスを定義します。

public class ListSource
 {
    public string[] ItemsSource { get; private set; }

    // コンストラクタ
    public ListSource()
    {
        ItemsSource = new string[] { "item1", "item2", "item3" };
    }
}

利点はXAMLで定義するより自由度が高く、Viewのコードビハインドに書くより行儀が良いことでしょうか?
難点はXAMLでの定義同様、動的に変更されるような場合には向かないこと。

この場合、実際には

public class ListProvider<T>
{
    public List<T> Items { get; protected set; }
    public ListProvider(IEnumerable<T> items)
    {
        Items = new List<T>(items);
    }
}

というようなジェネリックなクラスを用意して、そこから派生させるのが良いんじゃないでしょうか。

どれを採用すべきか?

ケースバイケースとしか言いようがないでしょうが、個人的には可能であれば4の独自クラスを定義する方法、でなければ3のViewModel側で提供する方法を利用するのが良いかなと感じています。

他にも何か良い方法がありますでしょうか?

サンプルコード

MainViewModel.cs
// ViewModel
class MainViewModel : WindowViewModelBase
{
    public List<string> ItemsSource
    {
        get { return GetDataBindItem<List<string>>("ItemsSource").Value; }
        set { GetDataBindItem<List<string>>("ItemsSource").Value = value; }
    }

    // コンストラクタ
    public MainViewModel()
    {
        CreateDataBindItem<List<string>>("ItemsSource", new List<string> { "item1", "item2", "item3" });
    }
}
MainWindow.xaml.cs

// ItemsScoue提供クラス
public class ListSource
 {
    public string[] ItemsSource { get; private set; }

    // コンストラクタ
    public ListSource()
    {
        ItemsSource = new string[] { "item1", "item2", "item3" };
    }
}

// View
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new MainViewModel();
        list2.ItemsSource = new string[] { "item1", "item2", "item3" };
    }
}
MainWindow.xaml
<Window x:Class="ItemsSourceTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ItemsSourceTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:ListSource x:Key="ListSource" />
    </Window.Resources>
    <Grid>
        <StackPanel Orientation="Horizontal">
            <!-- list1 : XAML内で要素を定義する -->
            <ListBox Name="list1" >
                <ListBoxItem Content="item1"/>
                <ListBoxItem Content="item2"/>
                <ListBoxItem Content="item3"/>
            </ListBox>

            <!-- コードビハインドで要素を設定する -->
            <ListBox Name="list2" />

            <!-- ViewModel で要素を設定する -->
            <ListBox Name="list3" ItemsSource="{Binding ItemsSource}" />

            <!-- 独自のクラスを定義して設定する -->
            <ListBox Name="list4" ItemsSource="{Binding ItemsSource, Source={StaticResource ListSource}}" />
        </StackPanel>
    </Grid>
</Window>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
56
Help us understand the problem. What are the problem?