teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

見直しキャンペーン中

2023/07/25 14:04

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,183 +1,181 @@
1
- XY 問題な気がします。本当にやりたいことは何ですか?
2
- [「XY 問題」とは何ですか? - スタック・オーバーフローMeta](https://ja.meta.stackoverflow.com/questions/2701/xy-%E5%95%8F%E9%A1%8C-%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B)
3
-
4
-
5
- > 一つのコントロールに複数のプロパティを紐づけたく
6
-
7
- 複数のプロパティをバインドするなら、`MultiBinding`があります。
8
-
9
- [BindingBase.StringFormat プロパティ (System.Windows.Data) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.data.bindingbase.stringformat)
10
- [IMultiValueConverter インターフェイス (System.Windows.Data) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.data.imultivalueconverter)
11
-
12
-
13
- しかしコードを見るに「バインド先を切り替えたい」ように見えます。
14
-
15
- であれば`ViewModel`側で、切り替え後の値を提供するのが真っ当に思います。
16
- 手を抜くなら2つコントロールを置いて、`Visibility`を切り替えるとか^^;
17
-
18
-
19
- > Passが見つからないのがエラーの原因だと考えています。
20
-
21
- Passが見つからないというか`StackOverflowException`ですので、`Mein`と`ViewModel`をぐるぐるしてしまうみたいな状況じゃないでしょうか(よくわかっていませんが^^;
22
-
23
- `Binding`は`DataContext`から探しますから、その`DataContext`自身を切り替えたらダメでしょう(`DataContext`とは何なのかをしっかり意識しましょう)
24
-
25
- どうしても今の形にこだわるなら、
26
- ```xaml
27
- <DataTrigger Binding="{Binding DataContext.Pass, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="true">
28
- <Setter Property="DataContext" Value="{Binding VM1}" />
29
- </DataTrigger>
30
- ```
31
- というようになっていないといけません。
32
-
33
-
34
- ```xaml
35
- <Window
36
- x:Class="Questions310362.MainWindow"
37
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
38
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
39
- xmlns:local="clr-namespace:Questions310362"
40
- Width="450"
41
- SizeToContent="Height">
42
- <Window.DataContext>
43
- <local:MainViewModel />
44
- </Window.DataContext>
45
- <Window.Resources>
46
- <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
47
- <local:AConverter x:Key="AConverter" />
48
-
49
- <Style x:Key="Style" TargetType="{x:Type TextBlock}">
50
- <Style.Triggers>
51
- <DataTrigger Binding="{Binding DataContext.Pass, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="true">
52
- <Setter Property="DataContext" Value="{Binding VM1}" />
53
- </DataTrigger>
54
- <DataTrigger Binding="{Binding DataContext.Pass, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="false">
55
- <Setter Property="DataContext" Value="{Binding VM2}" />
56
- </DataTrigger>
57
- </Style.Triggers>
58
- </Style>
59
- </Window.Resources>
60
- <StackPanel>
61
- <Button Command="{Binding ButtonPush}" Content="Button" />
62
-
63
- <GroupBox Margin="5" Header="StringFormat">
64
- <TextBlock>
65
- <TextBlock.Text>
66
- <MultiBinding StringFormat="VM1:{0}, VM2:{1}">
67
- <Binding Path="VM1.A" />
68
- <Binding Path="VM2.A" />
69
- </MultiBinding>
70
- </TextBlock.Text>
71
- </TextBlock>
72
- </GroupBox>
73
-
74
- <GroupBox Margin="5" Header="IMultiValueConverter">
75
- <TextBlock>
76
- <TextBlock.Text>
77
- <MultiBinding Converter="{StaticResource AConverter}">
78
- <Binding Path="Pass" />
79
- <Binding Path="VM1.A" />
80
- <Binding Path="VM2.A" />
81
- </MultiBinding>
82
- </TextBlock.Text>
83
- </TextBlock>
84
- </GroupBox>
85
-
86
- <GroupBox Margin="5" Header="切り替え後の値 VM">
87
- <TextBlock Text="{Binding VM.A}" />
88
- </GroupBox>
89
-
90
- <GroupBox Margin="5" Header="切り替え後の値 A">
91
- <TextBlock Text="{Binding A}" />
92
- </GroupBox>
93
-
94
- <GroupBox Margin="5" Header="手抜き">
95
- <Grid>
96
- <TextBlock Text="{Binding VM2.A}" />
97
- <TextBlock
98
- Background="White"
99
- Text="{Binding VM1.A}"
100
- Visibility="{Binding Pass, Converter={StaticResource BooleanToVisibilityConverter}}" />
101
- </Grid>
102
- </GroupBox>
103
-
104
- <GroupBox Margin="5" Header="DataTrigger">
105
- <TextBlock Style="{StaticResource Style}" Text="{Binding A}" />
106
- </GroupBox>
107
- </StackPanel>
108
- </Window>
109
- ```
110
-
111
- ```C#
112
- using Prism.Commands;
113
- using Prism.Mvvm;
114
- using System;
115
- using System.Diagnostics;
116
- using System.Globalization;
117
- using System.Windows;
118
- using System.Windows.Data;
119
-
120
- namespace Questions310362
121
- {
122
- internal class AConverter : IMultiValueConverter
123
- {
124
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
125
- {
126
- if (values[0] is bool pass) return pass ? values[1].ToString() : values[2].ToString();
127
- return DependencyProperty.UnsetValue;
128
- }
129
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
130
- }
131
-
132
- internal class MainViewModel : BindableBase
133
- {
134
- private bool _pass;
135
- public bool Pass { get => _pass; set => SetProperty(ref _pass, value); }
136
- public ChildViewModel VM1 { get; } = new();
137
- public ChildViewModel VM2 { get; } = new();
138
- public ChildViewModel VM => Pass ? VM1 : VM2;
139
- public int A => Pass ? VM1.A : VM2.A;
140
-
141
- public DelegateCommand ButtonPush { get; }
142
-
143
- public MainViewModel() => ButtonPush = new(Push);
144
-
145
- private int i = 2;
146
- private void Push()
147
- {
148
- Pass = ++i % 2 == 0;
149
- VM1.Calculate(i);
150
- VM2.Calculate(-i); // 違いが出ないのでマイナス
151
-
152
- OnPropertyChanged(new(nameof(VM))); // VM変更通知
153
- OnPropertyChanged(new(nameof(A))); // A変更通知
154
-
155
- Debug.WriteLine($"VM1:{VM1.A}, VM2:{VM2.A}");
156
- }
157
- }
158
-
159
- internal class ChildViewModel : BindableBase
160
- {
161
- private int _a;
162
- public int A { get => _a; set => SetProperty(ref _a, value); }
163
- public void Calculate(int c) => A = 2 * c;
164
- }
165
-
166
- public partial class MainWindow : Window
167
- {
168
- public MainWindow() => InitializeComponent();
169
- }
170
- }
171
- ```
172
- ![アプリ画像](f2b2b8ba19667f538401a81c59fc2f1f.png)
173
-
174
- ---
175
-
176
- `BindableBase`・`DelegateCommand`は、`Prism.Core`を使用しました。
177
- [NuGet Gallery | Prism.Core 8.0.0.1909](https://www.nuget.org/packages/Prism.Core/)
178
-
179
- 特に指定がないので`.NET 5.0`で書きました^^
180
- `.NET Framework 4.8`等では、`new()`でエラーが出るので型を書いてください。
181
-
182
- [Target-typed new expressions - C# 9.0 specification proposals | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new)
1
+ XY 問題な気がします。本当にやりたいことは何ですか?
2
+ [「XY 問題」とは何ですか? - スタック・オーバーフローMeta](https://ja.meta.stackoverflow.com/questions/2701/xy-%E5%95%8F%E9%A1%8C-%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B)
3
+
4
+
5
+ > 一つのコントロールに複数のプロパティを紐づけたく
6
+
7
+ 複数のプロパティをバインドするなら、`MultiBinding`があります。
8
+
9
+ [BindingBase.StringFormat プロパティ (System.Windows.Data) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.data.bindingbase.stringformat)
10
+ [IMultiValueConverter インターフェイス (System.Windows.Data) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.data.imultivalueconverter)
11
+
12
+
13
+ しかしコードを見るに「バインド先を切り替えたい」ように見えます。
14
+
15
+ であれば`ViewModel`側で、切り替え後の値を提供するのが真っ当に思います。
16
+ 手を抜くなら2つコントロールを置いて、`Visibility`を切り替えるとか^^;
17
+
18
+
19
+ > Passが見つからないのがエラーの原因だと考えています。
20
+
21
+ Passが見つからないというか`StackOverflowException`ですので、`Mein`と`ViewModel`をぐるぐるしてしまうみたいな状況じゃないでしょうか(よくわかっていませんが^^;
22
+
23
+ `Binding`は`DataContext`から探しますから、その`DataContext`自身を切り替えたらダメでしょう(`DataContext`とは何なのかをしっかり意識しましょう)
24
+
25
+ どうしても今の形にこだわるなら、↓のようになっていないといけません。
26
+ ```xml
27
+ <DataTrigger Binding="{Binding DataContext.Pass, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="true">
28
+ <Setter Property="DataContext" Value="{Binding VM1}" />
29
+ </DataTrigger>
30
+ ```
31
+
32
+
33
+ ```xml
34
+ <Window
35
+ x:Class="Questions310362.MainWindow"
36
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
37
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
38
+ xmlns:local="clr-namespace:Questions310362"
39
+ Width="450"
40
+ SizeToContent="Height">
41
+ <Window.DataContext>
42
+ <local:MainViewModel />
43
+ </Window.DataContext>
44
+ <Window.Resources>
45
+ <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
46
+ <local:AConverter x:Key="AConverter" />
47
+
48
+ <Style x:Key="Style" TargetType="{x:Type TextBlock}">
49
+ <Style.Triggers>
50
+ <DataTrigger Binding="{Binding DataContext.Pass, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="true">
51
+ <Setter Property="DataContext" Value="{Binding VM1}" />
52
+ </DataTrigger>
53
+ <DataTrigger Binding="{Binding DataContext.Pass, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="false">
54
+ <Setter Property="DataContext" Value="{Binding VM2}" />
55
+ </DataTrigger>
56
+ </Style.Triggers>
57
+ </Style>
58
+ </Window.Resources>
59
+ <StackPanel>
60
+ <Button Command="{Binding ButtonPush}" Content="Button" />
61
+
62
+ <GroupBox Margin="5" Header="StringFormat">
63
+ <TextBlock>
64
+ <TextBlock.Text>
65
+ <MultiBinding StringFormat="VM1:{0}, VM2:{1}">
66
+ <Binding Path="VM1.A" />
67
+ <Binding Path="VM2.A" />
68
+ </MultiBinding>
69
+ </TextBlock.Text>
70
+ </TextBlock>
71
+ </GroupBox>
72
+
73
+ <GroupBox Margin="5" Header="IMultiValueConverter">
74
+ <TextBlock>
75
+ <TextBlock.Text>
76
+ <MultiBinding Converter="{StaticResource AConverter}">
77
+ <Binding Path="Pass" />
78
+ <Binding Path="VM1.A" />
79
+ <Binding Path="VM2.A" />
80
+ </MultiBinding>
81
+ </TextBlock.Text>
82
+ </TextBlock>
83
+ </GroupBox>
84
+
85
+ <GroupBox Margin="5" Header="切り替え後の値 VM">
86
+ <TextBlock Text="{Binding VM.A}" />
87
+ </GroupBox>
88
+
89
+ <GroupBox Margin="5" Header="切り替え後の値 A">
90
+ <TextBlock Text="{Binding A}" />
91
+ </GroupBox>
92
+
93
+ <GroupBox Margin="5" Header="手抜き">
94
+ <Grid>
95
+ <TextBlock Text="{Binding VM2.A}" />
96
+ <TextBlock
97
+ Background="White"
98
+ Text="{Binding VM1.A}"
99
+ Visibility="{Binding Pass, Converter={StaticResource BooleanToVisibilityConverter}}" />
100
+ </Grid>
101
+ </GroupBox>
102
+
103
+ <GroupBox Margin="5" Header="DataTrigger">
104
+ <TextBlock Style="{StaticResource Style}" Text="{Binding A}" />
105
+ </GroupBox>
106
+ </StackPanel>
107
+ </Window>
108
+ ```
109
+
110
+ ```cs
111
+ using Prism.Commands;
112
+ using Prism.Mvvm;
113
+ using System;
114
+ using System.Diagnostics;
115
+ using System.Globalization;
116
+ using System.Windows;
117
+ using System.Windows.Data;
118
+
119
+ namespace Questions310362
120
+ {
121
+ internal class AConverter : IMultiValueConverter
122
+ {
123
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
124
+ {
125
+ if (values[0] is bool pass) return pass ? values[1].ToString() : values[2].ToString();
126
+ return DependencyProperty.UnsetValue;
127
+ }
128
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
129
+ }
130
+
131
+ internal class MainViewModel : BindableBase
132
+ {
133
+ private bool _pass;
134
+ public bool Pass { get => _pass; set => SetProperty(ref _pass, value); }
135
+ public ChildViewModel VM1 { get; } = new();
136
+ public ChildViewModel VM2 { get; } = new();
137
+ public ChildViewModel VM => Pass ? VM1 : VM2;
138
+ public int A => Pass ? VM1.A : VM2.A;
139
+
140
+ public DelegateCommand ButtonPush { get; }
141
+
142
+ public MainViewModel() => ButtonPush = new(Push);
143
+
144
+ private int i = 2;
145
+ private void Push()
146
+ {
147
+ Pass = ++i % 2 == 0;
148
+ VM1.Calculate(i);
149
+ VM2.Calculate(-i); // 違いが出ないのでマイナス
150
+
151
+ OnPropertyChanged(new(nameof(VM))); // VM変更通知
152
+ OnPropertyChanged(new(nameof(A))); // A変更通知
153
+
154
+ Debug.WriteLine($"VM1:{VM1.A}, VM2:{VM2.A}");
155
+ }
156
+ }
157
+
158
+ internal class ChildViewModel : BindableBase
159
+ {
160
+ private int _a;
161
+ public int A { get => _a; set => SetProperty(ref _a, value); }
162
+ public void Calculate(int c) => A = 2 * c;
163
+ }
164
+
165
+ public partial class MainWindow : Window
166
+ {
167
+ public MainWindow() => InitializeComponent();
168
+ }
169
+ }
170
+ ```
171
+ ![アプリ画像](f2b2b8ba19667f538401a81c59fc2f1f.png)
172
+
173
+ ---
174
+
175
+ `BindableBase`・`DelegateCommand`は、`Prism.Core`を使用しました。
176
+ [NuGet Gallery | Prism.Core 8.0.0.1909](https://www.nuget.org/packages/Prism.Core/)
177
+
178
+ 特に指定がないので.NET 5.0で書きました^^
179
+ .NET Framework 4.8等では、`new()`エラーが出るので型をいてください。
180
+ [Target-typed new expressions - C# 9.0 specification proposals | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new)
183
181
  [ターゲットからの new 型推論 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/oo_construct.html#target-typed-new)