Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使WPF验证冒泡到父控件?_C#_Wpf_Xaml_Inotifydataerrorinfo - Fatal编程技术网

C# 如何使WPF验证冒泡到父控件?

C# 如何使WPF验证冒泡到父控件?,c#,wpf,xaml,inotifydataerrorinfo,C#,Wpf,Xaml,Inotifydataerrorinfo,所以我有一个类似于这个简化版本的控件: <local:ImageMapField x:Class="ImageApp.WPF.Controls.ImageMapContentField" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

所以我有一个类似于这个简化版本的控件:

<local:ImageMapField x:Class="ImageApp.WPF.Controls.ImageMapContentField"
             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:ImageApp.WPF.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
                        x:Name="Me">

    <Grid HorizontalAlignment="Left">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Column="0" HorizontalAlignment="Stretch" Style="{DynamicResource BaseLabelStyle}">
            <TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=local:ImageMapContentField, Mode=FindAncestor}}" TextWrapping="WrapWithOverflow"></TextBlock>
        </Label>
        <StackPanel Grid.Column="1">
            <Image />
            <Border Margin="20,5,5,2">
                <ContentPresenter Content="{Binding DataEntryContent, ElementName=Me}" />
            </Border>
        </StackPanel>
    </Grid>

</local:ImageMapField>
CustomTextField.xaml

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">


    <Window.Resources>
        <Style TargetType="local:CustomTextField">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Setter Property="Background" Value="LightPink"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style TargetType="local:CustomContentControl">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Setter Property="Background" Value="LightPink"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MyModel />
    </Window.DataContext>
    <StackPanel>
        <local:CustomTextField LabelText="Number 1" Value="{Binding Number1, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
        <local:CustomContentControl LabelText="Number 2">
            <local:CustomContentControl.DataEntryContent>
                <TextBox Text="{Binding Number2, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
            </local:CustomContentControl.DataEntryContent>    
        </local:CustomContentControl>
    </StackPanel>
</Window>
<UserControl x:Class="WpfApp1.CustomTextField"
             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:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="Me">
        <StackPanel>
            <Label Content="{Binding ElementName=Me, Path=LabelText}" />
            <TextBox Text="{Binding ElementName=Me, Path=Value}" />
        </StackPanel>
</UserControl>
<UserControl x:Class="WpfApp1.CustomContentControl"
             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:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="Me">
        <Grid>
            <StackPanel>
                <Label Content="{Binding ElementName=Me, Path=LabelText}" />
                <ContentPresenter Content="{Binding DataEntryContent, ElementName=Me}" />
            </StackPanel>
        </Grid>
</UserControl>
    <Style TargetType="local:CustomContentControl">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                        <AdornedElementPlaceholder/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="HasErrors" Value="True">
                <Setter Property="Background" Value="LightPink"/>
            </Trigger>
        </Style.Triggers>
    </Style>
CustomContentControl.xaml

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">


    <Window.Resources>
        <Style TargetType="local:CustomTextField">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Setter Property="Background" Value="LightPink"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style TargetType="local:CustomContentControl">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Setter Property="Background" Value="LightPink"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MyModel />
    </Window.DataContext>
    <StackPanel>
        <local:CustomTextField LabelText="Number 1" Value="{Binding Number1, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
        <local:CustomContentControl LabelText="Number 2">
            <local:CustomContentControl.DataEntryContent>
                <TextBox Text="{Binding Number2, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
            </local:CustomContentControl.DataEntryContent>    
        </local:CustomContentControl>
    </StackPanel>
</Window>
<UserControl x:Class="WpfApp1.CustomTextField"
             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:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="Me">
        <StackPanel>
            <Label Content="{Binding ElementName=Me, Path=LabelText}" />
            <TextBox Text="{Binding ElementName=Me, Path=Value}" />
        </StackPanel>
</UserControl>
<UserControl x:Class="WpfApp1.CustomContentControl"
             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:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="Me">
        <Grid>
            <StackPanel>
                <Label Content="{Binding ElementName=Me, Path=LabelText}" />
                <ContentPresenter Content="{Binding DataEntryContent, ElementName=Me}" />
            </StackPanel>
        </Grid>
</UserControl>
    <Style TargetType="local:CustomContentControl">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                        <AdornedElementPlaceholder/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="HasErrors" Value="True">
                <Setter Property="Background" Value="LightPink"/>
            </Trigger>
        </Style.Triggers>
    </Style>
MyModel.cs

public partial class CustomTextField : UserControl
    {
        public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register(
                                                        "LabelText", typeof(string), typeof(CustomTextField), new PropertyMetadata(default(string)));

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
                                                        "Value", typeof(string), typeof(CustomTextField), new PropertyMetadata(default(string)));

        public string Value
        {
            get { return (string) GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public string LabelText
        {
            get { return (string) GetValue(LabelTextProperty); }
            set { SetValue(LabelTextProperty, value); }
        }

        public CustomTextField()
        {
            InitializeComponent();
        }
    }
public partial class CustomContentControl : UserControl
    {
        public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register(
                                                        "LabelText", typeof(string), typeof(CustomContentControl), new PropertyMetadata(default(string)));

        public static readonly DependencyProperty DataEntryContentProperty = DependencyProperty.Register(
                                                        "DataEntryContent", typeof(object), typeof(CustomContentControl), new PropertyMetadata(default(object)));

        public object DataEntryContent
        {
            get { return (object) GetValue(DataEntryContentProperty); }
            set { SetValue(DataEntryContentProperty, value); }
        }

        public string LabelText
        {
            get { return (string) GetValue(LabelTextProperty); }
            set { SetValue(LabelTextProperty, value); }
        }

        public CustomContentControl()
        {
            InitializeComponent();
        }
    }
public class MyModel : INotifyPropertyChanged
    {
        int _number1;
        int _number2;

        public int Number1
        {
            get { return _number1; }
            set
            {
                _number1 = value;
                OnPropertyChanged();
            }
        }

        public int Number2
        {
            get { return _number2; }
            set
            {
                _number2 = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

WPF验证已经冒泡到父控件(即使子控件在ContentPresenter中)——

这里的问题是,即使事件冒泡,附加的属性Validation.HasError也不会更新-这基本上是因为控件的属性绑定中没有错误。因此,你看不到背景的变化

要纠正此问题,您可以使用以下代码:

在MainWindow.xaml中更新样式

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">


    <Window.Resources>
        <Style TargetType="local:CustomTextField">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Setter Property="Background" Value="LightPink"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style TargetType="local:CustomContentControl">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Setter Property="Background" Value="LightPink"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MyModel />
    </Window.DataContext>
    <StackPanel>
        <local:CustomTextField LabelText="Number 1" Value="{Binding Number1, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
        <local:CustomContentControl LabelText="Number 2">
            <local:CustomContentControl.DataEntryContent>
                <TextBox Text="{Binding Number2, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
            </local:CustomContentControl.DataEntryContent>    
        </local:CustomContentControl>
    </StackPanel>
</Window>
<UserControl x:Class="WpfApp1.CustomTextField"
             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:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="Me">
        <StackPanel>
            <Label Content="{Binding ElementName=Me, Path=LabelText}" />
            <TextBox Text="{Binding ElementName=Me, Path=Value}" />
        </StackPanel>
</UserControl>
<UserControl x:Class="WpfApp1.CustomContentControl"
             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:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="Me">
        <Grid>
            <StackPanel>
                <Label Content="{Binding ElementName=Me, Path=LabelText}" />
                <ContentPresenter Content="{Binding DataEntryContent, ElementName=Me}" />
            </StackPanel>
        </Grid>
</UserControl>
    <Style TargetType="local:CustomContentControl">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
                        <AdornedElementPlaceholder/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="HasErrors" Value="True">
                <Setter Property="Background" Value="LightPink"/>
            </Trigger>
        </Style.Triggers>
    </Style>
你的背景也会更新。

你有完整的项目重现这个问题吗?@SimonMourier添加了一个例子。谢谢!我知道一定有什么我错过了。希望以某种方式触发标准的
验证。HasError
,因此它与其他所有操作一样,但这将起作用,而且非常简单。是的-我的第一反应也是手动将错误添加到验证中。Errors集合以触发验证中的更新。HasError。但引入自定义依赖属性似乎简单得多;特别是当您只有一个绑定验证要跟踪时。如果要跟踪多个绑定(多个子控件),则建议使用Validation.Errors来跟踪错误。