回答:
この投稿は非常に役に立ちました。貢献してくれたすべての人に感謝します。ここにあなたが好きか嫌いかのLINQバージョンがあります。
private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = IsValid(sender as DependencyObject);
}
private bool IsValid(DependencyObject obj)
{
// The dependency object is valid if it has no errors and all
// of its children (that are dependency objects) are error-free.
return !Validation.GetHasError(obj) &&
LogicalTreeHelper.GetChildren(obj)
.OfType<DependencyObject>()
.All(IsValid);
}
IsValid
関数をどのように呼び出しますか?CanExecute
[保存]ボタンのコマンドに関連すると思われる設定を行ったようです。コマンドを使用しない場合、これは機能しますか?そして、ボタンは、チェックする必要がある他のコントロールにどのように関連していますか?これを使用する方法について私の唯一の考えは、IsValid
検証する必要がある各コントロールを呼び出すことです。編集:sender
私が保存ボタンとして期待して いるものを検証しているようです。それは私には正しくないようです。
Window
も依存オブジェクトです。私はおそらく、何らかのイベントハンドラをに設定していますWindow
。または、クラスIsValid(this)
から直接呼び出すこともできますWindow
。
次のコード(Chris Sell&Ian GriffithsによるProgramming WPFブックから)は、依存関係オブジェクトとその子のすべてのバインディングルールを検証します。
public static class Validator
{
public static bool IsValid(DependencyObject parent)
{
// Validate all the bindings on the parent
bool valid = true;
LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
while (localValues.MoveNext())
{
LocalValueEntry entry = localValues.Current;
if (BindingOperations.IsDataBound(parent, entry.Property))
{
Binding binding = BindingOperations.GetBinding(parent, entry.Property);
foreach (ValidationRule rule in binding.ValidationRules)
{
ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
if (!result.IsValid)
{
BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
valid = false;
}
}
}
}
// Validate all the bindings on the children
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (!IsValid(child)) { valid = false; }
}
return valid;
}
}
あなたはあなたのページ/ウィンドウでこのような保存ボタンクリックイベントハンドラでこれを呼び出すことができます
private void saveButton_Click(object sender, RoutedEventArgs e)
{
if (Validator.IsValid(this)) // is valid
{
....
}
}
投稿されたコードは、ListBoxを使用すると機能しませんでした。私はそれを書き直し、今それは動作します:
public static bool IsValid(DependencyObject parent)
{
if (Validation.GetHasError(parent))
return false;
// Validate all the bindings on the children
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (!IsValid(child)) { return false; }
}
return true;
}
同じ問題があり、提供された解決策を試しました。H-Man2とskiba_kのソリューションの組み合わせは、1つの例外を除いて、ほとんど問題なく動作しました。MyWindowにはTabControlがあります。また、検証ルールは、現在表示されているTabItemに対してのみ評価されます。そこで、VisualTreeHelperをLogicalTreeHelperに置き換えました。今では動作します。
public static bool IsValid(DependencyObject parent)
{
// Validate all the bindings on the parent
bool valid = true;
LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
while (localValues.MoveNext())
{
LocalValueEntry entry = localValues.Current;
if (BindingOperations.IsDataBound(parent, entry.Property))
{
Binding binding = BindingOperations.GetBinding(parent, entry.Property);
if (binding.ValidationRules.Count > 0)
{
BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
expression.UpdateSource();
if (expression.HasError)
{
valid = false;
}
}
}
}
// Validate all the bindings on the children
System.Collections.IEnumerable children = LogicalTreeHelper.GetChildren(parent);
foreach (object obj in children)
{
if (obj is DependencyObject)
{
DependencyObject child = (DependencyObject)obj;
if (!IsValid(child)) { valid = false; }
}
}
return valid;
}
Deanの優れたLINQ実装に加えて、コードをDependencyObjectsの拡張機能にラップするのを楽しみました:
public static bool IsValid(this DependencyObject instance)
{
// Validate recursivly
return !Validation.GetHasError(instance) && LogicalTreeHelper.GetChildren(instance).OfType<DependencyObject>().All(child => child.IsValid());
}
これにより、再利用性を考えると非常に便利です。
これは、WPFでのフォーム検証用のライブラリです。Nugetパッケージはこちら。
サンプル:
<Border BorderBrush="{Binding Path=(validationScope:Scope.HasErrors),
Converter={local:BoolToBrushConverter},
ElementName=Form}"
BorderThickness="1">
<StackPanel x:Name="Form" validationScope:Scope.ForInputTypes="{x:Static validationScope:InputTypeCollection.Default}">
<TextBox Text="{Binding SomeProperty}" />
<TextBox Text="{Binding SomeOtherProperty}" />
</StackPanel>
</Border>
アイデアは、追跡する入力コントロールを伝える添付プロパティを介して検証スコープを定義することです。その後、次のことができます。
<ItemsControl ItemsSource="{Binding Path=(validationScope:Scope.Errors),
ElementName=Form}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type ValidationError}">
<TextBlock Foreground="Red"
Text="{Binding ErrorContent}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
WPFアプリケーションフレームワーク(WAF)のBookLibraryサンプルアプリケーションに興味があるかもしれません。WPFで検証を使用する方法と、検証エラーが存在する場合に[保存]ボタンを制御する方法を示します。
回答フォームaoganでは、検証ルールを明示的に繰り返すのではなく、呼び出すだけ expression.UpdateSource():
if (BindingOperations.IsDataBound(parent, entry.Property))
{
Binding binding = BindingOperations.GetBinding(parent, entry.Property);
if (binding.ValidationRules.Count > 0)
{
BindingExpression expression
= BindingOperations.GetBindingExpression(parent, entry.Property);
expression.UpdateSource();
if (expression.HasError) valid = false;
}
}