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

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

新規登録して質問してみよう
ただいま回答率
85.47%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

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

Q&A

解決済

1回答

308閲覧

WPFで動的にユーザーコントロールを追加したときに、値を設定する方法がわからない

chiken

総合スコア14

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

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

2グッド

1クリップ

投稿2024/03/29 07:48

実現したいこと

以下のような画面を作成しています。
・データ行は画面起動時の引数によって可変
・すべてのコンボボックスのデータは同じもの
・チェックボックスがONのときは横のコンボボックスが有効
・チェックボックスがOFFのときは各データのコンボボックスが有効
ここまでは実装しました。

次に、画面起動時に各コンボボックスにデータを設定したいのですが方法がわかりません。

イメージ説明

発生している問題・分からないこと

現在はチェックボックスとその横にあるコンボボックスがあるウィンドウを用意し、その下にユーザーコントロールを追加しています。
各ユーザーコントロールにあるコンボボックスに値を設定したのですが、今のコードではVM側からはユーザーコントロールのオブジェクトを認識できないため設定できません。何か方法はありませんでしょうか?

該当のソースコード

MainWindow.xaml

1 <Window.DataContext> 2 <vm:MainViewModel/> 3 </Window.DataContext> 4 <Window.Resources> 5 <vm:CBoolNegativeConverter x:Key="BoolNeg"/> 6 </Window.Resources> 7 <Grid> 8 <Grid.RowDefinitions> 9 <RowDefinition Height="1cm"/> 10 <RowDefinition Height="1cm"/> 11 <RowDefinition Height="Auto"/> 12 </Grid.RowDefinitions> 13 <Grid.ColumnDefinitions> 14 <ColumnDefinition /> 15 <ColumnDefinition /> 16 <ColumnDefinition /> 17 </Grid.ColumnDefinitions> 18 <TextBlock Grid.Row="0" Grid.Column="1" Text="項目1" HorizontalAlignment="Center" VerticalAlignment="Center"/> 19 <TextBlock Grid.Row="0" Grid.Column="2" Text="項目2" HorizontalAlignment="Center" VerticalAlignment="Center"/> 20 <CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding IsCheck}" HorizontalAlignment="Center" VerticalAlignment="Center"/> 21 <ComboBox Grid.Row="1" Grid.Column="2" 22 IsEnabled="{Binding IsCheck}" 23 SelectedItem="{Binding SelectedAllItem}" 24 ItemsSource="{Binding DataItems}"/> 25 <StackPanel x:Name="DataPanel" Grid.Row="2" Grid.ColumnSpan="3"/> 26 </Grid> 27</Window> 28

MainWindow.xaml.cs

1namespace WpfApp4 2{ 3 /// <summary> 4 /// MainWindow.xaml の相互作用ロジック 5 /// </summary> 6 public partial class MainWindow : Window 7 { 8 public MainWindow() 9 { 10 InitializeComponent(); 11 12 int dataCounter = 3; //引数によって変動 13 14 for(int i = 0; i < dataCounter; i++) 15 { 16 var tmpUC = new UserControl1(i,"test", this); 17 tmpUC.UCTitle.Content = "Data" + i.ToString(); 18 DataPanel.Children.Add(tmpUC); 19 } 20 } 21 } 22} 23

UserControl1.xaml

1<UserControl x:Class="WpfApp4.UserControl1" 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:WpfApp4" 7 xmlns:vm="clr-namespace:WpfApp4.ViewModels" 8 mc:Ignorable="d" 9 d:DesignHeight="76" d:DesignWidth="581"> 10 <UserControl.Resources> 11 <vm:CBoolNegativeConverter x:Key="BoolNeg"/> 12 </UserControl.Resources> 13 <Grid> 14 <Grid.ColumnDefinitions> 15 <ColumnDefinition /> 16 <ColumnDefinition /> 17 <ColumnDefinition /> 18 </Grid.ColumnDefinitions> 19 <Label Grid.Column="0" 20 x:Name="UCTitle" 21 HorizontalAlignment="Center"/> 22 <ComboBox Grid.Column="1" 23 Margin="5,0" 24 SelectedItem="{Binding DisplayData1Info}" 25 ItemsSource="{Binding DisplayData1Items}" 26 DisplayMemberPath="Data"/> 27 <ComboBox Grid.Column="2" 28 Margin="0" 29 IsEnabled="{Binding Path=IsCheck, Converter={StaticResource BoolNeg}}" 30 SelectedItem="{Binding DisplayData2Info}" 31 ItemsSource="{Binding DisplayData2Items}" 32 DisplayMemberPath="Data"/> 33 </Grid> 34</UserControl> 35

UserControl1.xaml.cs

1namespace WpfApp4 2{ 3 /// <summary> 4 /// UserControl1.xaml の相互作用ロジック 5 /// </summary> 6 public partial class UserControl1 : UserControl 7 { 8 public UserControl1() 9 { 10 this.DataContext = new ViewModels.MainViewModel(); 11 InitializeComponent(); 12 } 13 14 public UserControl1(int ItemNumber, string row, MainWindow main) 15 { 16 main.DataContext = new ViewModels.MainViewModel(ItemNumber); 17 InitializeComponent(); 18 19 } 20 } 21} 22

MainViewModel.cs

1namespace WpfApp4.ViewModels 2{ 3 4 public class CBoolNegativeConverter : IValueConverter 5 { 6 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 7 { 8 return !(value is bool && (bool)value); 9 } 10 11 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 12 { 13 return !(value is bool && (bool)value); 14 } 15 16 } 17 18 class MainViewModel : NotificationObject 19 { 20 public string[] DataList = new string[] 21 { 22 "test1", 23 "test2", 24 "test3", 25 }; 26 27 public class DisplayDataInfo 28 { 29 public DisplayDataInfo(string data, int item1, int item2) 30 { 31 Data = data; 32 Item1 = item1; 33 Item2 = item2; 34 } 35 36 public string Data { get; set; } 37 public int Item1 { get; set; } //項目1 38 public int Item2 { get; set; } //項目2 39 } 40 41 42 public MainViewModel() 43 { 44 DataItems = DataList; 45 } 46 47 public MainViewModel(int ItemNumber) 48 { 49 foreach(var data in DataList) 50 { 51 DisplayData1Items.Add(new DisplayDataInfo(data, ItemNumber + 1, 0)); 52 DisplayData2Items.Add(new DisplayDataInfo(data, ItemNumber + 1, 1)); 53 } 54 55 //テストデータ 56 bool flg = true; 57 string allItem = DataList[1]; 58 string data1Item1 = DataList[0]; 59 string data2Item1 = DataList[1]; 60 string data3Item1 = DataList[2]; 61 string data1Item2 = DataList[1]; 62 63 //各データに値を設定 64 if (flg) 65 { 66 IsCheck = flg; 67 SelectedAllItem = allItem; 68 69 //どのように代入すればいいのかわからない 70 //対象のユーザーコントロールを指定して 71 //DisplayData1Info = DataList[0];ように代入すればよい? 72 } 73 else 74 { 75 //同じく 76 } 77 78 } 79 80 public string[] DataItems 81 { 82 get { return DataList; } 83 set 84 { 85 DataList = value; 86 RaisePropertyChanged("DataItems"); 87 } 88 } 89 90 /// <summary> 91 /// 全体リスト 92 /// </summary> 93 public ObservableCollection<DisplayDataInfo> DisplayDataItems { get; } = new ObservableCollection<DisplayDataInfo>(); 94 95 /// <summary> 96 /// 項目1リスト 97 /// </summary> 98 public ObservableCollection<DisplayDataInfo> DisplayData1Items { get; } = new ObservableCollection<DisplayDataInfo>(); 99 100 /// <summary> 101 /// 項目2リスト 102 /// </summary> 103 public ObservableCollection<DisplayDataInfo> DisplayData2Items { get; } = new ObservableCollection<DisplayDataInfo>(); 104 105 106 private bool _IsCheck = false; 107 public bool IsCheck 108 { 109 get { return _IsCheck; } 110 set 111 { 112 if (_IsCheck != value) 113 { 114 _IsCheck = value; 115 RaisePropertyChanged("IsCheck"); 116 } 117 } 118 } 119 120 private string _SelectedAllItem; 121 public string SelectedAllItem 122 { 123 get { return _SelectedAllItem; } 124 set 125 { 126 if (_SelectedAllItem != value) 127 { 128 _SelectedAllItem = value; 129 RaisePropertyChanged("SelectedAllItem"); 130 } 131 } 132 } 133 134 /// <summary> 135 /// 項目1の選択肢 136 /// </summary> 137 private DisplayDataInfo _DisplayData1Info = null; 138 public DisplayDataInfo DisplayData1Info 139 { 140 get { return _DisplayData1Info; } 141 set 142 { 143 if (_DisplayData1Info != value) 144 { 145 RaisePropertyChanged("DisplayData1Info"); 146 } 147 148 } 149 } 150 151 /// <summary> 152 /// 項目2の選択肢 153 /// </summary> 154 private DisplayDataInfo _DisplayData2Info = null; 155 public DisplayDataInfo DisplayData2Info 156 { 157 get { return _DisplayData2Info; } 158 set 159 { 160 if (_DisplayData2Info != value) 161 { 162 RaisePropertyChanged("DisplayData2Info"); 163 } 164 } 165 } 166 167 } 168} 169

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

そもそもウィンドウとユーザーコントロールのVMを共通しているのがいけないのでしょうか?
MVVMに強いこだわりはないので、ユーザーコントロール部分をウィンドウのコードビハインドで記述してしまえばできそうとは考えています。

補足

visual studio 2017
.net framework 4.7.2

TN8001, mikannohito👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

・すべてのコンボボックスのデータは同じもの
・チェックボックスがONのときは横のコンボボックスが有効
・チェックボックスがOFFのときは各データのコンボボックスが有効

項目1と項目2の選択肢は別なんですよね?
それぞれのデータは選択肢は同一だが、選択した値は別々に持つわけですよね?
項目2については一括指定か・個別指定かをチェックで切り替える。と

そもそもウィンドウとユーザーコントロールのVMを共通しているのがいけないのでしょうか?

こういった増減するUserControlの場合、子ViewModelを持つと見通しがよくなります。
MainViewModelでは子ViewModelのコレクションを保持し、各選択は子ViewModelで管理します。

xml:MainWindow.xaml

1<Window 2 x:Class="Qe1i8qsarazg7kq.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:Qe1i8qsarazg7kq" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 Title="MainWindow" 9 Width="600" 10 Height="300" 11 mc:Ignorable="d"> 12 <Grid> 13 <Grid.RowDefinitions> 14 <RowDefinition Height="1cm" /> 15 <RowDefinition Height="1cm" /> 16 <RowDefinition Height="Auto" /> 17 </Grid.RowDefinitions> 18 <Grid.ColumnDefinitions> 19 <ColumnDefinition /> 20 <ColumnDefinition /> 21 <ColumnDefinition /> 22 </Grid.ColumnDefinitions> 23 <TextBlock 24 Grid.Column="1" 25 HorizontalAlignment="Center" 26 VerticalAlignment="Center" 27 Text="項目1" /> 28 <TextBlock 29 Grid.Column="2" 30 HorizontalAlignment="Center" 31 VerticalAlignment="Center" 32 Text="項目2" /> 33 <CheckBox 34 Grid.Row="1" 35 HorizontalAlignment="Center" 36 VerticalAlignment="Center" 37 IsChecked="{Binding IsCheck}" /> 38 39 <!-- DisplayData1Items・DisplayData2Itemsはすべてに共通(=Staticで定義) --> 40 <ComboBox 41 Grid.Row="1" 42 Grid.Column="2" 43 DisplayMemberPath="Data" 44 IsEnabled="{Binding IsCheck}" 45 ItemsSource="{x:Static local:MainViewModel.DisplayData2Items}" 46 SelectedItem="{Binding SelectedAllItem}" /> 47 48 <!-- 動的なコントロール追加時の定石 --> 49 <ItemsControl 50 Grid.Row="2" 51 Grid.ColumnSpan="3" 52 d:ItemsSource="{d:SampleData ItemCount=3}" 53 ItemsSource="{Binding DataItems}"> 54 <ItemsControl.ItemTemplate> 55 <DataTemplate> 56 <local:UserControl1 /> 57 </DataTemplate> 58 </ItemsControl.ItemTemplate> 59 </ItemsControl> 60 </Grid> 61</Window>

cs:MainWindow.xaml.cs

1using System.Windows; 2 3 4namespace Qe1i8qsarazg7kq 5{ 6 public partial class MainWindow : Window 7 { 8 public MainWindow() 9 { 10 InitializeComponent(); 11 12 var dataCounter = 3; 13 DataContext = new MainViewModel(dataCounter); 14 } 15 } 16}

xml:UserControl1.xaml

1<UserControl 2 x:Class="Qe1i8qsarazg7kq.UserControl1" 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:Qe1i8qsarazg7kq" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 d:DesignHeight="76" 9 d:DesignWidth="581" 10 mc:Ignorable="d"> 11 <!-- DataContextはUserControl1ViewModel --> 12 <UserControl.Resources> 13 <local:CBoolNegativeConverter x:Key="BoolNeg" /> 14 </UserControl.Resources> 15 <Grid> 16 <Grid.ColumnDefinitions> 17 <ColumnDefinition /> 18 <ColumnDefinition /> 19 <ColumnDefinition /> 20 </Grid.ColumnDefinitions> 21 <Label HorizontalAlignment="Center" Content="{Binding Title}" /> 22 <ComboBox 23 Grid.Column="1" 24 Margin="5,0" 25 DisplayMemberPath="Data" 26 ItemsSource="{x:Static local:MainViewModel.DisplayData1Items}" 27 SelectedItem="{Binding DisplayData1Info}" /> 28 29 <!-- IsCheckはMainViewModelにあるため、RelativeSourceでWindowのDataContextを引っ張ってくる --> 30 <ComboBox 31 Grid.Column="2" 32 DisplayMemberPath="Data" 33 IsEnabled="{Binding DataContext.IsCheck, Converter={StaticResource BoolNeg}, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" 34 ItemsSource="{x:Static local:MainViewModel.DisplayData2Items}" 35 SelectedItem="{Binding DisplayData2Info}" /> 36 </Grid> 37</UserControl>

cs:UserControl1.xaml.cs

1using System; 2using System.Globalization; 3using System.Windows.Controls; 4using System.Windows.Data; 5 6 7namespace Qe1i8qsarazg7kq 8{ 9 public partial class UserControl1 : UserControl 10 { 11 public UserControl1() => InitializeComponent(); 12 } 13 14 // ConverterはViewのnamespaceに置くことが多い 15 public class CBoolNegativeConverter : IValueConverter 16 { 17 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 => !(value is bool && (bool)value); 19 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 => !(value is bool && (bool)value); 21 } 22}

cs

1using System.Collections.Generic; 2using Livet; 3 4 5namespace Qe1i8qsarazg7kq 6{ 7 class MainViewModel : NotificationObject 8 { 9 // ここに置くのは微妙か?どこでもいいっちゃいいのだが^^; 10 public static List<DisplayDataInfo> DisplayData1Items { get; } = new List<DisplayDataInfo>(); 11 public static List<DisplayDataInfo> DisplayData2Items { get; } = new List<DisplayDataInfo>(); 12 13 14 public bool IsCheck { get => _IsCheck; set => RaisePropertyChangedIfSet(ref _IsCheck, value); } 15 private bool _IsCheck; 16 17 public DisplayDataInfo SelectedAllItem { get => _SelectedAllItem; set => RaisePropertyChangedIfSet(ref _SelectedAllItem, value); } 18 private DisplayDataInfo _SelectedAllItem; 19 20 21 // 動的追加があるならObservableCollectionで 22 public List<UserControl1ViewModel> DataItems { get; } = new List<UserControl1ViewModel>(); 23 24 25 public MainViewModel(int itemNumber) 26 { 27 var items = new string[] { "test1", "test2", "test3", }; 28 foreach (var data in items) 29 { 30 DisplayData1Items.Add(new DisplayDataInfo($"Data1 {data}", itemNumber + 1, 0)); 31 DisplayData2Items.Add(new DisplayDataInfo($"Data2 {data}", itemNumber + 1, 1)); 32 } 33 34 for (var i = 0; i < itemNumber; i++) 35 { 36 DataItems.Add(new UserControl1ViewModel($"Data{i}")); 37 } 38 39 bool flg = true; 40 if (flg) 41 { 42 IsCheck = flg; 43 SelectedAllItem = DisplayData2Items[1]; 44 45 DataItems[0].DisplayData1Info = DisplayData1Items[0]; 46 DataItems[0].DisplayData2Info = DisplayData2Items[0]; 47 48 DataItems[1].DisplayData1Info = DisplayData1Items[2]; 49 DataItems[1].DisplayData2Info = DisplayData2Items[1]; 50 51 DataItems[2].DisplayData1Info = DisplayData1Items[0]; 52 DataItems[2].DisplayData2Info = DisplayData2Items[2]; 53 } 54 else 55 { 56 } 57 } 58 } 59 60 class UserControl1ViewModel : NotificationObject 61 { 62 public string Title { get; } 63 64 public DisplayDataInfo DisplayData1Info { get => _DisplayData1Info; set => RaisePropertyChangedIfSet(ref _DisplayData1Info, value); } 65 private DisplayDataInfo _DisplayData1Info; 66 67 public DisplayDataInfo DisplayData2Info { get => _DisplayData2Info; set => RaisePropertyChangedIfSet(ref _DisplayData2Info, value); } 68 private DisplayDataInfo _DisplayData2Info; 69 70 public UserControl1ViewModel(string title) => Title = title; 71 } 72 73 public class DisplayDataInfo 74 { 75 public string Data { get; set; } 76 public int Item1 { get; set; } 77 public int Item2 { get; set; } 78 79 public DisplayDataInfo(string data, int item1, int item2) 80 => (Data, Item1, Item2) = (data, item1, item2); 81 } 82}

NuGet Gallery | LivetCask.Mvvm 4.0.2

アプリ画像

投稿2024/03/29 10:58

編集2024/03/29 12:33
TN8001

総合スコア9363

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

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

chiken

2024/04/02 01:56

> 項目1と項目2の選択肢は別なんですよね? > それぞれのデータは選択肢は同一だが、選択した値は別々に持つわけですよね? > 項目2については一括指定か・個別指定かをチェックで切り替える。と すいません抜けていました。ご配慮いただきありがとうございます。 子ViewModelを持つという方法があるのですね。というより定石らしいのに全然知りませんでした。。。 また、Converterはview側に置くことや、ラムダ式の記述などまだまだ未熟な点も教えていただきありがとうございます。。。 希望通りの動作が行えました。ご教授いただき本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問