terullet
@terullet

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

WPFで入れ子になっている要素の更新を画面に反映させたいです

解決したいこと

WPFにおいて,入れ子になっているListBoxにViewModel側で要素を追加しても,画面に反映されません.
どうしたらよいか,解決方法を教えてください.よろしくお願いします.

発生している問題の詳細

図にすると下記のような感じです.

BindingDescription.png

デバッグでWPFビジュアライザーから確認したところ,
WrapperControl内のElementControlsItemsSourceObservableCollenction<ElementViewModel>が入っていることは確認できたのですが,
画面には反映されていませんでした.

ソースコード

MainWindow.xaml
<Window x:Class="ListBoxExperiment.MainWindow"
				xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
				xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
				xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
				xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
				xmlns:local="clr-namespace:ListBoxExperiment"
				mc:Ignorable="d"
				Title="MainWindow" Height="450" Width="800">
	<Window.DataContext>
		<local:MainViewModel/>
	</Window.DataContext>
	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition/>
			<RowDefinition Height="Auto"/>
			<RowDefinition Height="Auto"/>
		</Grid.RowDefinitions>

		<ListBox
			Grid.Row="0"
			x:Name="WrapperControls"
			ItemsSource="{Binding Wrappers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
			<ListBox.ItemTemplate>
				<DataTemplate>
					<local:WrapperControl/>
				</DataTemplate>
			</ListBox.ItemTemplate>
			<ListBox.ItemContainerStyle>
				<Style TargetType="{x:Type ListBoxItem}">
					<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
					<Setter Property="VerticalContentAlignment" Value="Stretch"/>
				</Style>
			</ListBox.ItemContainerStyle>
		</ListBox>

		<Grid Grid.Row="1">
			<Grid.ColumnDefinitions>
				<ColumnDefinition/>
				<ColumnDefinition/>
			</Grid.ColumnDefinitions>

			<Button
				Content="Wrapperを追加"
				Grid.Column="0"
				Command="{Binding AddWrapper}"/>

			<Button
				Content="先頭のWrapperにElementを追加"
				Grid.Column="1"
				Command="{Binding AddElement}"/>
		</Grid>

		<!-- デバッグ時の一時停止用のボタンです.
			ブレークポイントを設定しているだけの関数をコードビハインドに書いています. -->
		<Button Content="ブレイク" Grid.Row="2" Click="Break"/>
	</Grid>
</Window>
WrapperControl.xaml
<UserControl x:Class="ListBoxExperiment.WrapperControl"
						 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
						 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
						 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
						 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
						 xmlns:local="clr-namespace:ListBoxExperiment"
						 mc:Ignorable="d" 
						 d:DesignHeight="450" d:DesignWidth="800">
	<UserControl.DataContext>
		<local:WrapperViewModel/>
	</UserControl.DataContext>
	<Grid>
		<ListBox
			x:Name="ElementControls"
			ItemsSource="{Binding Elements, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
			<ListBox.ItemTemplate>
				<DataTemplate>
					<local:ElementControl/>
				</DataTemplate>
			</ListBox.ItemTemplate>
		</ListBox>
	</Grid>
</UserControl>
ElementControl.xaml
<UserControl x:Class="ListBoxExperiment.ElementControl"
						 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
						 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
						 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
						 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
						 xmlns:local="clr-namespace:ListBoxExperiment"
						 mc:Ignorable="d" 
						 d:DesignHeight="450" d:DesignWidth="800">
	<UserControl.DataContext>
		<local:ElementViewModel/>
	</UserControl.DataContext>
	<Grid>
		<TextBlock Text="{Binding AddedAt, StringFormat=Created at {0:yyyy/MM/dd-HH:mm:ss}}"/>
	</Grid>
</UserControl>
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Ink;
using System.Windows.Input;

namespace ListBoxExperiment
{
	public class MainViewModel : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler? PropertyChanged;

		public ObservableCollection<WrapperViewModel> Wrappers { get; private set; }

		public DelegateCommand AddElement { get; private set; }

		public DelegateCommand AddWrapper { get; private set; }

		public MainViewModel()
		{
			this.Wrappers = new ObservableCollection<WrapperViewModel>();
			this.Wrappers.Add(new WrapperViewModel());
			this.Wrappers[0].PropertyChanged += (s, e) => this.PropertyChanged?.Invoke(s, new PropertyChangedEventArgs(nameof(Wrappers)));
			this.AddElement = new DelegateCommand(() => this.Wrappers[0].Elements.Add(new ElementViewModel()), () => true);
			this.AddWrapper = new DelegateCommand(() => this.Wrappers.Add(new WrapperViewModel()), () => true);
		}
	}

	public class DelegateCommand : ICommand
	{
		public event EventHandler? CanExecuteChanged;

		public bool CanExecute(object? parameter) => this._canExecute();

		public void Execute(object? parameter) => this._execute();

		private Action _execute;
		private Func<bool> _canExecute;

		public DelegateCommand(Action execute, Func<bool> canExecute)
		{
			_execute = execute;
			_canExecute = canExecute;
		}
	}
}
WrapperViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ListBoxExperiment
{
	public class WrapperViewModel : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler? PropertyChanged;

		public ObservableCollection<ElementViewModel> Elements { get; private set; }

		public WrapperViewModel()
		{
			this.Elements = new ObservableCollection<ElementViewModel>();
			this.Elements.Add(new ElementViewModel());
			this.Elements.CollectionChanged += (s, e) => this.PropertyChanged?.Invoke(s, new PropertyChangedEventArgs(nameof(Elements)));
		}
	}
}
ElementViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ListBoxExperiment
{
	public class ElementViewModel
	{
		public DateTime AddedAt { get; private set; }

		public ElementViewModel()
		{
			this.AddedAt = DateTime.Now;
		}
	}
}

試したこと

上記のコードのように,CollectionChangedPropertyChangedを登録したり,
WrapperViewModelPropertyChangedMainViewModelPropertyChangedを登録したりしてみたのですが,
うまくいきませんでした.

0

1Answer

Your answer might help someone💌