绑定在 MVVM WPF C# 应用程序中的用户控件内时找不到文本框

问题描述 投票:0回答:1

我有两个带有自定义验证的文本框,一个在主窗口中,一个在用户控件中。在这两种情况下,验证错误都会正确显示。只有当两个文本框都没有任何验证错误时,我才尝试启用“确定”按钮。问题出在 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" />

在查看了这么多主要处理依赖属性的类似用例之后,我无法弄清楚我做错了什么,但这不是我的问题所在,而是用户内部(命名的)文本框的可见性控制。

mvvm data-binding user-controls multidatatrigger
1个回答
0
投票

我找到了一篇帖子,其中包含到达 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" />
© www.soinside.com 2019 - 2024. All rights reserved.