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

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

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

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

Q&A

解決済

3回答

189閲覧

WPF(XAML)でエラーメッセージ表示をする際、レイアウトを変更したい

kikiinu

総合スコア24

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

0グッド

0クリップ

投稿2025/03/26 08:12

実現したいこと

実現したいこと : 下の図のようにエラーメッセージが表示されると下のコントロール(ラベル : 出身地とそのTextBox)が下にずれてほしい
イメージ説明

今は下の2枚の図の動きです
入力前
イメージ説明

入力後 : エラーメッセージを表示すると下のコントロールと重なっている!!
イメージ説明

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

ControlTemplate内で実現できるかどうかがわからない

該当のソースコード

WPF(XAML)

1<Window x:Class="Test.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:local="clr-namespace:Test" 5 Title="MainWindow" 6 Width="525" 7 Height="350"> 8 <Window.DataContext> 9 <local:MainWindowViewModel /> 10 </Window.DataContext> 11 <Window.Resources> 12 <ControlTemplate x:Key="ValidationTemplate"> 13 <StackPanel> 14 <AdornedElementPlaceholder x:Name="adornedelem" /> 15 <TextBlock Foreground="Red" Text="{Binding AdornedElement.(Validation.Errors)[0].ErrorContent, ElementName=adornedelem}" /> 16 </StackPanel> 17 </ControlTemplate> 18 </Window.Resources> 19 <Grid> 20 <Grid.ColumnDefinitions> 21 <ColumnDefinition Width="*"/> 22 <ColumnDefinition Width="Auto"/> 23 <ColumnDefinition Width="*"/> 24 <ColumnDefinition Width="*"/> 25 </Grid.ColumnDefinitions> 26 <Grid.RowDefinitions> 27 <RowDefinition Height="auto"/> 28 <RowDefinition Height="auto"/> 29 </Grid.RowDefinitions> 30 31 <Label Grid.Row="0" Grid.Column="1" Content="年齢" /> 32 <TextBox Grid.Row="0" Grid.Column="2" 33 Text="{Binding InputString, 34 UpdateSourceTrigger=PropertyChanged}" 35 Validation.ErrorTemplate="{StaticResource ValidationTemplate}" /> 36 37 <Label Grid.Row="1" Grid.Column="1" Content="出身地" /> 38 <TextBox Grid.Row="1" Grid.Column="2" 39 Text="{Binding Area, 40 UpdateSourceTrigger=PropertyChanged}" 41 Validation.ErrorTemplate="{StaticResource ValidationTemplate}" /> 42 </Grid> 43</Window>

試したこと・調べたこと

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

年齢を入力するTextBoxの下にエラーメッセージ表示用のLabelをVisibility="Collapsed"にしてエラーが発生したらVisibility="Visible"にすればうまくいくのかなと思っているのですがControlTemplate内からエラーメッセージ表示用のLabelのアクセスの仕方がわかりません

名前を付けて指定するようなやり方ではなく他でも使用できるように汎用性があるやり方があれば教えていただきたいです

補足

特になし

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

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

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

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

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

guest

回答3

0

ValidationAdornerSiteForのような得体の知れないものは使わない、基本的なバインドのみの素朴な実装です^^;
テーマ変更時にErrorTemplateAdornerごと消えてしまう不具合?があるようなのでErrorTemplateも使いません。

使用側の記述が非常にシンプルで使いやすくなっております。

  • LabelTextBoxを一体で扱える
  • Gridが不要で項目の追加や入れ替えが容易
  • アクセスキーに対応かつLabel.Targetの記述も不要

これならわたしも使いたい(項目が多ければ多いほどうれしいw

xml

1<Window 2 x:Class="Qo1g4oys53jcubr.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:Qo1g4oys53jcubr" 6 Width="525" 7 Height="350" 8 ThemeMode="System"> 9 <Window.DataContext> 10 <local:MainWindowViewModel /> 11 </Window.DataContext> 12 <Window.Resources> 13 <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> 14 15 <!-- GroupBoxなのがポイント(HeaderedContentControlではダメ) --> 16 <Style x:Key="TextBoxStyle" TargetType="GroupBox"> 17 <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" /> 18 19 <!-- HeaderTemplate・ContentTemplateは華麗にスルー --> 20 <Setter Property="Template"> 21 <Setter.Value> 22 <ControlTemplate TargetType="GroupBox"> 23 <Grid> 24 <Grid.ColumnDefinitions> 25 <ColumnDefinition Width="Auto" SharedSizeGroup="a" /> 26 <ColumnDefinition Width="4" /> 27 <ColumnDefinition /> 28 </Grid.ColumnDefinitions> 29 <Grid.RowDefinitions> 30 <RowDefinition /> 31 <RowDefinition Height="Auto" /> 32 </Grid.RowDefinitions> 33 <Label VerticalAlignment="Center" Content="{TemplateBinding Header}" /> 34 35 <TextBox Grid.Column="2" Text="{Binding Content, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}" /> 36 37 <!-- 本題には関係ないがアクセント色を変えたらオシャレかな?と上にかぶせる雑実装w --> 38 <Border 39 x:Name="border" 40 Grid.Column="2" 41 BorderBrush="{DynamicResource SystemFillColorCriticalBrush}" 42 BorderThickness="0,0,0,2" 43 CornerRadius="{StaticResource ControlCornerRadius}" 44 Visibility="{Binding (Validation.HasError), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}" /> 45 46 <ItemsControl 47 Grid.Row="1" 48 Grid.Column="2" 49 ItemsSource="{Binding (Validation.Errors), RelativeSource={RelativeSource TemplatedParent}}" 50 Visibility="{Binding Visibility, ElementName=border}"> 51 <ItemsControl.ItemTemplate> 52 <DataTemplate> 53 <BulletDecorator TextBlock.Foreground="{DynamicResource SystemFillColorCriticalBrush}"> 54 <BulletDecorator.Bullet> 55 <!-- ⚠️ --> 56 <TextBlock FontFamily="{StaticResource SymbolThemeFontFamily}" Text="&#xE814; " /> 57 </BulletDecorator.Bullet> 58 <TextBlock Text="{Binding ErrorContent}" TextWrapping="Wrap" /> 59 </BulletDecorator> 60 </DataTemplate> 61 </ItemsControl.ItemTemplate> 62 </ItemsControl> 63 </Grid> 64 </ControlTemplate> 65 </Setter.Value> 66 </Setter> 67 </Style> 68 </Window.Resources> 69 70 <Grid Margin="8"> 71 72 <!-- この中に項目を詰めていくイメージ --> 73 <StackPanel Grid.IsSharedSizeScope="True"> 74 <StackPanel.Resources> 75 76 <!-- スタイルを暗黙的に適用する --> 77 <Style BasedOn="{StaticResource TextBoxStyle}" TargetType="GroupBox" /> 78 </StackPanel.Resources> 79 80 <!-- 超すっきりした記述🤩 --> 81 <GroupBox Content="{Binding Value1, Mode=TwoWay}" Header="値_1" /> 82 <GroupBox Content="{Binding Value2, Mode=TwoWay}" Header="あたい_2" /> 83 </StackPanel> 84 </Grid> 85</Window>

cs

1using System.ComponentModel.DataAnnotations; 2using System.Windows; 3using CommunityToolkit.Mvvm.ComponentModel; 4 5namespace Qo1g4oys53jcubr; 6 7public partial class MainWindow : Window 8{ 9 public MainWindow() => InitializeComponent(); 10} 11 12public partial class MainWindowViewModel : ObservableValidator 13{ 14 [ObservableProperty, NotifyDataErrorInfo] 15 [Required(ErrorMessage = "何か入力してください")] 16 [StringLength(10, ErrorMessage = "10文字以内で入力してください\naaaaaa")] 17 [RegularExpression("[a-z]+", ErrorMessage = "a-zの文字列を入力してください")] 18 private string? value1; 19 20 [ObservableProperty, NotifyDataErrorInfo] 21 [Required(ErrorMessage = "何か入力してください")] 22 private string? value2; 23}

イメージ説明

投稿2025/03/29 02:14

TN8001

総合スコア9990

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

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

TN8001

2025/03/29 02:14

> もうちょっと使いやすくしたいところです(アイデアはあるのでうまくいったら追記します) 1万文字を超えるので別回答にさせていただきました。
kikiinu

2025/03/29 02:39

ありがとうございます!!凄すぎます!! こんなことができるんですね。XAML始めたばっかりで正直難しすぎて諦めていましたが、教えて頂いたコードを理解できるように勉強します ザムラーの皆さんはこのレベルが普通なんでしょうか? XAMLがこんなに難しいと、流行るとは思わないんですけど(笑)。どうなんでしょうか? 明日から1週間PC触れないですが、もう少しXAMLについて勉強してみます!! ありがとうございました
TN8001

2025/03/29 04:14

> こんなことができるんですね。XAML始めたばっかりで正直難しすぎて諦めていましたが、教えて頂いたコードを理解できるように勉強します 回答の不明点はお気軽にコメントください^^ > ザムラーの皆さんはこのレベルが普通なんでしょうか? う~んどうなんでしょうね? わたしは見た目にこだわりがあるほうなので、テンプレートで既存のコントロールを改変し放題のWPFが楽しくて、いつの間にか詳しくなっちゃった感じですw > XAMLがこんなに難しいと、流行るとは思わないんですけど(笑)。どうなんでしょうか? XAMLはWinUIやMAUI・Avalonia・Unoでも使われるので、覚えて損はない技術です。 その中でもWPFは元となるもの(Silverlight?知らんな~w)ですし、情報も多いので個人的には一番おすすめです。 「一生WinForm使ってやるっ」というのでなければ、避けては通れません^^; 例えばWinFormでもオーナードローとかやりだすと、難しさレベルは大差ない気はします。 「見た目なんかどうでもいいんだよ」と言われると言い返せないですがw 流行ってるかどうかはわかりませんが、最近WinFormから移行してるような方の記事やツイート等増えてきてる気がします。 まあ好意的だったりディスっていたりと反応はそれぞれですがw --- xamlが冗長になるのを厭わなければ、例えば↓のような3つを組で使うだけで用は足りています。 <Label Grid.Column="1" Content="年齢" /> <TextBox x:Name="textBox1" Grid.Column="2" Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=textBox1}" Visibility="{Binding (Validation.HasError), ElementName=textBox1, Converter={StaticResource BooleanToVisibilityConverter}}" /> でもこれが10セット20セットとなるとつらいですし、テンプレートをうまく使うと劇的に短縮できて気持ちがいいです^^
guest

0

自己解決

色々考えるより数行で対応できたのでコードビハインドで実装しました

まず、自作のTextBlockErrorMessageを作成しました

C#

1コード 2using System; 3using System.Collections.Generic; 4using System.ComponentModel; 5using System.Diagnostics; 6using System.Linq; 7using System.Text; 8using System.Threading.Tasks; 9using System.Windows; 10using System.Windows.Controls; 11using System.Windows.Data; 12using System.Windows.Documents; 13using System.Windows.Input; 14using System.Windows.Media; 15using System.Windows.Media.Imaging; 16using System.Windows.Navigation; 17using System.Windows.Shapes; 18 19namespace SeikouCustomControl 20{ 21 /// <summary> 22 /// このカスタム コントロールを XAML ファイルで使用するには、手順 1a または 1b の後、手順 2 に従います。 23 /// 24 /// 手順 1a) 現在のプロジェクトに存在する XAML ファイルでこのカスタム コントロールを使用する場合 25 /// この XmlNamespace 属性を使用場所であるマークアップ ファイルのルート要素に 26 /// 追加します: 27 /// 28 /// xmlns:MyNamespace="clr-namespace:SeikouCustomControl" 29 /// 30 /// 31 /// 手順 1b) 異なるプロジェクトに存在する XAML ファイルでこのカスタム コントロールを使用する場合 32 /// この XmlNamespace 属性を使用場所であるマークアップ ファイルのルート要素に 33 /// 追加します: 34 /// 35 /// xmlns:MyNamespace="clr-namespace:SeikouCustomControl;assembly=SeikouCustomControl" 36 /// 37 /// また、XAML ファイルのあるプロジェクトからこのプロジェクトへのプロジェクト参照を追加し、 38 /// リビルドして、コンパイル エラーを防ぐ必要があります: 39 /// 40 /// ソリューション エクスプローラーで対象のプロジェクトを右クリックし、 41 /// [参照の追加] の [プロジェクト] を選択してから、このプロジェクトを参照し、選択します。 42 /// 43 /// 44 /// 手順 2) 45 /// コントロールを XAML ファイルで使用します。 46 /// 47 /// <MyNamespace:TextBlockErrorMessage/> 48 /// 49 /// </summary> 50 public class TextBlockErrorMessage : TextBlock 51 { 52 static TextBlockErrorMessage() 53 { 54 DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBlockErrorMessage), new FrameworkPropertyMetadata(typeof(TextBlockErrorMessage))); 55 56 } 57 58 public TextBlockErrorMessage() 59 { 60 var descripter = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock)); 61 this.Visibility = Visibility.Collapsed; 62 descripter.AddValueChanged(this, OnTextChanged); 63 } 64 65 private static void OnTextChanged(object? sender, EventArgs e) 66 { 67 var textBlock = (TextBlock)sender; 68 if (string.IsNullOrEmpty(textBlock.Text)) 69 { 70 textBlock.Visibility = Visibility.Collapsed; 71 } 72 else 73 { 74 textBlock.Visibility = Visibility.Visible; 75 } 76 } 77 78 } 79} 80

自作のTextBlockErrorMessageを配置します

WPF

1<Window 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:local="clr-namespace:Test" xmlns:sys="http://schemas.microsoft.com/winfx/2009/xaml" 5 xmlns:scc="clr-namespace:SeikouCustomControl;assembly=SeikouCustomControl" 6 x:Class="Test.MainWindow" 7 Title="MainWindow" 8 Width="525" 9 Height="350"> 10 <Window.Resources> 11 </Window.Resources> 12 <Window.DataContext> 13 <local:MainWindowViewModel /> 14 </Window.DataContext> 15 <Grid> 16 <Grid.ColumnDefinitions> 17 <ColumnDefinition Width="*"/> 18 <ColumnDefinition Width="Auto"/> 19 <ColumnDefinition Width="*"/> 20 <ColumnDefinition Width="*"/> 21 </Grid.ColumnDefinitions> 22 <Grid.RowDefinitions> 23 <RowDefinition Height="auto"/> 24 <RowDefinition Height="auto"/> 25 <RowDefinition Height="auto"/> 26 </Grid.RowDefinitions> 27 28 <Label Grid.Row="0" Grid.Column="1" Content="年齢" /> 29 <TextBox Grid.Row="0" Grid.Column="2" 30 Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}"/> 31 32 <scc:TextBlockErrorMessage Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="100" 33 Text="{Binding InputStringErrorMessage}" 34 /> 35 </Grid> 36</Window>

ViewModelにプロパティを作ります

C#

1using System.ComponentModel.DataAnnotations; 2using System.Runtime.CompilerServices; 3using Test; 4 5namespace Test 6{ 7 public class MainWindowViewModel : ValidateableBase 8 { 9 private string inputString = string.Empty; 10 [Required(ErrorMessage = "年齢 : 何か入力してください")] 11 [StringLength(10, ErrorMessage = "年齢 : \r\n10文字以内で\r\n入力してください")] 12 public string InputString 13 { 14 get { return inputString; } 15 set 16 { 17 SetPropertyWithErrorMessage(ref this.inputString, value); 18 } 19 } 20 21 private string _InputStringErrorMessage = string.Empty; 22 public string InputStringErrorMessage 23 { 24 get { return _InputStringErrorMessage; } 25 private set { this.SetProperty(ref _InputStringErrorMessage, value); } 26 } 27 28 public MainWindowViewModel() 29 { 30 } 31 } 32} 33

次に汎用性を持たせてSetPropertyWithErrorMessageメソッドを作りました
これがベストなのかはわかりません。

C#

1using System.Collections; 2using System.ComponentModel; 3using System.ComponentModel.DataAnnotations; 4using System.Reflection; 5using System.Runtime.CompilerServices; 6 7namespace Test 8{ 9 public abstract class ValidateableBase : BindableBase, INotifyDataErrorInfo 10 { 11 /// <summary> 12 /// プロパティが既に目的の値と一致しているかどうかを確認します。必要な場合のみ、 13 /// プロパティを設定し、リスナーに通知します。 14 /// その後、プロパティの入力値検証を行います。 15 /// </summary> 16 /// <typeparam name="T">プロパティの型。</typeparam> 17 /// <param name="storage">get アクセス操作子と set アクセス操作子両方を使用したプロパティへの参照。</param> 18 /// <param name="value">プロパティに必要な値。</param> 19 /// <param name="propertyName">リスナーに通知するために使用するプロパティの名前。 20 /// この値は省略可能で、 21 /// CallerMemberName をサポートするコンパイラから呼び出す場合に自動的に指定できます。</param> 22 /// <returns>値が変更された場合は true、既存の値が目的の値に一致した場合は 23 /// false です。</returns> 24 protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) 25 { 26 var isChanged = base.SetProperty(ref storage, value, propertyName); 27 if (isChanged) 28 this.ValidateProperty(value, propertyName); 29 30 return isChanged; 31 } 32 33 protected Dictionary<string, PropertyInfo?> _properties = new Dictionary<string, PropertyInfo?>(); 34 35 protected void SetPropertyWithErrorMessage<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) 36 { 37 this.SetProperty(ref storage, value, propertyName); 38 39 var errorMessage = GetErrorMessage("\r\n", propertyName); 40 41 // プロパティ情報の取得 42 if (!_properties.TryGetValue(propertyName, out var property)) 43 { 44 property = this.GetType().GetProperty(propertyName + "ErrorMessage"); 45 _properties[propertyName] = property; 46 } 47 48 // インスタンスに値を設定 49 property?.SetValue(this, errorMessage); 50 } 51 private string GetErrorMessage(string? messageSeparator, String propertyName) 52 { 53 var errors = (List<string>)GetErrors(propertyName); 54 if (errors != null) 55 { 56 var ss = new string[errors.Count]; 57 errors.CopyTo(ss); 58 return string.Join(messageSeparator, ss); 59 } 60 61 return string.Empty; 62 } 63 64 protected void ValidateProperty(object value, [CallerMemberName] string propertyName = null) 65 { 66 var context = new ValidationContext(this) { MemberName = propertyName }; 67 var validationErrors = new List<ValidationResult>(); 68 if (!Validator.TryValidateProperty(value, context, validationErrors)) 69 { 70 var errors = validationErrors.Select(error => error.ErrorMessage); 71 foreach (var error in errors) 72 { 73 AddError(propertyName, error); 74 } 75 } 76 else 77 { 78 RemoveError(propertyName); 79 } 80 } 81 82 #region 発生中のエラーを保持する処理を実装 83 readonly Dictionary<string, List<string>> _currentErrors = new Dictionary<string, List<string>>(); 84 85 protected void AddError(string propertyName, string error) 86 { 87 if (!_currentErrors.ContainsKey(propertyName)) 88 _currentErrors[propertyName] = new List<string>(); 89 90 if (!_currentErrors[propertyName].Contains(error)) 91 { 92 _currentErrors[propertyName].Add(error); 93 OnErrorsChanged(propertyName); 94 } 95 } 96 97 protected void RemoveError(string propertyName) 98 { 99 if (_currentErrors.ContainsKey(propertyName)) 100 _currentErrors.Remove(propertyName); 101 102 OnErrorsChanged(propertyName); 103 } 104 #endregion 105 106 private void OnErrorsChanged(string propertyName) 107 { 108 var h = this.ErrorsChanged; 109 if (h != null) 110 { 111 h(this, new DataErrorsChangedEventArgs(propertyName)); 112 } 113 } 114 115 #region INotifyDataErrorInfoの実装 116 public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; 117 118 public IEnumerable GetErrors(string propertyName) 119 { 120 if (string.IsNullOrEmpty(propertyName) || 121 !_currentErrors.ContainsKey(propertyName)) 122 return null; 123 124 return _currentErrors[propertyName]; 125 } 126 127 public bool HasErrors 128 { 129 get { return _currentErrors.Count > 0; } 130 } 131 #endregion 132 } 133} 134

投稿2025/03/27 10:59

編集2025/03/28 08:48
kikiinu

総合スコア24

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

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

0

参考サイトは質問に明示してください。
WPFでの入力値検証・その4 ~ErrorTemplateで検証結果の表示をカスタマイズ~ - SourceChord

年齢を入力するTextBoxの下にエラーメッセージ表示用のLabelをVisibility="Collapsed"にしてエラーが発生したらVisibility="Visible"にすればうまくいくのかなと思っているのですが

TextBoxControlTemplateをいじればそんな感じのこともできますが、(無駄に)長くなってしまいます。
単にエラー状態だったら、下マージンを広げるだけでいいのでは?
アプリ画像

xml

1<Window 2 x:Class="Qo1g4oys53jcubr.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:Qo1g4oys53jcubr" 6 Width="525" 7 Height="350"> 8 <Window.DataContext> 9 <local:MainWindowViewModel /> 10 </Window.DataContext> 11 <Window.Resources> 12 <ControlTemplate x:Key="ValidationTemplate"> 13 <StackPanel> 14 <AdornedElementPlaceholder x:Name="adornedelem" /> 15 <TextBlock Foreground="Red" Text="{Binding AdornedElement.(Validation.Errors)[0].ErrorContent, ElementName=adornedelem}" /> 16 </StackPanel> 17 </ControlTemplate> 18 19 <Style TargetType="TextBox"> 20 <Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}" /> 21 <Style.Triggers> 22 <Trigger Property="Validation.HasError" Value="True"> 23 <Setter Property="Margin" Value="0,0,0,24" /> 24 </Trigger> 25 </Style.Triggers> 26 </Style> 27 28 <!-- 29 Fluentテーマ適用時 30 [WPF for .NET 9 の新機能 - WPF .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/whats-new/net90?view=netdesktop-9.0#fluent-theme) 31 --> 32 <!--<Style BasedOn="{StaticResource DefaultTextBoxStyle}" TargetType="TextBox"> 33 <Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}" /> 34 <Style.Triggers> 35 <Trigger Property="Validation.HasError" Value="True"> 36 <Setter Property="Margin" Value="0,0,0,24" /> 37 </Trigger> 38 </Style.Triggers> 39 </Style>--> 40 </Window.Resources> 41 42 <Grid> 43 <Grid.ColumnDefinitions> 44 <ColumnDefinition /> 45 <ColumnDefinition Width="Auto" /> 46 <ColumnDefinition /> 47 <ColumnDefinition /> 48 </Grid.ColumnDefinitions> 49 <Grid.RowDefinitions> 50 <RowDefinition Height="auto" /> 51 <RowDefinition Height="auto" /> 52 </Grid.RowDefinitions> 53 54 <Label Grid.Column="1" Content="年齢" /> 55 <TextBox Grid.Column="2" Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}" /> 56 57 <Label 58 Grid.Row="1" 59 Grid.Column="1" 60 Content="出身地" /> 61 <TextBox 62 Grid.Row="1" 63 Grid.Column="2" 64 Text="{Binding Area, UpdateSourceTrigger=PropertyChanged}" /> 65 </Grid> 66</Window>

cs

1using System.ComponentModel.DataAnnotations; 2using System.Windows; 3using CommunityToolkit.Mvvm.ComponentModel; 4 5namespace Qo1g4oys53jcubr; 6 7public partial class MainWindow : Window 8{ 9 public MainWindow() => InitializeComponent(); 10} 11 12public partial class MainWindowViewModel : ObservableValidator 13{ 14 [ObservableProperty, NotifyDataErrorInfo] 15 [Required(ErrorMessage = "何か入力してください")] 16 [StringLength(10, ErrorMessage = "10文字以内で入力してください")] 17 private string? inputString; 18 19 [ObservableProperty, NotifyDataErrorInfo] 20 [Required(ErrorMessage = "何か入力してください")] 21 private string? area; 22}

NuGet Gallery | CommunityToolkit.Mvvm
MVVM Toolkit の概要 - Community Toolkits for .NET | Microsoft Learn


年齢を入力するTextBoxの下にエラーメッセージ表示用のLabelをVisibility="Collapsed"にしてエラーが発生したらVisibility="Visible"にすればうまくいく

をxamlで愚直に実装しました。
TextBox分用意しなくてはならずValidationAdornerSiteの設定も面倒なので、1,2個までかなぁという気はします(TextBoxTemplateをいじるよりはマシかw

Validation.ValidationAdornerSite 添付プロパティ (System.Windows.Controls) | Microsoft Learn
Validation.ValidationAdornerSiteFor 添付プロパティ (System.Windows.Controls) | Microsoft Learn
Nine Works 例外を利用したデータ検証

厳密にやるなら複数エラーにも対応すべきでしょう。
WPFでの入力値検証・その6 ~複数のエラー表示への対応~ - SourceChord

xml

1<Window 2 x:Class="Qo1g4oys53jcubr.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:Qo1g4oys53jcubr" 6 Width="525" 7 Height="350" 8 ThemeMode="System"> 9 <Window.DataContext> 10 <local:MainWindowViewModel /> 11 </Window.DataContext> 12 <Window.Resources> 13 <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> 14 15 <Style x:Key="ErrorStyle" TargetType="{x:Type ItemsControl}"> 16 <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" /> 17 <Setter Property="ItemsSource" Value="{Binding (Validation.ValidationAdornerSiteFor).(Validation.Errors), RelativeSource={RelativeSource Self}}" /> 18 <Setter Property="Visibility" Value="{Binding (Validation.ValidationAdornerSiteFor).(Validation.HasError), Converter={StaticResource BooleanToVisibilityConverter}, RelativeSource={RelativeSource Self}}" /> 19 <Setter Property="ItemTemplate"> 20 <Setter.Value> 21 <DataTemplate> 22 <BulletDecorator TextBlock.Foreground="Red"> 23 <BulletDecorator.Bullet> 24 <TextBlock Text="! " /> 25 </BulletDecorator.Bullet> 26 <TextBlock Text="{Binding ErrorContent}" TextWrapping="Wrap" /> 27 </BulletDecorator> 28 </DataTemplate> 29 </Setter.Value> 30 </Setter> 31 </Style> 32 </Window.Resources> 33 34 <Grid> 35 <Grid.ColumnDefinitions> 36 <ColumnDefinition Width="Auto" /> 37 <ColumnDefinition /> 38 </Grid.ColumnDefinitions> 39 <Grid.RowDefinitions> 40 <RowDefinition Height="auto" /> 41 <RowDefinition Height="auto" /> 42 </Grid.RowDefinitions> 43 44 <Label Content="年齢" /> 45 <StackPanel Grid.Column="1"> 46 <TextBox 47 Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}" 48 Validation.ErrorTemplate="{x:Null}" 49 Validation.ValidationAdornerSite="{Binding ElementName=inputStringError}" /> 50 <ItemsControl x:Name="inputStringError" Style="{StaticResource ErrorStyle}" /> 51 </StackPanel> 52 53 <Label Grid.Row="1" Content="出身地" /> 54 <StackPanel Grid.Row="1" Grid.Column="1"> 55 <TextBox 56 Text="{Binding Area, UpdateSourceTrigger=PropertyChanged}" 57 Validation.ErrorTemplate="{x:Null}" 58 Validation.ValidationAdornerSite="{Binding ElementName=areaError}" /> 59 <ItemsControl x:Name="areaError" Style="{StaticResource ErrorStyle}" /> 60 </StackPanel> 61 </Grid> 62</Window>

cs

1using System.ComponentModel.DataAnnotations; 2using System.Windows; 3using CommunityToolkit.Mvvm.ComponentModel; 4 5namespace Qo1g4oys53jcubr; 6 7public partial class MainWindow : Window 8{ 9 public MainWindow() => InitializeComponent(); 10} 11 12public partial class MainWindowViewModel : ObservableValidator 13{ 14 [ObservableProperty, NotifyDataErrorInfo] 15 [Required(ErrorMessage = "何か入力してください")] 16 [StringLength(10, ErrorMessage = "10文字以内で入力してください\naaaaaa")] 17 [RegularExpression("[a-z]+", ErrorMessage = "a-zの文字列を入力してください")] 18 private string? inputString; 19 20 [ObservableProperty, NotifyDataErrorInfo] 21 [Required(ErrorMessage = "何か入力してください")] 22 private string? area; 23}

アプリ画像

投稿2025/03/26 09:50

編集2025/03/27 14:10
TN8001

総合スコア9990

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

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

kikiinu

2025/03/27 10:58

回答ありがとうございます 参考サイト載せるの忘れていました Marginを設定すれば確かにできますね!!恥ずかしながら気づきませんでした ただMarginに設定する値が求めないといけないのとその値を計算するのはどうかな?と感じました エラーが複数行あると、と考えるのが面倒くさくなりました(笑) 色々考えるより数行で対応できたのでコードビハインドで実装しました
TN8001

2025/03/27 14:13 編集

> ただMarginに設定する値が求めないといけないのとその値を計算するのはどうかな?と感じました > エラーが複数行あると、と考えるのが面倒くさくなりました(笑) 確かにねぇ~ ほとんどのサイトがツールチップなんかでごまかしている理由が分かりますねw 一応当初案を実現する方法を追記しました(ただわたし自身はあまり使う気にならないです^^; > 色々考えるより数行で対応できたのでコードビハインドで実装しました ナレッジになるように差し支えない範囲で、具体的なコードを書くとよいでしょう。 [ヘルプ|質問をした後に自己解決してしまった](https://teratail.com/help#resolve-myself)
kikiinu

2025/03/28 08:30 編集

実装してくださったのですね!!ありがとうございます!! ナレッジになるように。ナレッジがわかりませんでした(笑) 今後同じような悩みが出てくるかもしれないので私なりの対応を書いておきます
TN8001

2025/03/28 09:26

> 実装してくださったのですね!!ありがとうございます!! xamlこねくり回すのが好きなのでやってみましたが、ぶっちゃけいまいちですよねぇ^^; もうちょっと使いやすくしたいところです(アイデアはあるのでうまくいったら追記します) > ナレッジになるように。ナレッジがわかりませんでした(笑) すいません。わたしも全然できないくせに舶来語を使ってしまいましたw
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.32%

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

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

質問する

関連した質問