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

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

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

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

WPF

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

解決済

ItemsControlで配置したボタンが動作しない

shubk
shubk

総合スコア10

MVVM

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

WPF

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

3回答

0グッド

1クリップ

1192閲覧

投稿2022/05/06 10:33

WPF初学者です。

wpf(MVVM)のItemsControlを使って、動的にボタンを配置しましたが、そのボタンをクリックしても動作しません。
機能の概要は以下となります。
・MVVMで作成しています。
・ボタンクリックの動作はXAML上の「Command」から呼び出しています。
・RelayCommandクラスを作成しています。
・名前と年齢のみのモデルを3人分ItemsControlで表示しており、各Item毎にボタンを1つ配置しています。
・ボタンの動作は全て同じで、固定文のメッセージボックスを表示するだけです。

トップのGrid直下などに問題のボタンを配置すると、メッセージボックスが正常に出ますが、
ItemsControl配下に配置したボタンはメッセージボックスが出力されませんでした。
デバッグしたところ、ItemsControl配下に配置したボタンをクリックしても「RelayCommand」クラスの「Execute」メソッドが呼び出されないところまでは
分かったのですがなぜ呼び出されないまで分かりませんでした。

上記原因及び対応方法、もしくは調べるためのヒントだけでも構いませんので、ご教示の程宜しくお願い致します。
ソースコードは以下となります。

View(Xaml)

1<Window x:Class="WpfApp1.Views.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:WpfApp1" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Grid> 10 <ItemsControl ItemsSource="{Binding TestList}"> 11 <ItemsControl.ItemTemplate> 12 <DataTemplate> 13 <StackPanel> 14 <TextBlock Text="{Binding Name}"></TextBlock> 15 <TextBlock Text="{Binding Age}"></TextBlock> 16 <Button Content="test" Height="30" Width="90" HorizontalAlignment="Left" Command="{Binding ClickOutMsg}"></Button> 17 </StackPanel> 18 </DataTemplate> 19 </ItemsControl.ItemTemplate> 20 </ItemsControl> 21 </Grid> 22</Window>

View(クラス)

1using System.Windows; 2using WpfApp1.ViewModels; 3 4namespace WpfApp1.Views 5{ 6 public partial class MainWindow : Window 7 { 8 public MainWindow() 9 { 10 InitializeComponent(); 11 this.DataContext = new MainViewModel(); 12 } 13 } 14}

ViewModel

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.ComponentModel; 7using System.Windows.Input; 8using System.Collections.ObjectModel; 9using WpfApp1.Models; 10using System.Data; 11using System.Windows; 12using WpfApp1.Common; 13 14namespace WpfApp1.ViewModels 15{ 16 public class MainViewModel: INotifyPropertyChanged 17 { 18 public event PropertyChangedEventHandler PropertyChanged; 19 20 private void NotifyPropertyChanged(string info) 21 { 22 if (PropertyChanged != null) { 23 PropertyChanged(this, new PropertyChangedEventArgs(info)); 24 } 25 } 26 27 public ObservableCollection<Person> TestList { get; set; } 28 29 public ICommand ClickOutMsg { get; set; } 30 31 public MainViewModel() 32 { 33 ClickOutMsg = new RelayCommand(OutMsg); 34 35 TestList = new ObservableCollection<Person>(Enumerable.Range(0, 3).Select(x => new Person() { 36 Name = "tanaka" + x.ToString(), 37 Age = (30 + x) % 50 38 }).ToArray()); 39 } 40 41 private void OutMsg() 42 { 43 MessageBox.Show("test"); 44 } 45 } 46}

Model

1namespace WpfApp1.Models 2{ 3 public class Person 4 { 5 public string Name { get; set; } 6 public int Age { get; set; } 7 } 8}

RelayCommand

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.Windows.Input; 7 8namespace WpfApp1.Common 9{ 10 /// <summary> 11 /// その機能を中継することのみを目的とするコマンド 12 /// デリゲートを呼び出すことにより、他のオブジェクトに対して呼び出します。 13 ///CanExecute メソッドの既定の戻り値は 'true' です。 14 /// <see cref="RaiseCanExecuteChanged"/> は、次の場合は必ず呼び出す必要があります。 15 /// <see cref="CanExecute"/> は、別の値を返すことが予期されます。 16 /// </summary> 17 public class RelayCommand : ICommand 18 { 19 private readonly Action _execute; 20 private readonly Func<bool> _canExecute; 21 22 /// <summary> 23 /// RaiseCanExecuteChanged が呼び出されたときに生成されます。 24 /// </summary> 25 public event EventHandler CanExecuteChanged; 26 27 /// <summary> 28 /// 常に実行可能な新しいコマンドを作成します。 29 /// </summary> 30 /// <param name="execute">実行ロジック。</param> 31 public RelayCommand(Action execute) 32 : this(execute, null) 33 { 34 } 35 36 /// <summary> 37 /// 新しいコマンドを作成します。 38 /// </summary> 39 /// <param name="execute">実行ロジック。</param> 40 /// <param name="canExecute">実行ステータス ロジック。</param> 41 public RelayCommand(Action execute, Func<bool> canExecute) 42 { 43 if (execute == null) 44 throw new ArgumentNullException("execute"); 45 _execute = execute; 46 _canExecute = canExecute; 47 } 48 49 /// <summary> 50 /// 現在の状態でこの <see cref="RelayCommand"/> が実行できるかどうかを判定します。 51 /// </summary> 52 /// <param name="parameter"> 53 /// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。 54 /// </param> 55 /// <returns>このコマンドが実行可能な場合は true、それ以外の場合は false。</returns> 56 public bool CanExecute(object parameter) 57 { 58 return _canExecute == null ? true : _canExecute(); 59 } 60 61 /// <summary> 62 /// 現在のコマンド ターゲットに対して <see cref="RelayCommand"/> を実行します。 63 /// </summary> 64 /// <param name="parameter"> 65 /// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。 66 /// </param> 67 public void Execute(object parameter) 68 { 69 _execute(); 70 } 71 72 /// <summary> 73 /// <see cref="CanExecuteChanged"/> イベントを発生させるために使用されるメソッド 74 /// <see cref="CanExecute"/> の戻り値を表すために 75 /// メソッドが変更されました。 76 /// </summary> 77 public void RaiseCanExecuteChanged() 78 { 79 var handler = CanExecuteChanged; 80 if (handler != null) 81 { 82 handler(this, EventArgs.Empty); 83 } 84 } 85 } 86 87 88 /// <summary> 89 /// 任意の型の引数を1つ受け付けるRelayCommand 90 /// </summary> 91 /// <typeparam name="T"></typeparam> 92 public class RelayCommand<T> : ICommand 93 { 94 private readonly Action<T> _execute; 95 private readonly Func<T, bool> _canExecute; 96 97 /// <summary> 98 /// RaiseCanExecuteChanged が呼び出されたときに生成されます。 99 /// </summary> 100 public event EventHandler CanExecuteChanged; 101 102 /// <summary> 103 /// 常に実行可能な新しいコマンドを作成します。 104 /// </summary> 105 /// <param name="execute">実行ロジック。</param> 106 public RelayCommand(Action<T> execute) 107 : this(execute, null) 108 { 109 } 110 111 /// <summary> 112 /// 新しいコマンドを作成します。 113 /// </summary> 114 /// <param name="execute">実行ロジック。</param> 115 /// <param name="canExecute">実行ステータス ロジック。</param> 116 public RelayCommand(Action<T> execute, Func<T, bool> canExecute) 117 { 118 if (execute == null) 119 throw new ArgumentNullException("execute"); 120 _execute = execute; 121 _canExecute = canExecute; 122 } 123 124 /// <summary> 125 /// 現在の状態でこの <see cref="RelayCommand"/> が実行できるかどうかを判定します。 126 /// </summary> 127 /// <param name="parameter"> 128 /// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。 129 /// </param> 130 /// <returns>このコマンドが実行可能な場合は true、それ以外の場合は false。</returns> 131 public bool CanExecute(object parameter) 132 { 133 return _canExecute == null ? true : _canExecute((T)parameter); 134 } 135 136 /// <summary> 137 /// 現在のコマンド ターゲットに対して <see cref="RelayCommand"/> を実行します。 138 /// </summary> 139 /// <param name="parameter"> 140 /// コマンドによって使用されるデータ。コマンドが、データの引き渡しを必要としない場合、このオブジェクトを null に設定できます。 141 /// </param> 142 public void Execute(object parameter) 143 { 144 _execute((T)parameter); 145 } 146 147 /// <summary> 148 /// <see cref="CanExecuteChanged"/> イベントを発生させるために使用されるメソッド 149 /// <see cref="CanExecute"/> の戻り値を表すために 150 /// メソッドが変更されました。 151 /// </summary> 152 public void RaiseCanExecuteChanged() 153 { 154 var handler = CanExecuteChanged; 155 if (handler != null) 156 { 157 handler(this, EventArgs.Empty); 158 } 159 } 160 } 161} 162

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答3

2

ベストアンサー

TestListが分解され、DataTemplateで定義したもののDataContextにバインドして生成されるイメージです。
なのでNameとAgeは正しく表示できているかと思います。
しかし、PersonはClickOutMsgを持たないのでバインドできていない状態です。
MainViewModelではなくPesonに実装しましょう。

投稿2022/05/06 12:02

caster

総合スコア71

TN8001, shubk👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

1

デバッグしたところ、ItemsControl配下に配置したボタンをクリックしても「RelayCommand」クラスの「Execute」メソッドが呼び出されないところまでは
分かったのですがなぜ呼び出されないまで分かりませんでした。

[XAML バインド エラー]ウィンドウ(出し方:メニューから デバッグ > ウィンドウ > XAML バインド エラー)に、エラー内容が出ているはずです。
[XAML バインド エラー]ウィンドウがない場合は、[出力]ウィンドウ(出し方:メニューから 表示 > 出力)でもいいです(単に見やすくなっているだけなので)

MainViewModelではなくPesonに実装しましょう。

上記の通り、実装しましたところ、ボタンが正常に動作しました!

それでいい場合は特に言うことはありません。

しかしわかったうえで、MainViewModelにバインドしたいときも往々にしてあります。
その場合はRelativeSourceElementNameでたぐってくることになります。

.NET6です。ICommand実装は下記を使用しました。
NuGet Gallery | CommunityToolkit.Mvvm 7.1.2

xaml

1<Window 2 x:Class="Qpca9318udwdlpg.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:Qpca9318udwdlpg" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 x:Name="window" 9 Width="800" 10 Height="450" 11 d:DataContext="{d:DesignInstance local:MainViewModel, IsDesignTimeCreatable=True}" 12 mc:Ignorable="d"> 13 <Window.DataContext> 14 <local:MainViewModel /> 15 </Window.DataContext> 16 <DockPanel> 17 <Button 18 Command="{Binding MainCommand}" 19 Content="MainCommand" 20 DockPanel.Dock="Top" /> 21 <Button 22 Command="{Binding TestList/PersonCommand}" 23 Content="PersonCommand" 24 DockPanel.Dock="Top" /> 25 <ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding TestList}"> 26 <ListBox.ItemTemplate> 27 <DataTemplate> 28 <StackPanel> 29 <TextBlock Text="{Binding Name}" /> 30 <Button Command="{Binding MainCommand}" Content="MainCommand not work!" /> 31 <Button Command="{Binding DataContext.MainCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Content="MainCommand RelativeSource" /> 32 <Button Command="{Binding DataContext.MainCommand, ElementName=window}" Content="MainCommand ElementName" /> 33 <Button Command="{Binding PersonCommand}" Content="PersonCommand" /> 34 </StackPanel> 35 </DataTemplate> 36 </ListBox.ItemTemplate> 37 </ListBox> 38 </DockPanel> 39</Window>

C#

1using System.Collections.ObjectModel; 2using System.Diagnostics; 3using System.Linq; 4using System.Windows; 5using CommunityToolkit.Mvvm.Input; 6 7namespace Qpca9318udwdlpg 8{ 9 public class MainViewModel 10 { 11 public ObservableCollection<Person> TestList { get; } 12 = new(Enumerable.Range(0, 3).Select(x => new Person { Name = $"tanaka{x}", })); 13 14 public RelayCommand MainCommand { get; } = new(() => Debug.WriteLine("MainCommand")); 15 } 16 17 public class Person 18 { 19 public string? Name { get; set; } 20 public RelayCommand PersonCommand { get; } 21 public Person() => PersonCommand = new(() => Debug.WriteLine($"PersonCommand {Name}")); 22 } 23 24 public partial class MainWindow : Window 25 { 26 public MainWindow() => InitializeComponent(); 27 } 28}

投稿2022/05/06 14:25

TN8001

総合スコア8037

draq👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

0

ご回答ありがとうございます。

MainViewModelではなくPesonに実装しましょう。

上記の通り、実装しましたところ、ボタンが正常に動作しました!
ベストアンサーとさせて頂きます。

修正後のソースコードも掲載します。

ViewModel(修正後)

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.ComponentModel; 7using System.Windows.Input; 8using System.Collections.ObjectModel; 9using WpfApp1.Models; 10using System.Data; 11using System.Windows; 12using WpfApp1.Common; 13 14namespace WpfApp1.ViewModels 15{ 16 public class MainViewModel: INotifyPropertyChanged 17 { 18 public event PropertyChangedEventHandler PropertyChanged; 19 20 private void NotifyPropertyChanged(string info) 21 { 22 if (PropertyChanged != null) { 23 PropertyChanged(this, new PropertyChangedEventArgs(info)); 24 } 25 } 26 27 public ObservableCollection<Person> TestList { get; set; } 28 29 // 削除 30 //public ICommand ClickOutMsg { get; set; } 31 32 public MainViewModel() 33 { 34 // 削除 35 //ClickOutMsg = new RelayCommand(OutMsg); 36 37 TestList = new ObservableCollection<Person>(Enumerable.Range(0, 3).Select(x => new Person() { 38 Name = "tanaka" + x.ToString(), 39 Age = (30 + x) % 50 40 }).ToArray()); 41 } 42 43 // 削除 44 //private void OutMsg() 45 //{ 46 // MessageBox.Show("test"); 47 //} 48 } 49} 50

Model(修正後)

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.Windows.Input; 7using WpfApp1.Common; 8using System.Windows; 9 10namespace WpfApp1.Models 11{ 12 public class Person 13 { 14 public string Name { get; set; } 15 public int Age { get; set; } 16 17 // 追加 18 public ICommand ClickOutMsg { get; set; } 19 20 // 追加 21 public Person() 22 { 23 ClickOutMsg = new RelayCommand(OutMsg); 24 } 25 26 // 追加 27 private void OutMsg() 28 { 29 MessageBox.Show("test"); 30 } 31 } 32} 33

投稿2022/05/06 13:26

shubk

総合スコア10

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

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

ただいまの回答率
86.12%

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

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

質問する

関連した質問

同じタグがついた質問を見る

MVVM

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

WPF

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