UserControl中的控件ErrorTemplate

时间:2016-01-13 17:58:06

标签: wpf validation

我在stackoverflow上的读者比写作者更多,但是我对此很绝望地回答问题。到目前为止我找到的解决方案无法解决我的问题: 虽然在以下Window-code中验证并显示TextBox的正确ErrorTemplate(绑定后面的ViewModel实现了INotifyDataErrorInfo),但TextBox完全相同的代码移动到自定义用户控件(控件:LabeledTextField),赢得了#39 ;吨

<Window x:Class="ValidationErrorTemplateTest.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:ignore="http://www.galasoft.ch/ignore"
    xmlns:controls="clr-namespace:ValidationErrorTemplateTest"
    mc:Ignorable="d ignore"
    Height="300"
    Width="300"
    DataContext="{Binding Main, Source={StaticResource Locator}}">

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skins/MainSkin.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

<Grid x:Name="LayoutRoot">
    <StackPanel>
    <TextBox Width="100" Height="20" Text="{Binding WelcomeTitle, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged,
        ValidatesOnNotifyDataErrors=True}" Validation.ErrorTemplate="{StaticResource validationErrorHint}" Margin="40"/>
    <controls:LabeledTextField Value="{Binding WelcomeTitle}"/>
    </StackPanel>
</Grid>

外观/ MainSkin.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ControlTemplate x:Key="validationErrorHint">
        <DockPanel LastChildFill="True">
            <Border Background="Red" DockPanel.Dock="Right" Margin="5,0,0,0" Width="12" Height="12" CornerRadius="6" 
                    ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}">
                <TextBlock Text="!" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" Foreground="White" FontSize="10"/>
            </Border>
            <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center">
            </AdornedElementPlaceholder>
        </DockPanel>
    </ControlTemplate>
</ResourceDictionary>

这是LabeledTextField的xaml:

<UserControl x:Class="ValidationErrorTemplateTest.LabeledTextField"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="30" d:DesignWidth="340">
<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skins/MainSkin.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="Root">
    <TextBox Width="100" Height="20" Text="{Binding Value, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged,
        ValidatesOnNotifyDataErrors=True}" Validation.ErrorTemplate="{StaticResource validationErrorHint}"/>
</Grid>

和代码隐藏:

namespace ValidationErrorTemplateTest
{
    public partial class LabeledTextField : UserControl
    {
        public LabeledTextField()
        {
            InitializeComponent();
            Root.DataContext = this;
        }

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

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(string), typeof(LabeledTextField), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    }
}

我读过有关添加<ArdornerDecorator> - 图层的内容,但它似乎无法解决问题。 我假设使用了usercontrol的ErrorTemplate而不是在usercontrol中分配给TextBox的ErrorTemplate,但我不知道如何解决这个问题。任何提示都会非常感激!

1 个答案:

答案 0 :(得分:0)

我能够做到这一点。我刚刚使用了您粘贴的代码并制作了我的应用程序版本。

我的MainWindow

<Window x:Class="ErrorTemplateNotWorking.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:ErrorTemplateNotWorking"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid x:Name="LayoutRoot">
        <StackPanel>
            <TextBox Width="100" Height="20" 
                     Text="{Binding WelcomeTitle, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" 
                     Validation.ErrorTemplate="{StaticResource validationErrorHint}" 
                     Margin="40"/>
            <local:LabeledTextField Value="{Binding WelcomeTitle, ValidatesOnDataErrors=True}" 
                                    Width="100" Height="20"
                                    Validation.ErrorTemplate="{StaticResource validationErrorHint}" />
        </StackPanel>
    </Grid>
</Window>

请注意,我只在绑定中使用ValidatesOnDataErrors=True,我也为Validation.ErrorTemplate="{StaticResource validationErrorHint}"设置了UserControl。另一个重要的事情是为Width设置HeightUserControl,这样验证感叹号就无法超出窗口的范围,因为控件只会在没有这个的情况下伸展到最大宽度。

MainWindow的代码隐藏:

namespace ErrorTemplateNotWorking
{
    using System.Windows;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using Annotations;

    public class MainWindowViewModel : IDataErrorInfo, INotifyPropertyChanged
    {
        public string this[string columnName]
        {
            get {
                if (columnName == nameof(WelcomeTitle))
                {
                    if (string.IsNullOrEmpty(WelcomeTitle) || WelcomeTitle.Length < 5)
                    {
                        return "Title should be at least 5 characters long";
                    }
                }

                return "";
            }
        }

        public string Error => null;

        private string _welcomeTitle;
        public string WelcomeTitle
        {
            get { return _welcomeTitle; }
            set
            {
                _welcomeTitle = value;
                OnPropertyChanged(nameof(WelcomeTitle));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new MainWindowViewModel();
        }
    }
}

请注意,我使用了一些ReSharper和C#6功能,但要点应该是可以理解的。基本上我只是将DataContext设置为VM,而在VM中我使用验证规则实现IDataErrorInfo,文本的长度至少应为5个字符。

MainSkin.xaml是一样的。

UserControl XAML:

<UserControl x:Class="ErrorTemplateNotWorking.LabeledTextField"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid x:Name="Root">
        <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</UserControl>

Binding,我甚至不需要任何额外的东西。 结果是:

Result

我希望这会有所帮助。