Hrethric
Hrethric

Reputation: 31

WPF "BasedOn" not Setting My Validation.ErrorTemplate

I have a bunch of styles that I'm trying to override in order to set an Error Template so that I can display error messages. When using "BasedOn" to attempt to inherit from the style, the base style remains, but the ErrorTemplate displays as a tiny box with no text. If I don't use "BasedOn" it works great, but the base style is no longer present (as expected). Has anyone seen this before?

Styles in question:

        <ControlTemplate x:Key="ValidationErrorTemplate">
            <StackPanel ClipToBounds="False">
                <Border BorderBrush="Red" ClipToBounds="False"
                        BorderThickness="2" HorizontalAlignment="Left" >
                    <!-- Placeholder for the DataGridTextColumn itself -->
                    <AdornedElementPlaceholder x:Name="AdornedElement"  />
                </Border>
                <Border 
                    BorderBrush="Red" ClipToBounds="False"
                    Padding="1"
                    BorderThickness="1,1,1,1" 
                    HorizontalAlignment="Left" Background="Black"
                    Margin="100,0,0,0">
                    <ItemsControl ItemsSource="{Binding}" HorizontalAlignment="Left">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding ErrorContent}" ClipToBounds="False"
                                           Foreground="Red" TextWrapping="WrapWithOverflow" Width="250" Height="100"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </Border>
            </StackPanel>
        </ControlTemplate>

        <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip" 
                            Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
                    <Setter Property="BorderBrush" Value="Red"/>
                    <Setter Property="BorderThickness" Value="3"/>
                    <Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationErrorTemplate}"/>
                </Trigger>
            </Style.Triggers>
        </Style>

Simple Text VM to use:

    public class TextValidationVM : INotifyDataErrorInfo, INotifyPropertyChanged
    {
        private Dictionary<string, List<string>> _Errors;

        public TextValidationVM() => this._Errors = new Dictionary<string, List<string>>();

        public event PropertyChangedEventHandler PropertyChanged;

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        protected string _SomeText;
        public string SomeText
        {
            get { return _SomeText; }
            set
            {
                ValidateProperty(value, ValidateText);
                SetProperty(ref _SomeText, value);
            }
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if (propertyChanged == null)
                return;
            propertyChanged((object)this, new PropertyChangedEventArgs(propertyName));
        }

        protected bool SetProperty<T>(
            ref T backingStore,
            T value,
            Action onChanged = null,
            [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;
            backingStore = value;
            if (onChanged != null)
                onChanged();
            this.OnPropertyChanged(propertyName);
            return true;
        }

        private (bool IsValid, IEnumerable<string> ErrorMessages) ValidateText(string arg)
        {
            return (false, new List<string>() {"This is not valid text"});
        }

        public IEnumerable GetErrors(string propertyName)
        {
            List<string> stringList;
            return !string.IsNullOrWhiteSpace(propertyName) ? (this._Errors.TryGetValue(propertyName, out stringList) ? (IEnumerable)stringList : (IEnumerable)new List<string>()) : (IEnumerable)this._Errors.SelectMany<KeyValuePair<string, List<string>>, string>((Func<KeyValuePair<string, List<string>>, IEnumerable<string>>)(entry => (IEnumerable<string>)entry.Value));
        }

        public bool HasErrors => this._Errors.Any<KeyValuePair<string, List<string>>>();

        public bool ValidateProperty<TValue>(
            TValue value,
            Func<TValue, (bool IsValid, IEnumerable<string> ErrorMessages)> validationDelegate,
            Action onInvalid = null,
            Action onValid = null,
            [CallerMemberName] string propertyName = "")
        {
            this._Errors.Remove(propertyName);
            this.OnErrorsChanged(propertyName);
            (bool, IEnumerable<string>) valueTuple = validationDelegate(value);
            if (!valueTuple.Item1)
            {
                foreach (string errorMessage in valueTuple.Item2)
                    this.AddError(propertyName, errorMessage);
                if (onInvalid != null)
                    onInvalid();
            }
            else if (onValid != null)
                onValid();
            return valueTuple.Item1;
        }

        public void AddError(string propertyName, string errorMessage, bool isWarning = false)
        {
            List<string> stringList;
            if (!this._Errors.TryGetValue(propertyName, out stringList))
            {
                stringList = new List<string>();
                this._Errors[propertyName] = stringList;
            }
            if (stringList.Contains(errorMessage))
                return;
            if (isWarning)
                stringList.Add(errorMessage);
            else
                stringList.Insert(0, errorMessage);
            this.OnErrorsChanged(propertyName);
        }

        public bool PropertyHasErrors(string propertyName)
        {
            List<string> source;
            return this._Errors.TryGetValue(propertyName, out source) && source.Any<string>();
        }

        protected virtual void OnErrorsChanged(string propertyName)
        {
            EventHandler<DataErrorsChangedEventArgs> errorsChanged = this.ErrorsChanged;
            if (errorsChanged == null)
                return;
            errorsChanged((object)this, new DataErrorsChangedEventArgs(propertyName));
        }
    }

WPF Window Content:

    <Grid>
        <TextBox Text="{Binding SomeText}" MaxHeight="100"/>
    </Grid>

Code behind:

        public MainWindow()
        {
            InitializeComponent();

            DataContext = new TextValidationVM() {SomeText = "This is text"};
        }

With all of the above implemented it should always spit out an error below the TextBox that says: "This is not valid text"

Upvotes: 0

Views: 37

Answers (0)

Related Questions