我有两个带有自定义验证的文本框,一个在主窗口中,一个在用户控件中。在这两种情况下,验证错误都会正确显示。只有当两个文本框都没有任何验证错误时,我才尝试启用“确定”按钮。问题出在 MultiDataTrigger 的 XAML 上,我遇到了绑定失败:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=TBUserControl'. BindingExpression:Path=(0); DataItem=null; target element is 'Button' (Name=''); target property is 'NoTarget' (type 'Object')
这是主窗口xaml:
<Window x:Class="minimalExample.MainWindow"
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:minimalExample"
xmlns:uc="clr-namespace:minimalExample.Views"
mc:Ignorable="d"
Title="MainWindow"
Height="150"
Width="300">
<Window.Resources>
<ResourceDictionary Source="Dictionary1.xaml" />
</Window.Resources>
<Grid>
<StackPanel>
<TextBox Name="TBMain"
Width="100"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
Text="{Binding MainTextBox.Text, UpdateSourceTrigger=PropertyChanged}" />
<uc:MyUserControl Margin="10"/>
<Button Content="OK"
Width="50"
Margin="20"
Style="{StaticResource CanOK}">
</Button>
</StackPanel>
</Grid>
</Window>
这是用户控件 XAML:
<UserControl x:Class="minimalExample.Views.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:minimalExample.Views"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="150">
<UserControl.Resources>
<ResourceDictionary Source="../Dictionary1.xaml" />
</UserControl.Resources>
<Grid >
<StackPanel >
<TextBox x:Name="TBUserControl"
Width="100"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
Text="{Binding UserControlTextBox.Text, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</UserControl>
这里是资源词典:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="ValidationTemplate">
<StackPanel Orientation="Vertical">
<DockPanel>
<Border BorderThickness="1"
BorderBrush="Red"
DockPanel.Dock="Left">
<AdornedElementPlaceholder Name="ErrorAdorner" />
</Border>
<TextBlock Text="" />
</DockPanel>
<TextBlock Foreground="Red"
Background="#f5f5f5"
FontWeight="bold"
FontFamily="Segoe UI"
FontSize="11"
Text="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}" />
</StackPanel>
</ControlTemplate>
<Style x:Key="CanOK"
TargetType="Button">
<Setter Property="IsEnabled"
Value="False" />
<Setter Property="MinWidth"
Value="100" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=TBMain}"
Value="False" />
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=TBUserControl}"
Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled"
Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
主窗口视图模型:
namespace minimalExample
{
public partial class MainWindowViewModel: ObservableValidator
{
[ObservableProperty]
public Param mainTextBox;
[ObservableProperty]
public Param userControlTextBox;
public MainWindowViewModel()
{
MainTextBox = new Param(10, -100, 100);
UserControlTextBox = new Param(5, -50, 50);
}
}
}
包含自定义验证器的参数类:
namespace minimalExample
{
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Globalization;
using CommunityToolkit.Mvvm.ComponentModel;
public partial class Param : ObservableValidator
{
<snip>
private string text;
[CustomValidation(typeof(Param), nameof(ValidateMe))]
public string Text
{
get
{
text = Value.ToString();
return text;
}
set => SetProperty(ref text, value, true);
}
public static ValidationResult ValidateMe(string val, ValidationContext context)
{
Param instance = (Param)context.ObjectInstance;
if (instance != null)
{
string test = instance.Validate(val);
if (test == string.Empty)
{
return ValidationResult.Success!;
}
return new(test);
}
return ValidationResult.Success!;
}
}
}
我发现了以下帖子,这与我的情况相似,但没有被接受的答案,我不完全理解评论中的建议。
这是我在第二个 MultiDataTrigger 条件下尝试的另一篇文章中的建议:
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type uc:MyUserControl}}, Path=TBUserControl.(Validation.HasError)}"
Value="False" />
在查看了这么多主要处理依赖属性的类似用例之后,我无法弄清楚我做错了什么,但这不是我的问题所在,而是用户内部(命名的)文本框的可见性控制。
我找到了一篇帖子,其中包含到达 UserControl 中的 TextBox 的答案: Wpf,如何从一个用户控件中找到另一个元素名称
修复是在后面的UserControl代码中添加一个公共属性:
public object InnerButton { get { return TBUserControl; } }
然后,在 MainWindow xaml 中命名 UserControl:
<uc:MyUserControl x:Name="MyUC" Margin="10" />
最后需要将有问题的 MultiDataTrigger Condition 更改为:
<Condition Binding="{Binding InnerButton.(Validation.HasError), ElementName=MyUC}" Value="False" />