1
1

More than 3 years have passed since last update.

WPFで使いまわしができる「ListBox.SelectedItemsを取得するメソッド」

Last updated at Posted at 2020-09-18

(2020/09/22追記)
「ジェネリックメソッド」を用いることで、 List<T><T> に当たる部分を呼び出し元で指定できることが分かりました。リストボックスの中身を取得するメソッドくん②、最終的なコード全体②として記載しました。 @MinoruSanou さん、ありがとうございます。

(2020/09/26追記)
長々と記事を書きましたが、 @TN8001 さんの こちらのコメント に、現状僕が知る中で一番簡単な ListBox.SelectedItem を取得するコードを掲載頂きました。TN8001 さんありがとうございます。

背景

以前 こちら でListBox.SelectedItemsの中身を取り出す方法を書きました。しかし ListBox が複数になるとコード量が多く邪魔だったので、中身を取得する専用のメソッドくんを考えました。

準備

WPFでリストを複数(今回は二つ)作る。
c#でリスト項目をセット

image.png

MainWindow.xaml
<Window>
    <Grid>
        <!-- 上下に2分割 -->
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- 上半分 -->
        <Grid Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="25"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="25"/>
            </Grid.RowDefinitions>

            <Label Grid.Row="0" Content="リストボックス1"/>
            <ListBox Grid.Row="1" Name="ExampleList1" Margin="10" SelectionMode="Extended" ScrollViewer.VerticalScrollBarVisibility="Auto">
                <!-- SelectionMode="Extended" :ctrl,shift+クリックで複数選択可能 -->
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Id, StringFormat=IDは{0} :}"/>
                            <TextBlock Text="{Binding Name, StringFormat= Nameは{0} :}"/>
                            <TextBlock Text="{Binding Age, StringFormat= Ageは{0}}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <Button Grid.Row="2" Name="Btn1" Content="ボタン1" Margin="50,0,50,0" Click="Btn1_Click"/>
        </Grid>

        <!-- 下半分 -->
        <Grid Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="25"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="25"/>
            </Grid.RowDefinitions>

            <Label Grid.Row="0" Content="リストボックス2" Grid.ColumnSpan="2"/>
            <ListBox Grid.Row="1" Name="ExampleList2" Margin="10" SelectionMode="Extended" ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.ColumnSpan="2">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Id, StringFormat=IDは{0} :}"/>
                            <TextBlock Text="{Binding Name, StringFormat= Nameは{0} :}"/>
                            <TextBlock Text="{Binding Age, StringFormat= Ageは{0}}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <Button Grid.Row="2" Name="Btn2" Content="ボタン2" Margin="50,0,50,0" Click="Btn2_Click" Grid.ColumnSpan="2"/>
        </Grid>
    </Grid>
</Window>
MainWindow.xaml.cs
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
    List<ExampleClass> list = new List<ExampleClass>();
    public MainWindow()
    {
        InitializeComponent();

        list.Add(new ExampleClass() { Id = 0, Name = "aaa", Age = 10 });
        list.Add(new ExampleClass() { Id = 1, Name = "bbb", Age = 20 });
        list.Add(new ExampleClass() { Id = 2, Name = "ccc", Age = 30 });
        list.Add(new ExampleClass() { Id = 3, Name = "ddd", Age = 40 });
        list.Add(new ExampleClass() { Id = 4, Name = "eee", Age = 50 });
        ExampleList1.ItemsSource = list;
        ExampleList2.ItemsSource = list;
    }

    private void Btn1_Click(object sender, RoutedEventArgs e)
    {
        //リストボックス1の選択アイテムを取り出す
    }

    private void Btn2_Click(object sender, RoutedEventArgs e)
    {
        //リストボックス2の選択アイテムを取り出す
    }
}
class ExampleClass
{
    public int Id { get; set; }
    public string Name { get; set; }
    public byte Age { get; set; }
}

リストボックスの中身を取得するメソッドくん

今回の本題です。このメソッドを、リスト選択アイテムを知りたい場所で呼び出します。

MainWindow.xaml.cs
    //! SelectedItemsの中身を取り出すメソッド
    private List<ExampleClass> GetSelectedItems(ListBox listBox)
    {
        List<ExampleClass> selItems = new List<ExampleClass>();
        foreach (var oneItemLine in listBox.SelectedItems)
        {
            ExampleClass item = oneItemLine as ExampleClass;
            selItems.Add(item);
        }
        return selItems;
    }



各ボタンから呼び出します。
対象のコントロールを探す方法は こちら を参考にしました。

MainWindow.xaml.cs
    //! リストボックス1の選択中アイテムを表示する
    private void Btn1_Click(object sender, RoutedEventArgs e)
    {
        // 操作するコントロール
        string controlName = "ExampleList1";

        // リストボックスを探す
        object controlObj = FindName(controlName);
        ListBox listBox = (ListBox)controlObj;

        // 選択項目が0 => メソッドを出る
        if (listBox.SelectedItems.Count == 0)
            return;

        // 選択中のアイテムを取得する
        List<ExampleClass> selItems = GetSelectedItems(listBox);

        // メッセージボックスに表示する内容
        string message = "";
        foreach (var line in selItems)
        {
            message = message + string.Format("\r\nId:「{0}」 Nmae:「{1}」 Age:「{2}」", line.Id, line.Name, line.Age);
        }
        message = string.Format($"{controlName} で選択中の項目は\r\n{message}\r\n\r\nです");

        MessageBox.Show(message);
    }

    //! リストボックス2の選択中アイテムを表示する
    private void Btn2_Click(object sender, RoutedEventArgs e)
    {
        // リスト1と大体同じ。ContorolNameを変えるのみ。
    }

ボタン1 を押すと
image.png
選択中の項目が取り出せました。ボタン2でも同じです。
でもボタン1と2のコードもだいたい同じだからまとめたいですね。まとめた最終的なコードを最後に載せておきます。

最終的なコード全体

ExampleClassは変わっていないので省略します。

MainWindow.xaml.cs
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
    List<ExampleClass> list = new List<ExampleClass>();
    public MainWindow()
    {
        InitializeComponent();

        list.Add(new ExampleClass() { Id = 0, Name = "aaa", Age = 10 });
        list.Add(new ExampleClass() { Id = 1, Name = "bbb", Age = 20 });
        list.Add(new ExampleClass() { Id = 2, Name = "ccc", Age = 30 });
        list.Add(new ExampleClass() { Id = 3, Name = "ddd", Age = 40 });
        list.Add(new ExampleClass() { Id = 4, Name = "eee", Age = 50 });
        ExampleList1.ItemsSource = list;
        ExampleList2.ItemsSource = list;
    }

    //! リストボックス1の選択中アイテムを表示する
    private void Btn1_Click(object sender, RoutedEventArgs e)
    {
        // 操作するコントロール
        string controlName = "ExampleList1";

        ItemShow(controlName);
    }

    //! リストボックス2の選択中アイテムを表示する
    private void Btn2_Click(object sender, RoutedEventArgs e)
    {
        // 操作するコントロール
        string controlName = "ExampleList2";

        ItemShow(controlName);
    }

    // SelectedItemsの中身を取り出す~メッセージ表示メソッド
    private void ItemShow(string controlName)
    {
        // リストボックスを探す
        object controlObj = FindName(controlName);
        ListBox listBox = (ListBox)controlObj;

        // 選択項目が0 => メソッドを出る
        if (listBox.SelectedItems.Count == 0)
            return;

        // 選択中のアイテムを取得する
        List<ExampleClass> selItems = GetSelectedItems(listBox);

        // メッセージボックスに表示する内容
        string message = "";
        foreach (var line in selItems)
        {
            message = message + string.Format("\r\nId:「{0}」 Nmae:「{1}」 Age:「{2}」", line.Id, line.Name, line.Age);
        }
        message = string.Format($"{controlName} で選択中の項目は\r\n{message}\r\n\r\nです");

        MessageBox.Show(message);
    }

    //! SelectedItemsの中身を取り出すメソッド
    private List<ExampleClass> GetSelectedItems(ListBox listBox)
    {
        List<ExampleClass> selItems = new List<ExampleClass>();
        foreach (var oneItemLine in listBox.SelectedItems)
        {
            ExampleClass item = oneItemLine as ExampleClass;
            selItems.Add(item);
        }
        return selItems;
    }
}

image.png
image.png
image.png
アイテムはクリックした順で取得します。

リストボックスの中身を取得するメソッドくん②

以下 2020/09/22 の追記です。
ジェネリックメソッドを用いることで、T を呼び出し元で指定することができます。これにより使いまわし性が向上します。戻り値 selItemsList<T> 型です。

public List<T> GetSelectedItems<T>(ListBox listBox) where T : class
{
    var selItems = new List<T>();
    foreach (var oneItemLine in listBox.SelectedItems)
    {
        var item = oneItemLine as T;
        selItems.Add(item);
    }
    return selItems;
}



これをこのままMainWindow.xaml.cs内に記述してもいいですが、せっかく使いまわしの自由度が増したので、独立したクラスにしたいと思います。

GetSelectedItems.cs
class GetSelectedItems<T> where T : class
{
    ListBox listBox;

    public GetSelectedItems(ListBox listBox)
    {
        this.listBox = listBox;
    }

    //! SelectedItemsの中身を取り出すメソッド
    public List<T> Get()
    {
        var selItems = new List<T>();
        foreach (var oneItemLine in listBox.SelectedItems)
        {
            var item = oneItemLine as T;
            selItems.Add(item);
        }
        return selItems;
    }
}



呼び出し元で以下の記述をすることで、SelectedItemsの中身を取り出せます。var selItemsList<T> 型で、選択中のリストボックスアイテムが入ります。

    // リストボックスを探す
    object controlObj = FindName(controlName);
    ListBox listBox = (ListBox)controlObj;

    // 選択中のアイテムを取得する
    GetSelectedItems<ExampleClass> g = new GetSelectedItems<ExampleClass>(listBox);
    var selItems = g.Get();

最終的なコード全体②

内容が増えたのでc#の記述全体を書きます。

MainWindow.xaml.cs
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
    List<ExampleClass> list = new List<ExampleClass>();
    public MainWindow()
    {
        InitializeComponent();

        list.Add(new ExampleClass() { Id = 0, Name = "aaa", Age = 10 });
        list.Add(new ExampleClass() { Id = 1, Name = "bbb", Age = 20 });
        list.Add(new ExampleClass() { Id = 2, Name = "ccc", Age = 30 });
        list.Add(new ExampleClass() { Id = 3, Name = "ddd", Age = 40 });
        list.Add(new ExampleClass() { Id = 4, Name = "eee", Age = 50 });
        ExampleList1.ItemsSource = list;
        ExampleList2.ItemsSource = list;
    }

    //! リストボックス1の選択中アイテムを表示する
    private void Btn1_Click(object sender, RoutedEventArgs e)
    {
        // 操作するコントロール
        string controlName = "ExampleList1";

        ItemShow(controlName);
    }

    //! リストボックス2の選択中アイテムを表示する
    private void Btn2_Click(object sender, RoutedEventArgs e)
    {
        // 操作するコントロール
        string controlName = "ExampleList2";

        ItemShow(controlName);
    }

    // SelectedItemsの中身を取り出す~メッセージボックス表示メソッド
    private void ItemShow(string controlName)
    {
        // リストボックスを探す
        object controlObj = FindName(controlName);
        ListBox listBox = (ListBox)controlObj;

        // 選択項目が0 => メソッドを出る
        if (listBox.SelectedItems.Count == 0)
            return;

        // 選択中のアイテムを取得する
        GetSelectedItems<ExampleClass> g = new GetSelectedItems<ExampleClass>(listBox);
        var selItems = g.Get();

        // メッセージボックスに表示する内容
        string message = "";
        foreach (var line in selItems)
        {
            message = message + string.Format("\r\nId:「{0}」 Nmae:「{1}」 Age:「{2}」", line.Id, line.Name, line.Age);
        }
        message = string.Format($"{controlName} で選択中の項目は\r\n{message}\r\n\r\nです");

        MessageBox.Show(message);
    }
}
ExampleClass.cs
class ExampleClass
{
    public int Id { get; set; }
    public string Name { get; set; }
    public byte Age { get; set; }
}
GetSelectedItems.cs
class GetSelectedItems<T> where T : class
{
    ListBox listBox;

    public GetSelectedItems(ListBox listBox)
    {
        this.listBox = listBox;
    }

    //! SelectedItemsの中身を取り出すメソッド
    public List<T> Get()
    {
        var selItems = new List<T>();
        foreach (var oneItemLine in listBox.SelectedItems)
        {
            var item = oneItemLine as T;
            selItems.Add(item);
        }
        return selItems;
    }
}
1
1
6

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
1
1