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

回答編集履歴

1

見直しキャンペーン中

2023/07/17 13:20

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,213 +1,212 @@
1
- MVVMにこだわらないのであれば、
2
- ```xaml
3
- <Window
4
- x:Class="WpfApp1.SubWindow"
5
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7
- xmlns:local="clr-namespace:WpfApp1"
8
- Title="SubWindow"
9
- Width="800"
10
- Height="450">
11
- <Window.DataContext>
12
- <local:SubViewModel />
13
- </Window.DataContext>
14
- <Grid>
15
- <Grid.RowDefinitions>
16
- <RowDefinition />
17
- <RowDefinition />
18
- </Grid.RowDefinitions>
19
- <TextBox Text="{Binding Text}" />
20
- <Grid Grid.Row="1">
21
- <Grid.ColumnDefinitions>
22
- <ColumnDefinition />
23
- <ColumnDefinition />
24
- </Grid.ColumnDefinitions>
25
- <Button Content="Cancel" IsCancel="True" />
26
- <Button
27
- Grid.Column="1"
28
- Command="{Binding DoSomethingCommand}"
29
- CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
30
- Content="OK"
31
- IsDefault="True" />
32
- </Grid>
33
- </Grid>
34
- </Window>
35
- ```
36
-
37
- ```C#
38
- using System.Windows;
39
- using Prism.Commands;
40
-
41
- namespace WpfApp1
42
- {
43
- public class SubViewModel
44
- {
45
- public string Text { get; set; }
46
- public DelegateCommand<Window> DoSomethingCommand { get; }
47
-
48
- public SubViewModel()
49
- {
50
- DoSomethingCommand = new DelegateCommand<Window>((w) =>
51
- {
52
- System.Diagnostics.Debug.WriteLine("DoSomethigCommand");
53
-
54
- if(string.IsNullOrEmpty(Text))
55
- {
56
- MessageBox.Show("何か入力してください。");
57
- }
58
- else
59
- {
60
- w.DialogResult = true;
61
- }
62
- });
63
- }
64
- }
65
- }
66
- ```
67
- こんな感じでどうでしょうか。
68
-
69
- ---
70
-
71
- 実際には入力値はもっとあってそれぞれ検証したいのであれば`INotifyDataErrorInfo`等を実装し、エラーがあればボタンを押せなくするのがきれいかもしれません。
72
-
73
- ```xaml
74
- <Window
75
- x:Class="WpfApp1.SubWindow"
76
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
77
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
78
- xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
79
- xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
80
- xmlns:local="clr-namespace:WpfApp1"
81
- Name="SubWnd"
82
- Title="SubWindow"
83
- Width="800"
84
- Height="450">
85
- <Window.Resources>
86
- <ControlTemplate x:Key="ValidationTemplate">
87
- <StackPanel>
88
- <ItemsControl HorizontalAlignment="Right" ItemsSource="{Binding AdornedElement.(Validation.Errors), ElementName=validationTarget}">
89
- <ItemsControl.ItemTemplate>
90
- <DataTemplate>
91
- <TextBlock Foreground="Red" Text="{Binding ErrorContent}" />
92
- </DataTemplate>
93
- </ItemsControl.ItemTemplate>
94
- </ItemsControl>
95
- <AdornedElementPlaceholder x:Name="validationTarget" />
96
- </StackPanel>
97
- </ControlTemplate>
98
- </Window.Resources>
99
- <Window.DataContext>
100
- <local:SubViewModel />
101
- </Window.DataContext>
102
- <DockPanel>
103
- <StackPanel
104
- HorizontalAlignment="Right"
105
- DockPanel.Dock="Bottom"
106
- Orientation="Horizontal">
107
- <Button
108
- MinWidth="80"
109
- Margin="5"
110
- Command="{Binding DoSomethingCommand}"
111
- Content="OK">
112
- <i:Interaction.Triggers>
113
- <i:EventTrigger EventName="Click">
114
- <ei:ChangePropertyAction
115
- PropertyName="DialogResult"
116
- TargetObject="{Binding ElementName=SubWnd}"
117
- Value="True" />
118
- </i:EventTrigger>
119
- </i:Interaction.Triggers>
120
- </Button>
121
- <Button
122
- MinWidth="80"
123
- Margin="5"
124
- Content="Cancel"
125
- IsCancel="True" />
126
- </StackPanel>
127
-
128
- <StackPanel>
129
- <HeaderedContentControl Margin="5" Header="Text1">
130
- <TextBox Text="{Binding Text1, UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
131
- </HeaderedContentControl>
132
- <HeaderedContentControl Margin="5" Header="Text2">
133
- <TextBox Text="{Binding Text2, UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
134
- </HeaderedContentControl>
135
- <HeaderedContentControl Margin="5" Header="Text3">
136
- <TextBox Text="{Binding Text3, UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
137
- </HeaderedContentControl>
138
- </StackPanel>
139
- </DockPanel>
140
- </Window>
141
- ```
142
-
143
- ```C#
144
- using System;
145
- using System.Collections;
146
- using System.Collections.Generic;
147
- using System.ComponentModel;
148
- using System.ComponentModel.DataAnnotations;
149
- using System.Linq;
150
- using System.Runtime.CompilerServices;
151
- using Prism.Commands;
152
- using Prism.Mvvm;
153
-
154
- namespace WpfApp1
155
- {
156
- public class SubViewModel : BindableBase, INotifyDataErrorInfo
157
- {
158
- [Required(ErrorMessage = "何か入力してください。")]
159
- public string Text1 { get => _Text1; set => SetProperty(ref _Text1, value); }
160
- private string _Text1;
161
-
162
- [MinLength(10, ErrorMessage = "10文字以上入力してください。")]
163
- public string Text2 { get => _Text2; set => SetProperty(ref _Text2, value); }
164
- private string _Text2;
165
-
166
- [RegularExpression("[a-z]+", ErrorMessage = "a-zの文字列を入力してください。")]
167
- public string Text3 { get => _Text3; set => SetProperty(ref _Text3, value); }
168
- private string _Text3;
169
-
170
- public DelegateCommand DoSomethingCommand { get; }
171
-
172
-
173
- public SubViewModel()
174
- {
175
- errorsContainer = new ErrorsContainer<string>(x => ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(x)));
176
-
177
- Text1 = ""; // ""に深い意味はない。初期値nullから動かしてValidatorを通してるだけ
178
- Text2 = "";
179
- Text3 = "A";
180
-
181
- DoSomethingCommand = new DelegateCommand(
182
- () => System.Diagnostics.Debug.WriteLine("DoSomethigCommand"),
183
- () => !HasErrors)
184
- .ObservesProperty(() => HasErrors);
185
- }
186
-
187
-
188
- #region INotifyDataErrorInfo
189
- private readonly ErrorsContainer<string> errorsContainer;
190
- public bool HasErrors => errorsContainer.HasErrors;
191
- public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
192
- protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
193
- {
194
- if(!base.SetProperty(ref storage, value, propertyName)) return false;
195
-
196
- var context = new ValidationContext(this) { MemberName = propertyName };
197
- var errors = new List<ValidationResult>();
198
- if(!Validator.TryValidateProperty(value, context, errors))
199
- {
200
- errorsContainer.SetErrors(propertyName, errors.Select(x => x.ErrorMessage));
201
- }
202
- else
203
- {
204
- errorsContainer.ClearErrors(propertyName);
205
- }
206
- RaisePropertyChanged(nameof(HasErrors));
207
- return true;
208
- }
209
- public IEnumerable GetErrors(string propertyName) => errorsContainer.GetErrors(propertyName);
210
- #endregion
211
- }
212
- }
1
+ MVVMにこだわらないのであれば、こんな感じでどうでしょうか。
2
+ ```xml
3
+ <Window
4
+ x:Class="WpfApp1.SubWindow"
5
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7
+ xmlns:local="clr-namespace:WpfApp1"
8
+ Title="SubWindow"
9
+ Width="800"
10
+ Height="450">
11
+ <Window.DataContext>
12
+ <local:SubViewModel />
13
+ </Window.DataContext>
14
+ <Grid>
15
+ <Grid.RowDefinitions>
16
+ <RowDefinition />
17
+ <RowDefinition />
18
+ </Grid.RowDefinitions>
19
+ <TextBox Text="{Binding Text}" />
20
+ <Grid Grid.Row="1">
21
+ <Grid.ColumnDefinitions>
22
+ <ColumnDefinition />
23
+ <ColumnDefinition />
24
+ </Grid.ColumnDefinitions>
25
+ <Button Content="Cancel" IsCancel="True" />
26
+ <Button
27
+ Grid.Column="1"
28
+ Command="{Binding DoSomethingCommand}"
29
+ CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
30
+ Content="OK"
31
+ IsDefault="True" />
32
+ </Grid>
33
+ </Grid>
34
+ </Window>
35
+ ```
36
+
37
+ ```cs
38
+ using System.Windows;
39
+ using Prism.Commands;
40
+
41
+ namespace WpfApp1
42
+ {
43
+ public class SubViewModel
44
+ {
45
+ public string Text { get; set; }
46
+ public DelegateCommand<Window> DoSomethingCommand { get; }
47
+
48
+ public SubViewModel()
49
+ {
50
+ DoSomethingCommand = new DelegateCommand<Window>((w) =>
51
+ {
52
+ System.Diagnostics.Debug.WriteLine("DoSomethigCommand");
53
+
54
+ if(string.IsNullOrEmpty(Text))
55
+ {
56
+ MessageBox.Show("何か入力してください。");
57
+ }
58
+ else
59
+ {
60
+ w.DialogResult = true;
61
+ }
62
+ });
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ 実際には入力値はもっとあってそれぞれ検証したいのであれば、`INotifyDataErrorInfo`等を実装しエラーがあればボタンを押せなくするのがきれいかもしれません。
71
+
72
+ ```xml
73
+ <Window
74
+ x:Class="WpfApp1.SubWindow"
75
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
76
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
77
+ xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
78
+ xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
79
+ xmlns:local="clr-namespace:WpfApp1"
80
+ Name="SubWnd"
81
+ Title="SubWindow"
82
+ Width="800"
83
+ Height="450">
84
+ <Window.Resources>
85
+ <ControlTemplate x:Key="ValidationTemplate">
86
+ <StackPanel>
87
+ <ItemsControl HorizontalAlignment="Right" ItemsSource="{Binding AdornedElement.(Validation.Errors), ElementName=validationTarget}">
88
+ <ItemsControl.ItemTemplate>
89
+ <DataTemplate>
90
+ <TextBlock Foreground="Red" Text="{Binding ErrorContent}" />
91
+ </DataTemplate>
92
+ </ItemsControl.ItemTemplate>
93
+ </ItemsControl>
94
+ <AdornedElementPlaceholder x:Name="validationTarget" />
95
+ </StackPanel>
96
+ </ControlTemplate>
97
+ </Window.Resources>
98
+ <Window.DataContext>
99
+ <local:SubViewModel />
100
+ </Window.DataContext>
101
+ <DockPanel>
102
+ <StackPanel
103
+ HorizontalAlignment="Right"
104
+ DockPanel.Dock="Bottom"
105
+ Orientation="Horizontal">
106
+ <Button
107
+ MinWidth="80"
108
+ Margin="5"
109
+ Command="{Binding DoSomethingCommand}"
110
+ Content="OK">
111
+ <i:Interaction.Triggers>
112
+ <i:EventTrigger EventName="Click">
113
+ <ei:ChangePropertyAction
114
+ PropertyName="DialogResult"
115
+ TargetObject="{Binding ElementName=SubWnd}"
116
+ Value="True" />
117
+ </i:EventTrigger>
118
+ </i:Interaction.Triggers>
119
+ </Button>
120
+ <Button
121
+ MinWidth="80"
122
+ Margin="5"
123
+ Content="Cancel"
124
+ IsCancel="True" />
125
+ </StackPanel>
126
+
127
+ <StackPanel>
128
+ <HeaderedContentControl Margin="5" Header="Text1">
129
+ <TextBox Text="{Binding Text1, UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
130
+ </HeaderedContentControl>
131
+ <HeaderedContentControl Margin="5" Header="Text2">
132
+ <TextBox Text="{Binding Text2, UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
133
+ </HeaderedContentControl>
134
+ <HeaderedContentControl Margin="5" Header="Text3">
135
+ <TextBox Text="{Binding Text3, UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
136
+ </HeaderedContentControl>
137
+ </StackPanel>
138
+ </DockPanel>
139
+ </Window>
140
+ ```
141
+
142
+ ```cs
143
+ using System;
144
+ using System.Collections;
145
+ using System.Collections.Generic;
146
+ using System.ComponentModel;
147
+ using System.ComponentModel.DataAnnotations;
148
+ using System.Linq;
149
+ using System.Runtime.CompilerServices;
150
+ using Prism.Commands;
151
+ using Prism.Mvvm;
152
+
153
+ namespace WpfApp1
154
+ {
155
+ public class SubViewModel : BindableBase, INotifyDataErrorInfo
156
+ {
157
+ [Required(ErrorMessage = "何か入力してください。")]
158
+ public string Text1 { get => _Text1; set => SetProperty(ref _Text1, value); }
159
+ private string _Text1;
160
+
161
+ [MinLength(10, ErrorMessage = "10文字以上入力してください。")]
162
+ public string Text2 { get => _Text2; set => SetProperty(ref _Text2, value); }
163
+ private string _Text2;
164
+
165
+ [RegularExpression("[a-z]+", ErrorMessage = "a-zの文字列を入力してください。")]
166
+ public string Text3 { get => _Text3; set => SetProperty(ref _Text3, value); }
167
+ private string _Text3;
168
+
169
+ public DelegateCommand DoSomethingCommand { get; }
170
+
171
+
172
+ public SubViewModel()
173
+ {
174
+ errorsContainer = new ErrorsContainer<string>(x => ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(x)));
175
+
176
+ Text1 = ""; // ""に深い意味はない。初期値nullから動かしてValidatorを通してるだけ
177
+ Text2 = "";
178
+ Text3 = "A";
179
+
180
+ DoSomethingCommand = new DelegateCommand(
181
+ () => System.Diagnostics.Debug.WriteLine("DoSomethigCommand"),
182
+ () => !HasErrors)
183
+ .ObservesProperty(() => HasErrors);
184
+ }
185
+
186
+
187
+ #region INotifyDataErrorInfo
188
+ private readonly ErrorsContainer<string> errorsContainer;
189
+ public bool HasErrors => errorsContainer.HasErrors;
190
+ public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
191
+ protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
192
+ {
193
+ if(!base.SetProperty(ref storage, value, propertyName)) return false;
194
+
195
+ var context = new ValidationContext(this) { MemberName = propertyName };
196
+ var errors = new List<ValidationResult>();
197
+ if(!Validator.TryValidateProperty(value, context, errors))
198
+ {
199
+ errorsContainer.SetErrors(propertyName, errors.Select(x => x.ErrorMessage));
200
+ }
201
+ else
202
+ {
203
+ errorsContainer.ClearErrors(propertyName);
204
+ }
205
+ RaisePropertyChanged(nameof(HasErrors));
206
+ return true;
207
+ }
208
+ public IEnumerable GetErrors(string propertyName) => errorsContainer.GetErrors(propertyName);
209
+ #endregion
210
+ }
211
+ }
213
212
  ```