WPFで入れ子になっている要素の更新を画面に反映させたいです
解決したいこと
WPFにおいて,入れ子になっているListBoxにViewModel側で要素を追加しても,画面に反映されません.
どうしたらよいか,解決方法を教えてください.よろしくお願いします.
発生している問題の詳細
図にすると下記のような感じです.
デバッグでWPFビジュアライザーから確認したところ,
WrapperControl
内のElementControls
のItemsSource
にObservableCollenction<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;
}
}
}
試したこと
上記のコードのように,CollectionChanged
にPropertyChanged
を登録したり,
WrapperViewModel
のPropertyChanged
にMainViewModel
のPropertyChanged
を登録したりしてみたのですが,
うまくいきませんでした.
0