Reputation: 3009
I am able to display a validation error for one specific cell in a datagrid but I don't know how to do this if there are more than one valiation error. For instance, I have ten columns and there are validation errors in two or more cells.
Currently I have defined a style in a separate file included by App.xaml as ResourceDictionary.
The style:
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontFamily" Value="ArialMT"/>
<Setter Property="Height" Value="24"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="12" Height="12" Fill="Red" Stroke="Black" StrokeThickness="0.5"/>
<TextBlock FontWeight="Bold" Padding="4,0,0,0" Margin="0" VerticalAlignment="Top" Foreground="White" Text="!" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<!--<DataTrigger Binding="{Binding Path=(Validation.HasError), RelativeSource={RelativeSource AncestorType=DataGridRow}}" Value="true" >-->
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="IsEnabled" Value="True" />
<!--<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).Currentitem.ErrorContent}"/>-->
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="cycleErrStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(Validation.HasError), RelativeSource={RelativeSource AncestorType=DataGridRow}}" Value="true" >
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
The model class:
public class RawTag : IDataErrorInfo, INotifyPropertyChanged
{
private readonly int hash;
private string tagName;
private string cycle;
private string source;
public RawTag()
{
hash = GetHashCode();
}
public RawTag(string tagName, string cycle, string source)
{
TagName = tagName;
Cycle = cycle;
Source = source;
hash = GetHashCode();
}
public string TagName
{
get => tagName;
set
{
if (value == tagName) return;
tagName = value;
OnPropertyChanged();
}
}
// should be an integer but any entered value shall be accepted
public string Cycle
{
get => cycle;
set
{
if (value.Equals(cycle))
{
return;
}
cycle = value;
OnPropertyChanged();
}
}
public string Source
{
get => source;
set
{
if (value == source) return;
source = value;
OnPropertyChanged();
}
}
string IDataErrorInfo.Error
{
get
{
StringBuilder error = new StringBuilder();
if (string.IsNullOrEmpty(TagName))
{
error.Append("Name cannot be null or empty");
}
if (!int.TryParse(Cycle.ToString(), out int i))
{
error.Append("Cycle should be an integer value.");
}
return error.ToString();
}
}
string IDataErrorInfo.this[string columnName]
{
get
{
// apply property level validation rules
if (columnName == "TagName")
{
if (string.IsNullOrEmpty(TagName))
return "Name cannot be null or empty";
}
if (columnName == "Cycle")
{
if (!int.TryParse(Cycle.ToString(), out int i))
return "Cycle should be an integer value.";
}
if (columnName == "Source")
{
if (string.IsNullOrEmpty(Source))
return "Source must not be empty";
}
return "";
}
}
public override string ToString()
{
return "TagName: " + TagName + " Cycle: " + Cycle + " Source: " + Source;
}
public bool IsDirty()
{
return hash != GetHashCode();
}
protected bool Equals(RawTag other)
{
return string.Equals(TagName, other.TagName) && string.Equals(Cycle, other.Cycle) && string.Equals(Source, other.Source);
//return string.Equals(TagName, other.TagName) && Cycle == other.Cycle && string.Equals(Source, other.Source);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((RawTag)obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (TagName != null ? TagName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (Cycle != null ? Cycle.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (Source != null ? Source.GetHashCode() : 0);
return hashCode;
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
My validation class:
public class CycleValidationRule : ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
{
BindingGroup group = (BindingGroup)value;
StringBuilder error = null;
foreach (var item in group.Items)
{
IDataErrorInfo info = item as IDataErrorInfo;
if (info != null)
{
if (!string.IsNullOrEmpty(info.Error))
{
if (error == null)
{
error = new StringBuilder();
}
error.Append((error.Length != 0 ? ", " : "") + info.Error);
}
}
}
if (error != null)
return new ValidationResult(false, error.ToString());
else
return new ValidationResult(true, "");
}
}
After validation I want the faulty cells to behighlighted and when hovering a faulty cell only the error message for this specific cell's/field's validation error shall be displayed in the Tooltip.
Update 2019-01-17:
There must be a way to combine IDataErrorInfo
with ValidationRules
in that way that the error messages from IDataErrorInfo
gets into the ValidationRule
(which currently works well) where I can combine the error messages with the ones from ValidationRule
and propose this sum of error messages to the GUI (WPF). ...but only the error messages from IDataErrorInfo
are displayed in the Tooltip.
Upvotes: -1
Views: 707
Reputation: 169160
IDataErrorInfo
doesn't support multiple validation errors per property. The newer INotifyDataErrorInfo interface that was introduced in .NET Framework 4.5 does.
There is an example of how to implement it avalilable here.
You could use an ItemsControl
to display the error messages. Please see my answer here for an example.
Upvotes: 0