1

I want to display a input-validation for the user when entering empty values in a text box, is there a simple way to use data annotation to do this for WPF ? I am new to WPF and C# if someone could explain this for me I would really appreciate it. I want something like this or similar : enter image description here

5
  • 1
    Here is a nice article: social.technet.microsoft.com/wiki/contents/articles/… Commented Jul 13, 2016 at 11:08
  • Please show, what you have tried already Commented Jul 13, 2016 at 11:16
  • I tried something like this : (the first comment) stackoverflow.com/questions/19539492/… , but it only displays a red line around the text box without any comment. Commented Jul 13, 2016 at 11:46
  • the second comment uses try and catch and they are very slow is there another way? Commented Jul 13, 2016 at 11:59
  • @LittleProgrammer see the article in the first comment to your question Commented Jul 13, 2016 at 12:05

3 Answers 3

3

Using a ValidationRule on your binding can get the effect you want, you specify them declaratively in your XAML, and you can make them as custom and complex as you need to:

<TextBox    x:Name="FilePathTextBox" Width="350" Margin="5,0,0,0">
    <TextBox.Text>
        <Binding Path="FilePath" UpdateSourceTrigger="PropertyChanged" >
            <Binding.ValidationRules>
                <this:FilePathValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

will produce this:

enter image description here

Reference my blog post Taking data binding, validation and MVVM to the next level

Sign up to request clarification or add additional context in comments.

Comments

2

You have to use ErrorTemplate like below :

<TextBox...>
    <Validation.ErrorTemplate>
        <ControlTemplate>
            <StackPanel>
                <!-- Placeholder for the TextBox itself -->
                <AdornedElementPlaceholder x:Name="textBox"/>
                <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
            </StackPanel>
        </ControlTemplate>
    </Validation.ErrorTemplate>
</TextBox>

Data Validation in WPF

2 Comments

what about the C# code behind what should I add to it ?
@LittleProgrammer See the link plz
0

Beside all the other correct answers here, additionally to ValidationRule, I'd recommend you to use IDataError-Interface.

With that interface you can match more easily your use of DataAnnotations as you mentioned in the title.

Demo-Model:

 public class Pony : IDataErrorInfo, INotifyPropertyChanged {
    private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();

    [Range(0,4)]
    public int Id {
      get; set;
    }
    [Required]
    public string Name {
      get; set;
    }
    public Brush Color {
      get; set;
    }

    public string Error {
      get {
        var builder = new StringBuilder();
        foreach (var error in this.Errors) {
          if (error.Value.Count > 0) {
            foreach (var text in error.Value) {
              builder.AppendLine(text);
            }
          }
        }
        return builder.Length > 0 ? builder.ToString(0, builder.Length - 2) : builder.ToString();
      }
    }

    public bool HasError => this.Errors.Count > 0;

    public virtual string this[string columnName] {
      get {
        var modelClassProperties = TypeDescriptor.GetProperties(this.GetType());
        foreach (PropertyDescriptor prop in modelClassProperties) {
          if (prop.Name != columnName) {
            continue;
          }
          this.Errors[columnName] = new List<string>();
          foreach (var attribute in prop.Attributes) {
            if (!(attribute is ValidationAttribute)) {
              continue;
            }
            var validation = attribute as ValidationAttribute;
            if (validation.IsValid(prop.GetValue(this))) {
              continue;
            }
            var dn = prop.Name;
            foreach (var pa in prop.Attributes.OfType<DisplayNameAttribute>()) {
              dn = pa.DisplayName;
            }
            this.Errors[columnName].Add(validation.FormatErrorMessage(dn));
            this.OnPropertyChanged("Error");
            return validation.FormatErrorMessage(dn);
          }
        }
        this.Errors.Remove(columnName);
        this.OnPropertyChanged("Error");
        return null;
      }
    }

    internal Dictionary<string, List<string>> Errors => this._errors;

    public event PropertyChangedEventHandler PropertyChanged;

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

Demo-XAML

<Grid  VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="200">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="MinWidth" Value="100"></Setter>
                <Setter Property="Validation.ErrorTemplate">
                    <Setter.Value>
                        <ControlTemplate>
                            <StackPanel>
                                <!-- Placeholder for the TextBox itself -->
                                <AdornedElementPlaceholder x:Name="textBox"/>
                                <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

            <Style TargetType="{x:Type WrapPanel}">
                <Setter Property="Margin" Value="0,0,0,10"></Setter>

            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>

            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <GroupBox Header="Pony 1" Grid.Row="1">
            <StackPanel>
                <WrapPanel>
                    <TextBlock Text="Id:"/>
                    <TextBox Text="{Binding Ponys[0].Id, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
                </WrapPanel>
                <WrapPanel Grid.Row="1">
                    <TextBlock Text="Name:"/>
                    <TextBox Text="{Binding Ponys[0].Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
                </WrapPanel>

                <WrapPanel Grid.Row="2">
                    <TextBlock Text="Color:"/>
                    <TextBox Text="{Binding Ponys[0].Color, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
                </WrapPanel>
            </StackPanel>
        </GroupBox>

        <GroupBox Header="Pony 2">
            <StackPanel>
                <WrapPanel>
                    <TextBlock Text="Id:"/>
                    <TextBox Text="{Binding Ponys[1].Id, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
                </WrapPanel>
                <WrapPanel Grid.Row="1">
                    <TextBlock Text="Name:"/>
                    <TextBox Text="{Binding Ponys[1].Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
                </WrapPanel>

                <WrapPanel Grid.Row="2">
                    <TextBlock Text="Color:"/>
                    <TextBox Text="{Binding Ponys[1].Color, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
                </WrapPanel>
            </StackPanel>
        </GroupBox>
    </Grid>

Demo-Usage:

public MainWindow() {
      InitializeComponent();     
      this.Ponys = new List<Pony>();
      this.Ponys.Add(new Pony() { Color = Brushes.HotPink });
      this.Ponys.Add(new Pony() { Id = 9, Name = "Not so fluffy", Color = Brushes.Chocolate });
      this.DataContext = this;

    }

Conclusion:

This Approach is designed to use in a Base-class. It fully supports Attribute-Validation and is capable of handling multiple Attributes per Property.

Since ValidationAttribute is not sealed, you can inherit from it and design your own validations.

Example:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
  public class Numeric : ValidationAttribute {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      if (value == null) {
        return ValidationResult.Success;
      }
      double result;
      var isNumeric = double.TryParse(value.ToString(), out result);
      return !isNumeric ? new ValidationResult(this.ErrorMessage) : ValidationResult.Success;
    }
  }

I developed this, because i hated how i had to use the ValidationRules. Additionally, the ValidationRules are only Input-Validations. This means, if your data is corrupt, you will never notice

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.