通常XAMLアプリケーションを作成し、バインディングを利用しているとエラー通知にルールというものを使います。
ちなみにネット上にはまだ情報がないと思うので、最新のMSDNライブラリか日本語版のWindowsSDKを落として
データ バインディングの概要
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.NETDEVFX.v20.ja/wpf_conceptual/html/c707c95f-7811-401d-956e-2fffd019a211.htm
を見てみてください。下のほうです。
でこのエラー通知にToolTipを使う簡単な方法というのが書かれています。
<Style x:Key="textStyleTextBox" TargetType="TextBox">
<Setter Property="Foreground" Value="#333333" />
<Setter Property="MaxLength" Value="40" />
<Setter Property="Width" Value="392" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
この[0]が直書きされているパターンのTipsは結構どこにでも転がっています。
ただこれをそのまま利用してみると非常に問題が多いです。
ミニマムセットを作って実験してみると、入力エラーなどを発生させると以下のようなスタックトレースが出力にはかれます。
System.Windows.Data Error: 12 : Cannot get '' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1'). BindingExpression:Path=(0).[0].ErrorContent; DataItem='TextBox' (Name='textBox1'); target element is 'TextBox' (Name='textBox1'); target property is 'ToolTip' (type 'Object') TargetInvocationException:'System.Reflection.TargetInvocationException: 呼び出しのターゲットが例外をスローしました。 ---> System.ArgumentOutOfRangeException: インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメータ名: index
場所 System.ThrowHelper.ThrowArgumentOutOfRangeException()
場所 System.Collections.Generic.List`1.get_Item(Int32 index)
場所 System.Collections.ObjectModel.Collection`1.get_Item(Int32 index)
場所 System.Collections.ObjectModel.ReadOnlyCollection`1.get_Item(Int32 index)
--- 内部例外スタック トレースの終わり ---
場所 System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
場所 System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
場所 MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
場所 MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'
かんぜんにエラーのパターンから[0]としているけど、1件もないエラーが発生しています。
ドキュメント上はこれをするようにと書かれているにもかかわらずこんな状態ということからバグじゃないかと思います。
エラーが発生するとValidation.Errorsにエラーがたまって、Validation.HasErrorにフラグがたつと書かれているのですが、逆にしてしまっているのじゃないかと思います。
さて解決策。
このToolTipに出力しているのがただのバインド式ということなので、コンバータで解決します。
public class ErrorContentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ReadOnlyObservableCollection<ValidationError> col = value as ReadOnlyObservableCollection<ValidationError>;
if (col == null)
{
return "";
}
else
{
if (col.Count > 0)
{
return col[0].ErrorContent;
}
else
{
return "";
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
かようなコンバータを作成します。
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors), Converter={StaticResource ErrorContentConverter}}"/>
</Trigger>
一部リソース式などは省略しているので、そこはうまくやってください。
ということで、こうすれば当然の如くうまくいきますので、 みなさん対処してくださいね。