基于我的答案的示例https://stackoverflow.com/a/74293719/13349759
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
namespace CommonCore
{
public static class BindingExpressionHelper
{
private static readonly Func<BindingExpressionBase, DependencyObject, DependencyProperty, object> GetValueOfBindingExpression;
static BindingExpressionHelper()
{
Type beType = typeof(BindingExpressionBase);
var beMethod = beType
.GetMethod("GetValue", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { typeof(DependencyObject), typeof(DependencyProperty) })
?? throw new Exception("GetValue method not found.");
var beFunc = (Func<BindingExpressionBase, DependencyObject, DependencyProperty, object>)
beMethod.CreateDelegate(typeof(Func<BindingExpressionBase, DependencyObject, DependencyProperty, object>));
GetValueOfBindingExpression = beFunc;
}
/// <summary>Returns the source value of this binding expression.</summary>
/// <param name="bindingExpression">The binding expression whose value to get.</param>
/// <returns>The value of the binding expression received.</returns>
public static object GetSourceValue(this BindingExpressionBase bindingExpression)
{
DependencyObject target = bindingExpression.Target;
DependencyProperty targetProperty = bindingExpression.TargetProperty;
var value = GetValueOfBindingExpression(bindingExpression, target, targetProperty);
return value;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using static System.Windows.Media.VisualTreeHelper;
namespace CommonCore.Helpers
{
public static partial class VisualTreeHelper
{
public static IEnumerable<DependencyObject> GetChildren(this DependencyObject parent)
{
ArgumentNullException.ThrowIfNull(parent);
Queue<DependencyObject> queue = new Queue<DependencyObject>(16);
queue.Enqueue(parent);
while (queue.Count != 0)
{
DependencyObject current = queue.Dequeue();
yield return current;
int count = GetChildrenCount(current);
for (int i = 0; i < count; i++)
{
queue.Enqueue(GetChild(current, i));
}
}
}
public static IEnumerable<T> GetChildren<T>(this DependencyObject parent)
where T : DependencyObject
=> parent.GetChildren().OfType<T>();
public static T GetFirstChild<T>(this DependencyObject parent)
where T : DependencyObject
=> (T)parent.GetChildren().FirstOrDefault(child => child is T);
}
}
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
using CommonCore;
namespace Core2024.SO.AlexeyTitov.question79081208
{
public class OrderIDValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
int id;
switch (value)
{
case int _id: id = _id; break;
case string text: id = int.Parse(text, cultureInfo); break;
case BindingExpressionBase bindingExpression:
var eee = bindingExpression.GetSourceValue();
var t = eee.GetType();
id = (int)(bindingExpression.GetSourceValue() ?? 0);
break;
default:
id = int.MinValue;
break;
}
if (id < 0 || id > 100)
return new ValidationResult(false, "Значение вне диапазона [0..100]");
return ValidationResult.ValidResult;
}
}
}
using System.Collections.ObjectModel;
namespace Core2024.SO.AlexeyTitov.question79081208
{
public class Order : BaseInpc
{
private int? _id;
public int? ID
{
get => _id;
set => Set(ref _id, value);
}
}
public class OrdersViewMode : BaseInpc
{
public ObservableCollection<Order> OrderCollection { get; }
= new ObservableCollection<Order>
(
Enumerable.Range(0, 20).Select(_ => new Order() { ID = Random.Shared.Next(-20, 120) })
);
}
}
<Window x:Class="Core2024.SO.AlexeyTitov.question79081208.TestErrorHandlerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Core2024.SO.AlexeyTitov.question79081208"
mc:Ignorable="d"
Title="TestErrorHandlerWindow" Height="450" Width="800">
<Window.DataContext>
<local:OrdersViewMode/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl x:Name="MyItemsControl"
ItemsSource="{Binding Path=OrderCollection}"
Validation.Error="OnOrderError">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="MyTextBox">
<TextBox.Text>
<Binding Path="ID" Mode="TwoWay" NotifyOnValidationError="True">
<Binding.ValidationRules>
<!--<local:OrderIDValidationRule ValidationStep="ConvertedProposedValue"/>-->
<local:OrderIDValidationRule ValidationStep="ConvertedProposedValue"
ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="MyButton" Grid.Row="1"
Margin="5" Padding="15 5"
Content="Click Me"
HorizontalAlignment="Center" VerticalAlignment="Center">
</Button>
<x:Code>
<![CDATA[
private void OnOrderError(object sender, ValidationErrorEventArgs e)
{
bool hasError = ((DependencyObject)sender).GetChildren().Any(Validation.GetHasError);
MyButton.IsEnabled = !hasError;
}
]]>
</x:Code>
</Grid>
</Window>