سؤال

في WPF، يمكنك إعداد التحقق من الصحة بناءً على الأخطاء التي تم طرحها في طبقة البيانات الخاصة بك أثناء ربط البيانات باستخدام ExceptionValidationRule أو DataErrorValidationRule.

لنفترض أن لديك مجموعة من عناصر التحكم التي تم إعدادها بهذه الطريقة وكان لديك زر حفظ.عندما ينقر المستخدم على زر حفظ، فإنك تحتاج إلى التأكد من عدم وجود أخطاء في التحقق من الصحة قبل متابعة الحفظ.إذا كانت هناك أخطاء في التحقق من الصحة، فأنت تريد الصراخ عليها.

في WPF، كيف يمكنك معرفة ما إذا كان أي من عناصر التحكم في Data Bound لديك يحتوي على مجموعة أخطاء في التحقق من الصحة؟

هل كانت مفيدة؟

المحلول

وكان هذا المنصب مفيدا للغاية.شكرا لجميع من ساهم.إليك إصدار 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);
}

نصائح أخرى

الكود التالي (من كتاب برمجة WPF من تأليف Chris Sell & Ian Griffiths) يتحقق من صحة جميع القواعد الملزمة على كائن التبعية وأبنائه:

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 بشكل جيد تقريبًا بالنسبة لي، باستثناء واحد:تحتوي نافذتي على 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;
    }

بالإضافة إلى التنفيذ الرائع لـ LINQ الذي قام به Dean، استمتعت بتغليف الكود في ملحق لـ 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>

يمكنك التكرار على جميع عناصر التحكم الخاصة بك بشكل متكرر والتحقق من الخاصية المرفقة Validation.HasErrorProperty، ثم التركيز على الخاصية الأولى التي تجدها فيها.

يمكنك أيضًا استخدام العديد من الحلول المكتوبة بالفعل التي يمكنك التحقق منها هذا موضوع للحصول على مثال ومزيد من المعلومات

قد تكون مهتمًا بـ مكتبة الكتب تطبيق عينة من إطار تطبيق WPF (WAF).يوضح كيفية استخدام التحقق من الصحة في 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;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top