【问题标题】:How to display multiple IDataErrorInfo's error in tooltip on WPF DataGridTextColumn for hovered cell?如何在 WPF DataGridTextColumn 的工具提示中为悬停单元格显示多个 IDataErrorInfo 错误?
【发布时间】:2019-06-10 11:47:58
【问题描述】:

我能够在数据网格中显示一个特定单元格的验证错误,但如果有多个验证错误,我不知道该怎么做.例如,我有十列,两个或更多单元格中存在验证错误。

目前我已在 App.xaml 包含的单独文件中将样式定义为 ResourceDictionary。

风格:

<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>

模型类:

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));
    }
}

我的验证类:

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, "");

    }
}

验证后,我希望突出显示错误单元格,当悬停错误单元格时,工具提示中将仅显示此特定单元格/字段验证错误的错误消息。

2019-01-17 更新:

必须有一种方法可以将IDataErrorInfoValidationRules 结合起来,这样IDataErrorInfo 的错误消息就会进入ValidationRule(目前运行良好),我可以结合 错误消息与来自ValidationRule 的错误消息一起向 GUI (WPF) 提出这些错误消息。 ...但只有来自IDataErrorInfo 的错误消息会显示在工具提示中。

【问题讨论】:

  • 错误是在 RawTag 类中设置的。尝试在 Cycle 单元格中插入一个字符串。

标签: c# datagrid styles idataerrorinfo validationrule


【解决方案1】:

IDataErrorInfo 不支持每个属性出现多个验证错误。 .NET Framework 4.5 中引入的较新的INotifyDataErrorInfo 接口可以。

有一个如何实现它的示例可用here

您可以使用ItemsControl 来显示错误消息。请看我的回答here 的例子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 2022-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多