c# – 如何在WPF应用程序中显示错误消息?

我正在尝试使用
WPF创建一个应用程序.我正在尝试使用MVVM模型完全构建它.但是,我很困惑如何正确显示错误消息.我认为这将是微不足道的一步,但似乎是最复杂的.

我使用xaml创建了以下视图

 <StackPanel Style="{StaticResource Col}">
    <DockPanel>
        <Grid DockPanel.Dock="Top">
            <Grid.ColumnDefinitions >
                <ColumnDefinition Width="*" ></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="0" Style="{StaticResource Col}">
                <Label Content="Name" Style="{StaticResource FormLabel}" />
                <Border Style="{StaticResource FormInputBorder}">
                    <TextBox x:Name="Name" Style="{StaticResource FormControl}" Text="{Binding Name, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" />
                </Border>
            </StackPanel>
            <StackPanel Grid.Column="1" Style="{StaticResource Col}">
                <Label Content="Phone Number" Style="{StaticResource FormLabel}" />
                <Border Style="{StaticResource FormInputBorder}">
                    <TextBox x:Name="Phone" Style="{StaticResource FormControl}" Text="{Binding Phone, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" />
                </Border>
            </StackPanel>
        </Grid>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Style="{StaticResource PrimaryButton}" Command="{Binding Create}">Create</Button>
            <Button>Reset</Button>
        </StackPanel>
    </DockPanel>
</StackPanel>

然后我创建了以下ViewModel

public class VendorViewModel : ViewModel
{
    protected readonly IUnitOfWork UnitOfWork;
    private string _Name { get; set; }
    private string _Phone { get; set; }

    public VendorViewModel()
        : this(new UnitOfWork())
    {
    }

    public VendorViewModel(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    [Required(ErrorMessage = "The name is required")]
    [MinLength(5, ErrorMessage = "Name must be more than or equal to 5 letters")] 
    [MaxLength(50, ErrorMessage = "Name must be less than or equal to 50 letters")] 
    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;
            NotifyPropertyChanged();
        }
    }

    public string Phone
    {
        get { return _Phone; }
        set
        {
            _Phone = value;
            NotifyPropertyChanged();
        }
    }

    /// <summary>
    /// Gets the collection of customer loaded from the data store.
    /// </summary>
    public ICollection<Vendor> Vendors { get; private set; }

    protected void AddVendor()
    {
        var vendor = new Vendor(Name, Phone);
        UnitOfWork.Vendors.Add(vendor);
    }

    public ICommand Create
    {
        get
        {
            return new ActionCommand(p => AddVendor(),
                                     p => IsValidRequest());
        }
    }

    public bool IsValidRequest()
    {
        // There got to be a better way to check if everything passed or now...
        return IsValid("Name") && IsValid("Phone");
    }
}

这是我的ViewModel基类的样子

public abstract class ViewModel : ObservableObject, IDataErrorInfo
{
    /// <summary>
    /// Gets the validation error for a property whose name matches the specified <see cref="columnName"/>.
    /// </summary>
    /// <param name="columnName">The name of the property to validate.</param>
    /// <returns>Returns a validation error if there is one, otherwise returns null.</returns>
    public string this[string columnName]
    {
        get { return OnValidate(columnName); }
    }

    /// <summary>
    /// Validates a property whose name matches the specified <see cref="propertyName"/>.
    /// </summary>
    /// <param name="propertyName">The name of the property to validate.</param>
    /// <returns>Returns a validation error, if any, otherwise returns null.</returns>
    protected virtual string OnValidate(string propertyName)
    {
        var context = new ValidationContext(this)
        {
            MemberName = propertyName
        };

        var results = new Collection<ValidationResult>();
        bool isValid = Validator.TryValidateObject(this, context, results, true);

        if (!isValid)
        {
            ValidationResult result = results.SingleOrDefault(p =>                                                                  p.MemberNames.Any(memberName => memberName == propertyName));
            if (result != null)
                return result.ErrorMessage;
        }
        return null;
    }

    protected virtual bool IsValid(string propertyName)
    {
        return OnValidate(propertyName) == null;
    }

    /// <summary>
    /// Not supported.
    /// </summary>
    [Obsolete]
    public string Error
    {
        get
        {
            throw new NotSupportedException();
        }
    }
}

这是我的ObservableObject类

public class ObservableObject : INotifyPropertyChanged
{
    /// <summary>
    /// Raised when the value of a property has changed.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises <see cref="PropertyChanged"/> for the property whose name matches <see cref="propertyName"/>.
    /// </summary>
    /// <param name="propertyName">Optional. The name of the property whose value has changed.</param>
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我的目标是在不正确的字段周围显示一个红色边框,然后在它下面显示错误消息,告诉使用出了什么问题.

如何正确显示错误?另外,如何在首次加载视图时不显示任何错误?

基于此blog,我需要编辑Validation.ErrorTemplate

所以我尝试将以下代码添加到App.xaml文件中

    <!-- Style the error validation by showing the text message under the field -->
    <Style TargetType="TextBox">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel>
                        <Border BorderThickness="1" BorderBrush="DarkRed">
                            <StackPanel>
                                <AdornedElementPlaceholder x:Name="errorControl" />
                            </StackPanel>
                        </Border>
                        <TextBlock Text="{Binding AdornedElement.ToolTip, ElementName=errorControl}" Foreground="Red" />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="BorderBrush" Value="Red" />
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
            </Trigger>
        </Style.Triggers>
    </Style>

但是这没有显示错误消息,也是在第一次加载视图时我得到一个错误.最后,即使表单生效,操作按钮也会保持禁用状态.

更新
将Property =“Validation.ErrorTemplate”移动到FormControl组后,它工作了.但是,错误消息似乎是通过按钮而不是按下按钮.此外,文本似乎没有垂直包装,允许边框覆盖其他控件,如下面的屏幕显示所示.

《c# – 如何在WPF应用程序中显示错误消息?》
《c# – 如何在WPF应用程序中显示错误消息?》
《c# – 如何在WPF应用程序中显示错误消息?》

最佳答案 我会尽力回答你的所有问题:

如何正确显示错误?

不应用ErrorTemplate,因为TextBox上的FormControl样式优先于包含Validation.ErrorTemplate的样式.将Validation.ErrorTemplate代码移动到FormControl样式将解决此问题.

另外,如何在首次加载视图时不显示任何错误?

如果不立即应用,必要验证的用途是什么?只有在开始键入字段时才会执行MinLength和MaxLength验证.

但是,错误消息似乎是通过按钮而不是按下按钮.

正如Will指出的那样,这是因为错误消息显示在AdornerLayer上,它不会干扰控件所在的层.您有以下选择:

>使用AdornerLayer但在控件之间留出一些空间
>使用工具提示显示错误消息
>在TextBox的模板中使用额外的TextBlock,以显示错误消息.

这些选项描述为here

点赞