WPF Doğrulama Hatalarını Algılama


115

WPF'de, ExceptionValidationRuleveya öğesini kullanarak Veri Bağlama sırasında Veri Katmanınızda atılan hatalara dayalı doğrulamayı ayarlayabilirsiniz DataErrorValidationRule.

Bu şekilde ayarlanmış bir sürü kontrolünüz olduğunu ve bir Kaydet düğmeniz olduğunu varsayalım. Kullanıcı Kaydet düğmesine tıkladığında, kaydetme işlemine devam etmeden önce doğrulama hatası olmadığından emin olmanız gerekir. Doğrulama hataları varsa, onlara bağırmak istersiniz.

WPF'de, Veri Bağlantısı denetimlerinizden herhangi birinde doğrulama hataları ayarlanmış olup olmadığını nasıl anlarsınız?

Yanıtlar:


137

Bu gönderi son derece yardımcı oldu. Emeği geçen herkese çok teşekkür ederiz. İşte seveceğiniz ya da nefret edeceğiniz bir LINQ sürümü.

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);
}

1
Bu özel çözümü çok seviyorum!
ChristopheD

Sadece bu konuyu tökezledi. Çok kullanışlı küçük işlev. Teşekkürler!
Olav Haugen

Yalnızca belirli bir DataContext'e bağlı olan DependencyObjects numaralandırmanın bir yolu var mı? Treewalk fikrini sevmiyorum. Belirli bir veri kaynağına bağlı bir bağlantı koleksiyonu olabilir.
ZAB

5
Merak ediyorum, IsValidişlevi nasıl çağırırsınız ? CanExecuteKaydet düğmesinin komutuyla ilgili olduğunu tahmin edebileceğim bir ayarlamışsınız . Komut kullanmıyorsam bu işe yarayacak mı? Ve düğme, kontrol edilmesi gereken diğer kontrollerle nasıl ilişkilidir? Bunun nasıl kullanılacağına dair tek düşüncem, IsValiddoğrulanması gereken her bir kontrolü çağırmaktır . Düzenleme: Görünüşe sendergöre, kaydet düğmesi olmasını beklediğimi onaylıyorsunuz. Bu bana doğru görünmüyor.
Nicholas Miller

1
@Nick Miller a Windowaynı zamanda bir bağımlılık nesnesidir. Muhtemelen bunu bir tür olay işleyicisiyle ayarlıyor Window. Alternatif olarak, sadece doğrudan diyebiliriz IsValid(this)gelen Windowsınıfından.
akousmata

47

Aşağıdaki kod (Chris Sell & Ian Griffiths'in Programlama WPF kitabından) bir bağımlılık nesnesi ve alt öğelerine ilişkin tüm bağlayıcı kuralları doğrular:

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;
    }

}

Bunu, sayfanızda / pencerenizde bu şekilde kaydet düğmesi tıklama olay işleyicinizde çağırabilirsiniz

private void saveButton_Click(object sender, RoutedEventArgs e)
{

  if (Validator.IsValid(this)) // is valid
   {

    ....
   }
}

33

ListBox kullanırken gönderilen kod benim için çalışmadı. Yeniden yazdım ve şimdi çalışıyor:

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;
}

1
ItemsControl'üm üzerinde çalışmak için çözümünüze oy verin.
Jeff T.

1
Datagrid'imin doğrulama hataları olup olmadığını kontrol etmek için bu çözümü kullanıyorum. Bununla birlikte, bu yöntem benim viewmodel komutum canexecute yönteminde çağrılıyor ve görsel ağaç nesnelerine erişimin bir şekilde MVVM patterini ihlal ettiğini düşünüyorum, değil mi? Herhangi Bir Alternatif?
Igor Kondrasovas

16

Aynı sorunu yaşadım ve sağlanan çözümleri denedim. H-Man2 ve skiba_k çözümlerinin bir kombinasyonu benim için neredeyse iyi çalıştı, bir istisna dışında: My Window'da bir TabControl var. Ve doğrulama kuralları yalnızca şu anda görünen TabItem için değerlendirilir. Bu yüzden VisualTreeHelper'ı LogicalTreeHelper ile değiştirdim. Şimdi çalışıyor.

    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;
    }

7

Dean'in harika LINQ uygulamasına ek olarak, kodu DependencyObjects için bir uzantıya sararken eğlendim:

public static bool IsValid(this DependencyObject instance)
{
   // Validate recursivly
   return !Validation.GetHasError(instance) &&  LogicalTreeHelper.GetChildren(instance).OfType<DependencyObject>().All(child => child.IsValid());
}

Bu, yeniden kullanılabilirliği göz önünde bulundurarak son derece güzel kılar.


2

Küçük bir optimizasyon öneririm.

Bunu aynı kontroller üzerinden birçok kez yaparsanız, doğrulama kurallarına sahip kontrollerin bir listesini tutmak için yukarıdaki kodu ekleyebilirsiniz. Daha sonra geçerliliği kontrol etmeniz gerektiğinde, tüm görsel ağaç yerine yalnızca bu kontrollerin üzerinden geçin. Bu tür birçok kontrolünüz varsa, bu çok daha iyi olacaktır.


2

İşte WPF'de form doğrulama için bir kitaplık . Nuget paketi burada .

Örneklem:

<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>

Buradaki fikir, ekli özellik aracılığıyla ona hangi giriş kontrollerinin izleneceğini söyleyen bir doğrulama kapsamı tanımlamamızdır. O zaman yapabiliriz:

<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>

0

Tüm denetimler ağacınızı yinelemeli olarak yineleyebilir ve ekli Validation.HasErrorProperty özelliğini kontrol edebilir, ardından içinde bulduğunuz ilk özelliğe odaklanabilirsiniz.

Ayrıca önceden yazılmış birçok çözümü de kullanabilirsiniz , bir örnek ve daha fazla bilgi için bu konuyu kontrol edebilirsiniz


0

WPF Uygulama Çerçevesi'nin (WAF) BookLibrary örnek uygulaması ilginizi çekebilir . WPF'de doğrulamanın nasıl kullanılacağını ve doğrulama hataları olduğunda Kaydet düğmesinin nasıl kontrol edileceğini gösterir.


0

Cevap formunda aogan, doğrulama kuralları aracılığıyla açıkça yinelemek yerine, daha iyi 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;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.