WPF 유효성 검사 오류 감지


115

WPF에서는이 설정 확인 데이터를 사용하여 바인딩 동안 데이터 계층에 던져 오류를 기반으로 할 수 있습니다 ExceptionValidationRule또는 DataErrorValidationRule.

이러한 방식으로 많은 컨트롤을 설정하고 저장 버튼이 있다고 가정합니다. 사용자가 저장 버튼을 클릭하면 저장을 진행하기 전에 유효성 검사 오류가 없는지 확인해야합니다. 유효성 검사 오류가있는 경우 소리를 지르고 싶을 것입니다.

WPF에서 Data Bound 컨트롤에 유효성 검사 오류가 설정되어 있는지 어떻게 알 수 있습니까?

답변:


137

이 게시물은 매우 도움이되었습니다. 기여해 주신 모든 분들께 감사드립니다. 다음은 당신이 좋아하거나 싫어할 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);
}

1
이 특정 솔루션을 많이 좋아합니다!
ChristopheD

이 스레드를 우연히 발견했습니다. 매우 유용한 작은 기능. 감사!
Olav Haugen 2011 년

특정 DataContext에 바인딩 된 DependencyObjects 만 열거하는 방법이 있습니까? 나는 treewalk의 아이디어를 좋아하지 않습니다. 특정 데이터 소스에 연결된 바인딩 모음이있을 수 있습니다.
ZAB 2014

5
IsValid함수를 어떻게 호출 합니까? CanExecute저장 버튼의 명령과 관련이 있다고 생각되는 설정을 확인했습니다 . 명령을 사용하지 않아도 작동합니까? 버튼은 확인해야하는 다른 컨트롤과 어떤 관련이 있습니까? 이것을 사용하는 방법에 대한 유일한 생각은 IsValid유효성 검사가 필요한 각 컨트롤을 호출 하는 것입니다. 편집 :sender 저장 버튼이 될 것으로 예상되는 것을 확인하는 것 같습니다 . 그것은 나에게 옳지 않은 것 같습니다.
Nicholas Miller

1
@Nick Miller a Window도 종속성 개체입니다. 나는 아마도 .NET Framework에서 일종의 이벤트 처리기로 설정하고있을 것입니다 Window. 또는 클래스 IsValid(this)에서 직접 호출 할 수 있습니다 Window.
akousmata

47

다음 코드 (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
   {

    ....
   }
}

33

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

1
내 ItemsControl 작업에 대한 솔루션에 찬성 투표하십시오.
Jeff T.

1
이 솔루션을 사용하여 데이터 그리드에 유효성 검사 오류가 있는지 확인합니다. 그러나이 메소드는 내 viewmodel 명령 canexecute 메소드에서 호출되며 시각적 트리 객체에 액세스하는 것이 MVVM 패턴을 위반한다고 생각합니다. 대안이 있습니까?
Igor Kondrasovas

16

동일한 문제가 있었고 제공된 솔루션을 시도했습니다. H-Man2와 skiba_k 솔루션의 조합은 거의 잘 작동했습니다. 단 한 가지 예외가 있습니다. My Window에는 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;
    }

7

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

이것은 재사용 가능성을 고려할 때 매우 좋습니다.


2

나는 작은 최적화를 제공 할 것입니다.

동일한 컨트롤에 대해이 작업을 여러 번 수행하는 경우 위 코드를 추가하여 실제로 유효성 검사 규칙이있는 컨트롤 목록을 유지할 수 있습니다. 그런 다음 유효성을 확인해야 할 때마다 전체 시각적 트리 대신 해당 컨트롤 만 살펴보십시오. 이러한 컨트롤이 많은 경우 훨씬 더 나은 것으로 입증됩니다.


2

다음은 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>

0

모든 컨트롤 트리를 재귀 적으로 반복하고 연결된 속성 Validation.HasErrorProperty를 확인한 다음 처음 찾은 항목에 집중할 수 있습니다.

이미 작성된 많은 솔루션을 사용할 수도 있습니다. 스레드에서 예제와 자세한 정보를 확인할 수 있습니다.


0

WPF 애플리케이션 프레임 워크 (WAF)BookLibrary 샘플 애플리케이션에 관심이있을 수 있습니다 . WPF에서 유효성 검사를 사용하는 방법과 유효성 검사 오류가있을 때 저장 단추를 제어하는 ​​방법을 보여줍니다.


0

대답 형식 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;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.