質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

2回答

2265閲覧

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

terullet

総合スコア2

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2022/08/30 03:05

解決したいこと

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

発生している問題の詳細

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

BindingDescription.png

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

ソースコード

xml:MainWindow.xaml

1<Window x:Class="ListBoxExperiment.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:ListBoxExperiment" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Window.DataContext> 10 <local:MainViewModel/> 11 </Window.DataContext> 12 <Grid> 13 <Grid.RowDefinitions> 14 <RowDefinition/> 15 <RowDefinition Height="Auto"/> 16 <RowDefinition Height="Auto"/> 17 </Grid.RowDefinitions> 18 19 <ListBox 20 Grid.Row="0" 21 x:Name="WrapperControls" 22 ItemsSource="{Binding Wrappers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"> 23 <ListBox.ItemTemplate> 24 <DataTemplate> 25 <local:WrapperControl/> 26 </DataTemplate> 27 </ListBox.ItemTemplate> 28 <ListBox.ItemContainerStyle> 29 <Style TargetType="{x:Type ListBoxItem}"> 30 <Setter Property="HorizontalContentAlignment" Value="Stretch"/> 31 <Setter Property="VerticalContentAlignment" Value="Stretch"/> 32 </Style> 33 </ListBox.ItemContainerStyle> 34 </ListBox> 35 36 <Grid Grid.Row="1"> 37 <Grid.ColumnDefinitions> 38 <ColumnDefinition/> 39 <ColumnDefinition/> 40 </Grid.ColumnDefinitions> 41 42 <Button 43 Content="Wrapperを追加" 44 Grid.Column="0" 45 Command="{Binding AddWrapper}"/> 46 47 <Button 48 Content="先頭のWrapperにElementを追加" 49 Grid.Column="1" 50 Command="{Binding AddElement}"/> 51 </Grid> 52 53 <!-- デバッグ時の一時停止用のボタンです. 54 ブレークポイントを設定しているだけの関数をコードビハインドに書いています. --> 55 <Button Content="ブレイク" Grid.Row="2" Click="Break"/> 56 </Grid> 57</Window>

xml:WrapperControl.xaml

1<UserControl x:Class="ListBoxExperiment.WrapperControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:ListBoxExperiment" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <UserControl.DataContext> 10 <local:WrapperViewModel/> 11 </UserControl.DataContext> 12 <Grid> 13 <ListBox 14 x:Name="ElementControls" 15 ItemsSource="{Binding Elements, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"> 16 <ListBox.ItemTemplate> 17 <DataTemplate> 18 <local:ElementControl/> 19 </DataTemplate> 20 </ListBox.ItemTemplate> 21 </ListBox> 22 </Grid> 23</UserControl>

xml:ElementControl.xaml

1<UserControl x:Class="ListBoxExperiment.ElementControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:ListBoxExperiment" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <UserControl.DataContext> 10 <local:ElementViewModel/> 11 </UserControl.DataContext> 12 <Grid> 13 <TextBlock Text="{Binding AddedAt, StringFormat=Created at {0:yyyy/MM/dd-HH:mm:ss}}"/> 14 </Grid> 15</UserControl>

cs:MainViewModel.cs

1using System; 2using System.Collections.Generic; 3using System.Collections.ObjectModel; 4using System.ComponentModel; 5using System.Linq; 6using System.Text; 7using System.Threading.Tasks; 8using System.Windows.Ink; 9using System.Windows.Input; 10 11namespace ListBoxExperiment 12{ 13 public class MainViewModel : INotifyPropertyChanged 14 { 15 public event PropertyChangedEventHandler? PropertyChanged; 16 17 public ObservableCollection<WrapperViewModel> Wrappers { get; private set; } 18 19 public DelegateCommand AddElement { get; private set; } 20 21 public DelegateCommand AddWrapper { get; private set; } 22 23 public MainViewModel() 24 { 25 this.Wrappers = new ObservableCollection<WrapperViewModel>(); 26 this.Wrappers.Add(new WrapperViewModel()); 27 this.Wrappers[0].PropertyChanged += (s, e) => this.PropertyChanged?.Invoke(s, new PropertyChangedEventArgs(nameof(Wrappers))); 28 this.AddElement = new DelegateCommand(() => this.Wrappers[0].Elements.Add(new ElementViewModel()), () => true); 29 this.AddWrapper = new DelegateCommand(() => this.Wrappers.Add(new WrapperViewModel()), () => true); 30 } 31 } 32 33 public class DelegateCommand : ICommand 34 { 35 public event EventHandler? CanExecuteChanged; 36 37 public bool CanExecute(object? parameter) => this._canExecute(); 38 39 public void Execute(object? parameter) => this._execute(); 40 41 private Action _execute; 42 private Func<bool> _canExecute; 43 44 public DelegateCommand(Action execute, Func<bool> canExecute) 45 { 46 _execute = execute; 47 _canExecute = canExecute; 48 } 49 } 50}

cs:WrapperViewModel.cs

1using System; 2using System.Collections.Generic; 3using System.Collections.ObjectModel; 4using System.ComponentModel; 5using System.Linq; 6using System.Text; 7using System.Threading.Tasks; 8 9namespace ListBoxExperiment 10{ 11 public class WrapperViewModel : INotifyPropertyChanged 12 { 13 public event PropertyChangedEventHandler? PropertyChanged; 14 15 public ObservableCollection<ElementViewModel> Elements { get; private set; } 16 17 public WrapperViewModel() 18 { 19 this.Elements = new ObservableCollection<ElementViewModel>(); 20 this.Elements.Add(new ElementViewModel()); 21 this.Elements.CollectionChanged += (s, e) => this.PropertyChanged?.Invoke(s, new PropertyChangedEventArgs(nameof(Elements))); 22 } 23 } 24}

cs:ElementViewModel.cs

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6 7namespace ListBoxExperiment 8{ 9 public class ElementViewModel 10 { 11 public DateTime AddedAt { get; private set; } 12 13 public ElementViewModel() 14 { 15 this.AddedAt = DateTime.Now; 16 } 17 } 18}

試したこと

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

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

表題は解決されたようですが、INotifyPropertyChangedINotifyCollectionChangedを混同しているようなので回答します。
INotifyPropertyChanged インターフェイス (System.ComponentModel) | Microsoft Docs
INotifyCollectionChanged インターフェイス (System.Collections.Specialized) | Microsoft Docs

ObservableCollectionはコレクションの増減を(Viewに)通知しますので、PropertyChangedを伝搬させるような?訳のわからないことをする必要はありません。

もちろん中の要素内の(プロパティ)変更は(ObservableCollectionは)関知しませんが、中の要素がINotifyPropertyChangedを実装していれば(Viewに)反映されます。
ObservableCollection<T> クラス (System.Collections.ObjectModel) | Microsoft Docs

中の要素のイベント購読は、できるだけ避けたほうがいいでしょう(削除時に購読解除しないとリークします)
どうしても必要な場合は、ObserveElementPropertyObserveElementObservablePropertyの利用を検討してください。
MVVM をリアクティブプログラミングで快適に ReactiveProperty オーバービュー 2020 年版 後編 - Qiita


.NET6です^^
NuGet Gallery | CommunityToolkit.Mvvm 8.0.0

xml

1<Window 2 x:Class="Q6osq4khrbfz2od.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Q6osq4khrbfz2od" 6 Width="800" 7 Height="450"> 8 <Window.DataContext> 9 <local:MainViewModel /> 10 </Window.DataContext> 11 <Window.Resources> 12 <DataTemplate DataType="{x:Type local:ElementViewModel}"> 13 <TextBlock Text="{Binding AddedAt, StringFormat=Created at {0:yyyy/MM/dd-HH:mm:ss}}" /> 14 </DataTemplate> 15 16 <DataTemplate DataType="{x:Type local:WrapperViewModel}"> 17 <Grid> 18 <!-- 多少意味は違いますが原因を確認する分には十分でしょう。 --> 19 <!-- ↓をコメント化した場合と、コメント解除した場合でBreakを確認してください。 --> 20 <!--<Grid.DataContext> 21 <local:WrapperViewModel /> 22 </Grid.DataContext>--> 23 <ListBox ItemsSource="{Binding Elements}" /> 24 </Grid> 25 </DataTemplate> 26 </Window.Resources> 27 <DockPanel> 28 <Button 29 Click="Break" 30 Content="ブレイク" 31 DockPanel.Dock="Bottom" /> 32 33 <UniformGrid DockPanel.Dock="Bottom" Rows="1"> 34 <Button Command="{Binding AddWrapper}" Content="Wrapperを追加" /> 35 <Button 36 Command="{Binding AddElement}" 37 CommandParameter="{Binding Wrappers[0]}" 38 Content="先頭のWrapperにElementを追加" /> 39 <Button 40 Command="{Binding AddElement}" 41 CommandParameter="{Binding SelectedItem, ElementName=WrapperControls}" 42 Content="選択中のWrapperにElementを追加" /> 43 </UniformGrid> 44 45 <ListBox x:Name="WrapperControls" ItemsSource="{Binding Wrappers}"> 46 <ListBox.ItemContainerStyle> 47 <Style TargetType="{x:Type ListBoxItem}"> 48 <Setter Property="Padding" Value="30,0,0,0" /> 49 <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 50 </Style> 51 </ListBox.ItemContainerStyle> 52 </ListBox> 53 </DockPanel> 54</Window>

cs

1using System; 2using System.Collections.ObjectModel; 3using System.Diagnostics; 4using System.Windows; 5using System.Windows.Media; 6using CommunityToolkit.Mvvm.Input; 7 8namespace Q6osq4khrbfz2od; 9 10 11public class MainViewModel 12{ 13 public ObservableCollection<WrapperViewModel> Wrappers { get; } = new() { new(), }; 14 public RelayCommand AddWrapper { get; } 15 public RelayCommand<WrapperViewModel> AddElement { get; } 16 17 public MainViewModel() 18 { 19 AddWrapper = new(() => Wrappers.Add(new())); 20 AddElement = new(x => x?.Elements.Add(new())); 21 } 22} 23 24public class WrapperViewModel 25{ 26 public ObservableCollection<ElementViewModel> Elements { get; } = new() { new(), }; 27 28 public override string ToString() => $"WrapperViewModel:{GetHashCode()}"; 29} 30 31public class ElementViewModel 32{ 33 public DateTime AddedAt { get; } = DateTime.Now; 34} 35 36public partial class MainWindow : Window 37{ 38 public MainWindow() => InitializeComponent(); 39 40 private void Break(object sender, RoutedEventArgs e) 41 { 42 var w0 = (DataContext as MainViewModel)?.Wrappers[0]; 43 44 var listBoxItem = WrapperControls.ItemContainerGenerator.ContainerFromItem(w0) as FrameworkElement; 45 var border = VisualTreeHelper.GetChild(listBoxItem, 0) as FrameworkElement; 46 var contentPresenter = VisualTreeHelper.GetChild(border, 0) as FrameworkElement; 47 var grid = VisualTreeHelper.GetChild(contentPresenter, 0) as FrameworkElement; 48 49 var w0_a = listBoxItem?.DataContext; 50 var w0_b = border?.DataContext; 51 var w0_c = contentPresenter?.DataContext; 52 var w0_d = grid?.DataContext; 53 54 Debug.WriteLine($"w0 {w0}"); 55 Debug.WriteLine($"w0_a {w0_a}"); 56 Debug.WriteLine($"w0_b {w0_b}"); 57 Debug.WriteLine($"w0_c {w0_c}"); 58 Debug.WriteLine($"w0_d {w0_d}"); // xamlのコメントを解除をするとこれだけ違う。 59 } 60}

投稿2022/08/30 10:15

TN8001

総合スコア9321

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terullet

2022/08/30 21:45 編集

ありがとうございます! イベントの購読は,要素内のプロパティ変更を受け取るのに必要かなと思ってやっていたことだったのですが,`INotifyPropertyChanged`を実装していれば問題なかったどころか,解除しないとリークしてしまうのですね. ためになりました!
guest

0

ベストアンサー

WrapperControlDataContextに設定されているVMがMainViewModelで保持しているWrapperViewModelとは別ものだからでは?

xml

1<UserControl x:Class="ListBoxExperiment.WrapperControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:ListBoxExperiment" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <!-- 10 <UserControl.DataContext> 11 <local:WrapperViewModel/> 12 </UserControl.DataContext> 13 --> 14 <Grid> 15 <ListBox

投稿2022/08/30 04:24

編集2022/08/30 04:26
ebiryo

総合スコア797

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terullet

2022/08/30 04:30

解決できました.ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問