WPF 유효성 검사 오류 감지
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);
}
다음 코드(Chris Sell & Ian Griffiths의 프로그래밍 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의 솔루션 조합은 다음과 같은 한 가지 예외를 제외하고는 거의 문제 없이 작동했습니다.내 창에는 탭 컨트롤이 있습니다.그리고 유효성 검사 규칙은 현재 표시된 TabItem에 대해서만 평가됩니다.그래서 저는 Visual Tree Helper를 Logical Tree Helper로 대체했습니다.이제 효과가 있습니다.
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 구현 외에도, 저는 코드를 DependencyObjects의 확장으로 묶는 것이 즐거웠습니다.
public static bool IsValid(this DependencyObject instance)
{
// Validate recursivly
return !Validation.GetHasError(instance) && LogicalTreeHelper.GetChildren(instance).OfType<DependencyObject>().All(child => child.IsValid());
}
따라서 재사용 가능성을 고려할 때 매우 유용합니다.
저는 작은 최적화를 제안합니다.
동일한 컨트롤에 대해 이 작업을 여러 번 수행할 경우 위의 코드를 추가하여 실제로 유효성 검사 규칙이 있는 컨트롤 목록을 유지할 수 있습니다.그런 다음 유효성을 확인해야 할 때마다 전체 시각적 트리 대신 해당 컨트롤만 검토하십시오.이러한 제어 장치가 여러 개 있으면 훨씬 더 낫습니다.
WPF의 양식 유효성 검사를 위한 라이브러리가 있습니다.너겟 패키지 여기.
샘플:
<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 속성을 확인할 수 있습니다.Has Error Property(오류 속성)를 선택한 다음 해당 속성에서 처음 발견한 것에 초점을 맞춥니다.
또한 이미 작성된 많은 솔루션을 사용할 수 있습니다. 예제 및 자세한 정보는 이 스레드를 확인할 수 있습니다.
WPF(WPF Application Framework)의 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;
}
}
저는 DataGrid를 사용하고 있으며, 위의 정상 코드는 DataGrid 자체가 초점을 잃을 때까지 오류를 찾지 못했습니다.아래 코드를 사용하더라도 행의 포커스가 없어질 때까지 오류를 "인식"하지 않지만 적어도 그리드의 포커스가 떨어질 때까지 기다리는 것보다는 낫습니다.
또한 이 버전은 문자열 목록의 모든 오류를 추적합니다.이 게시물의 다른 버전은 대부분 그렇게 하지 않으므로 첫 번째 오류에서 중지할 수 있습니다.
public static List<string> Errors { get; set; } = new();
public static bool IsValid(this DependencyObject parent)
{
Errors.Clear();
return IsValidInternal(parent);
}
private static bool IsValidInternal(DependencyObject parent)
{
// Validate all the bindings on this instance
bool valid = true;
if (Validation.GetHasError(parent) ||
GetRowsHasError(parent))
{
valid = false;
/*
* Find the error message and log it in the Errors list.
*/
foreach (var error in Validation.GetErrors(parent))
{
if (error.ErrorContent is string errorMessage)
{
Errors.Add(errorMessage);
}
else
{
if (parent is Control control)
{
Errors.Add($"<unknow error> on field `{control.Name}`");
}
else
{
Errors.Add("<unknow error>");
}
}
}
}
// Validate all the bindings on the children
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (IsValidInternal(child) == false)
{
valid = false;
}
}
return valid;
}
private static bool GetRowsHasError(DependencyObject parent)
{
DataGridRow dataGridRow;
if (parent is not DataGrid dataGrid)
{
/*
* This is not a DataGrid, so return and say we do not have an error.
* Errors for this object will be checked by the normal check instead.
*/
return false;
}
foreach (var item in dataGrid.Items)
{
/*
* Not sure why, but under some conditions I was returned a null dataGridRow
* so I had to test for it.
*/
dataGridRow = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(item);
if (dataGridRow != null &&
Validation.GetHasError(dataGridRow))
{
return true;
}
}
return false;
}
언급URL : https://stackoverflow.com/questions/127477/detecting-wpf-validation-errors
'programing' 카테고리의 다른 글
| 손상된 WPF 데이터 바인딩을 탐지하는 방법은 무엇입니까? (0) | 2023.04.29 |
|---|---|
| Azure Max 지출 한도 또는 비용 CAP $ 금액을 설정하는 방법은 무엇입니까? (0) | 2023.04.29 |
| IE10은 IE7 모드에서 렌더링합니다.표준 모드를 적용하는 방법은 무엇입니까? (0) | 2023.04.29 |
| 웹 사이트 프로젝트 유형과 함께 C# 6을 사용하는 방법은 무엇입니까? (0) | 2023.04.29 |
| 이클립스에서 내 그라들 종속성 업데이트 (0) | 2023.04.29 |