简体   繁体   English

属于 UserControl 子级的错误与 UserControl 而不是子级相关联

[英]Errors belonging to child of UserControl are associated with UserControl in stead of the child

I've created a UserControl that is designed for re-use and must be totally independent.我创建了一个为重复使用而设计的 UserControl,它必须是完全独立的。 It has dependency properties, doesn't set its DataContext to anything, and in its template it only binds to its own dependency propeties.它有依赖属性,没有将它的 DataContext 设置为任何东西,并且在它的模板中它只绑定到它自己的依赖属性。

This is an example situation:这是一个示例情况:

The control is used for entering an address.该控件用于输入地址。 It contains a few fields (City, street, PO box, etc.).它包含一些字段(城市、街道、邮政信箱等)。 The control has dependency properties like CityProperty , StreetProperty , ... Each field has a binding to the corresponding dependency property.该控件具有依赖属性,如CityPropertyStreetProperty ……每个字段都绑定到相应的依赖属性。 So the street TextBox Text has a binding with StreetProperty DP of the UserControl.所以街道TextBox Text与UserControl的StreetProperty DP有绑定。 Because there's no datacontext set from within the control explicitly, it'll always inherit the datacontext from its parent.因为没有从控件中显式设置数据上下文,所以它将始终从其父级继承数据上下文。

When using my control by embedding it in another control (view), I use its dependency properties to define a binding with the ViewModel (datacontext) of the view it is placed in.当通过将我的控件嵌入到另一个控件(视图)中来使用它时,我使用它的依赖属性来定义与放置它的视图的 ViewModel(数据上下文)的绑定。

<StackPanel x:Name="someView" DataContext=" ... a viewmodel ... "> <!-- Some view that uses my Address control -->

<TextBlock>Enter an address</TextBlock> <!-- Just some parts of the view -->

<myControls:AddressControl 
    City="{Binding ShippingCity"
    Street="{Binding ShippingStreet}"
    ...
/>

</StackPanel>

My control's dependency properties are bound to ViewModel properties of the view the control is placed in. The ViewModel implements INotifyDataErrorInfo .我的控件的依赖属性绑定到控件所在视图的 ViewModel 属性。 ViewModel 实现INotifyDataErrorInfo

When there are validation errors, the errors should be 'attached' to the corresponding TextBox es in the UserControl.当存在验证错误时,应将错误“附加”到 UserControl 中相应的TextBox中。 A TextBox in the usercontrol should be in error state and change its appearance to its error template.用户控件中的 TextBox 应该出现错误 state 并将其外观更改为其错误模板。

But what actually happens, is that the UserControl detects there's one or more errors associated with a binding to its dependency property.但实际发生的是,UserControl 检测到有一个或多个错误与其依赖属性的绑定相关联。 The framework interprets the control as one thing that has 'some' error.该框架将控件解释为具有“某些”错误的事物。 A red border around the entire UserControl is displayed (default behavior).显示整个 UserControl 周围的红色边框(默认行为)。

I want to display the errors to the corresponding field in the UserControl.我想将错误显示到 UserControl 中的相应字段。 But I don't know how to do this.但我不知道该怎么做。 How to handle the validation errors on bindings to dependency properties of a UserControl so, that the validation error can be forwarded to the correct child control in that UserControl.如何处理绑定到 UserControl 的依赖属性的验证错误,以便可以将验证错误转发到该 UserControl 中的正确子控件。 The only association between validation errors from the ViewModel to the right child control in the UserControl, is the imaginary 'route' via the dependency properties.从 ViewModel 到 UserControl 中右子控件的验证错误之间的唯一关联是通过依赖属性的假想“路线”。

I give you a UserControl class called an AddressControl that can be reused without dependence on external objects.我给你一个名为 AddressControl 的UserControl AddressControl ,它可以在不依赖外部对象的情况下重复使用。 And any change from outside control to inside control.以及从外部控制到内部控制的任何变化。

AddressControl.cs地址控制.cs

public partial class AddressControl : UserControl, IDataErrorInfo
{
   public string City
   {
      get { return (string)GetValue(CityProperty); }
      set { SetValue(CityProperty, value); }
   }

   // Using a DependencyProperty as the backing store for City.  This enables styling, binding, etc...
   public static readonly DependencyProperty CityProperty =
            DependencyProperty.Register("City", typeof(string), typeof(AddressControl), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnCityChanged)));

   private static void OnCityChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
   {
      AddressControl ac = new AddressControl();
      ac.ChangeCityText();
   }
   public void ChangeCityText()
   {
      txtCity.Text = City;
   }

   public string Street
   {
      get { return (string)GetValue(StreetProperty); }
      set { SetValue(StreetProperty, value); }
   }

   // Using a DependencyProperty as the backing store for Street.  This enables styling, binding, etc...
   public static readonly DependencyProperty StreetProperty =
   DependencyProperty.Register("Street", typeof(string), typeof(AddressControl), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnStreetChanged)));

   private static void OnStreetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
   {
      AddressControl ac = new AddressControl();
      ac.ChangeStreetText();
   }
   public void ChangeStreetText()
   {
      txtStreet.Text = Street;
   }
   public AddressControl()
   {
      InitializeComponent();
      DataContext = this;
   }

   public string Error
   {
       get
       {
          return null;
       }
   }

   public string this[string name]
   {
      get
      {
          string result = null;

          if (name == "City")
          {
              if (string.IsNullOrEmpty(City) || string.IsNullOrWhiteSpace(City))
              {
                 result = "please enter city";
              }
          }

          if (name == "Street")
          {
             if (string.IsNullOrEmpty(Street) && string.IsNullOrWhiteSpace(Street))
             {
                 result = "please enter street";
             }
          }
          return result;
      }
   }
}

AddressControl.xaml地址控制.xaml

<UserControl x:Class="WpfApp1.AddressControl"
             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" Width="600">
    <UserControl.Resources>
        <Style x:Key="CustomTextBoxTextStyle" TargetType="TextBox">
            <Setter Property="FontWeight" Value="Normal"></Setter>
            <Setter Property="FontSize" Value="14"></Setter>
            <Setter Property="Margin" Value="0"></Setter>
            <Setter Property="FontFamily" Value="Arial"></Setter>
            <Setter Property="FontWeight" Value="Bold"></Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <Border KeyboardNavigation.IsTabStop="False" x:Name="bg" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="2">
                            <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Opacity" TargetName="bg" Value="0.56"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="BorderBrush" TargetName="bg" Value="#FF7EB4EA"/>
                            </Trigger>
                            <Trigger Property="IsFocused" Value="True">
                                <Setter Property="BorderBrush" TargetName="bg" Value="#FF7EB4EA"/>
                            </Trigger>
                            <Trigger Property="Validation.HasError" Value="True">
                                <Trigger.Setters>
                                    <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>
                                    <Setter Property="BorderThickness" TargetName="bg" Value="2"/>
                                    <Setter Property="BorderBrush" TargetName="bg" Value="Red"/>
                                </Trigger.Setters>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </UserControl.Resources>
    <Grid FlowDirection="LeftToRight" HorizontalAlignment="Left">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"></ColumnDefinition>
            <ColumnDefinition MinWidth="460"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
        </Grid.RowDefinitions>


        <TextBlock Text="City :" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="5 0 0 0"></TextBlock>
        <TextBox Style="{StaticResource CustomTextBoxTextStyle}" TabIndex="1" Grid.Row="0" Grid.Column="1" x:Name="txtCity" Padding="10 10 10 3" BorderThickness="2" MinWidth="260" Height="38" HorizontalAlignment="Left" VerticalAlignment="Center">
            <TextBox.Text>
                <Binding Path="City"
                         ValidatesOnExceptions="True"
                         UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <DataErrorValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>

            <Validation.ErrorTemplate>
                <ControlTemplate>
                    <DockPanel>
                        <TextBlock FontSize="11" FontStyle="Italic" Foreground="Red" Margin="10 10 0 0" DockPanel.Dock="Right"
                                       Text="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
                        <AdornedElementPlaceholder x:Name="ErrorAdorner"
    ></AdornedElementPlaceholder>
                    </DockPanel>
                </ControlTemplate>
            </Validation.ErrorTemplate>
        </TextBox>


        <TextBlock Text="Street :" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="5 0 0 0"></TextBlock>
        <TextBox Style="{StaticResource CustomTextBoxTextStyle}" TabIndex="2" Grid.Row="1" Grid.Column="1" x:Name="txtStreet" Padding="10 10 10 3" BorderThickness="2" MinWidth="260" Height="38" HorizontalAlignment="Left" VerticalAlignment="Center">
            <TextBox.Text>
                <Binding Path="Street"
                         ValidatesOnExceptions="True"
                         UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <DataErrorValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>

            <Validation.ErrorTemplate>
                <ControlTemplate>
                    <DockPanel>
                        <TextBlock FontSize="11" FontStyle="Italic" Foreground="Red" Margin="10 10 0 0" DockPanel.Dock="Right"
                                       Text="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
                        <AdornedElementPlaceholder x:Name="ErrorAdorner" KeyboardNavigation.IsTabStop="False"
    ></AdornedElementPlaceholder>
                    </DockPanel>
                </ControlTemplate>
            </Validation.ErrorTemplate>
        </TextBox>

    </Grid>
</UserControl>

now Use:You can bind or change the amount of debt immediately现在使用:您可以立即绑定或更改债务金额

<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="450" Width="800">
    <StackPanel VerticalAlignment="Center">       
        <local:AddressControl x:Name="addressControl" City="Paris" Street="Street Menia"></local:AddressControl>
        <Button x:Name="btnTest" Content="Test" Width="100" Click="btnTest_Click" Margin="0 10 0 0"></Button>
    </StackPanel>
</Window>

You can replace the following line您可以替换以下行

<local:AddressControl x:Name="addressControl" City="Paris" Street="Street Menia"></local:AddressControl>

ٌWith this code ٌ使用此代码

<local:AddressControl x:Name="addressControl" City="{Binding ShippingCity}" Street="{Binding ShippingStreet}"></local:AddressControl>

And Button Click Event in MainWindow和 MainWindow 中的按钮单击事件

private void btnTest_Click(object sender, RoutedEventArgs e)
{
   string result = "";
   if (Validation.GetHasError(addressControl.txtCity))
   {
       result = Validation.GetErrors(addressControl.txtCity).FirstOrDefault().ErrorContent.ToString();
   }
   MessageBox.Show(result);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM