ListBox の選択項目をバインディングするのに手間取ったので備忘録として残しました。
手間取った理由はWPFとUWPではやり方が違うからです。

サンプルの構成

まず簡単に構成の説明・・・
UIは画面左にリストボックスを置き、右にリストボックスで選択された本の名前と価格を表示しています。
図のような見た目です。

XAML上で Page の DataContext として BookViewModel を登録しています。BookViewModel は、BookList という名前の List型のコレクションをプロパティとして保持しています。そのコレクションは Book クラスのコレクションで、Book クラスは Name と Price という名前のプロパティを保持しています。

文字だと分かり難いですね・・・
下がソースです。かなり単純な構成です。

■ Book.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SampleApp
{
	public class Book
	{
		public string Name { get; set; }
		public string Price { get; set; }
	}
}

■ BookViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HelloWPF
{
	public class BookViewModel
	{
		public List<Book> BookList { get; set; }

		public BookViewModel()
		{
			BookList = new List<Book>();

			// 初期値(とりあえず)
			BookList.Add( new Book { Name = "comic", Price = "350" } );
			BookList.Add( new Book { Name = "magazine", Price = "520" } );
		}
	}
}

これが一番重要です。

■ MainPage.xaml

<Page
    x:Class="SampleApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SampleApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:vm="using:SampleApp"
    mc:Ignorable="d">

	<!-- PageのDataContextとしてBookViewModelを登録 -->
	<Page.DataContext>
		<vm:BookViewModel />
	</Page.DataContext>

	<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
		<Grid.ColumnDefinitions>
			<ColumnDefinition />
			<ColumnDefinition />
		</Grid.ColumnDefinitions>

		<!-- ページ左側 -->
		<Grid Grid.Column="0">

			<Grid.RowDefinitions>
				<RowDefinition Height="auto"/>
				<RowDefinition />
			</Grid.RowDefinitions>

			<TextBlock Grid.Row="0" HorizontalAlignment="Center" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Center"/>
			<ListBox x:Name="listBox" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding BookList}" DisplayMemberPath="Name" />
		</Grid>

		<!-- ページ右側 -->
		<Grid Grid.Column="1">
			<StackPanel Margin="50,30">
				<!-- 名前 -->
				<TextBlock Text="■ 名前"/>
				<TextBlock Text="{Binding BookList/Name}" />

				<!-- 値段 -->
				<TextBlock Text="■ 値段"/>
				<TextBlock Text="{Binding SelectedItem.Price, ElementName=listBox}" />
			</StackPanel>
		</Grid>

	</Grid>
</Page>

見ても分かる通りかなり単純な構成です。

ListBox のバインディングは何を指すのか?

DataContext はXAML上で Page に BookViewModel を登録しているだけです。
つまり、画面左に配置されているリストボックスは BookViewModel がそのまま DataContext になります。
そして、ItemsSource で {Binding BookList} とバインディングしているので、Page の DataContext である BookViewModel の BookList を表示する事になります。

ただ、BookList は Bookクラスのコレクションです。これだけだと、表示する物が BookList[x].Name か BookList[x].Price かわかりません。
それは、DisplayMemberPath でどちらを表示するか指定しています。今回は Name を指定しているので BookList[ x ].Name がリストに表示されることになります。

選択項目のバインディングの仕組み

さて、肝心の選択項目のバインディングです。これには二つのやり方がありました。

#1.DataContext でバインディング

最初は ListBox の ItemsSource と同じく DataContext でバインディングする方法です。TextBlock の「▪️名前」を表示している方がそれにあたります。

Text に {Bindign BookList/Name} と指定します。コレクションのバインディングに対して「/」を付けると、現在の項目に対する参照を行います。つまり、リストボックスの0番目の項目を選んでいたら、BookList[0].Name を参照する事になります。

#2. リストボックスでバインディング

もう一つは、DataContext ではなく、リストボックスのプロパティでバインディングする方法です。「▪️値段」の方がそれにあたります。

Text に {Binding SelectedItem.Price, ElementName=listBox} としています。ElementName に コントロールの名前を入れると、DataContext ではなく、そのコントロールのプロパティを検索してくれます。

この場合だと、listBox.SelectedItem.Price を参照する事になります。listBox.SelectedItem は、BookListの選択項目なので、BookList[x]ということになります。そして、それは Book クラスを表します。つまり、0番目の項目を選択していた場合、listBox.SelectedItem.Price は、BookList[0].Price を表すことになります。

UWPで使えない方はどっち?

どちらも同じ値を参照するので結果は同じです。WPFだと両方同じ値が取れました。しかし、UWPだと#1の方法では選択項目の参照ができませんでした。

XAMLもUWPもWPFも初心者なので対応が分からずこれでかなり時間を使ってしまいました。知ってる人にとっては当り前なのかもしれませんが…

まぁ、おかげでXAMLとバインディングと DataContext に対する理解が深まったのでよしとするか…

スポンサーリンク