question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Invalid behavior of the icon in row header in data grid when record implements INotifyDataErrorInfo

See original GitHub issue

Description

  1. Add DataGrid
  2. Set as ItemsSource ObservableCollection with records which implement INotifyDataErrorInfo and INotifyPropertyChanged and with errors after initialization
  3. Run app
  4. Textbox has red border but icon is missing (strange but I can live with that)
  5. Double click on cell (icon in row header is shown)
  6. fix value and hit enter
  7. Textbox hasn’t got red border (PASS) but icon with tooltip with original error is still there (FAIL)

Am I implemented it incorrectly? Or it’s an issue in header style?

Environment

  • .NET Framework 4.7.2
  • Windows 10 1903

Nuget libraries

  • MahApps.Metro 1.6.5
  • FluentValidation 8.5.1 (for validation)
  • Fody 6.0.4
  • PropertyChanged.Fody 3.1.3 (for autiomatic notifications)
  • Caliburn.Micro 3.2.0 (only base class)

Code

using Caliburn.Micro;
using FluentValidation;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace WpfDataGridValidation
{
public class MainWindowViewModel : Conductor<IScreen>.Collection.AllActive
{
    public MainWindowViewModel()
    {
        var validator = new MyRecordValidator();
        var record = new MyRecord(validator);
        Records = new ObservableCollection<MyRecord>(Enumerable.Repeat(record, 1));
    }

    public ObservableCollection<MyRecord> Records { get; set; }
}

public class MyRecord : Caliburn.Micro.PropertyChangedBase, INotifyDataErrorInfo
{
    private readonly FluentValidation.AbstractValidator<MyRecord> _validator;

    public MyRecord(FluentValidation.AbstractValidator<MyRecord> validator)
    {
        _validator = validator;
        Validate();
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public bool HasErrors => Errors.Any();

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
            return null;
        if (Errors.TryGetValue(propertyName, out List<string> value))
            return value;
        return Enumerable.Empty<string>();
    }

    private void Validate()
    {
        if (_validator == null)
            return;
        var previousKeys = Errors.Select(x => x.Key).ToList();
        Errors = _validator.Validate(this)
            .Errors
            .GroupBy(x => x.PropertyName)
            .ToDictionary(x => x.Key, x => x.Select(y => y.ErrorMessage).ToList());
        if (ErrorsChanged == null)
            return;
        foreach (var key in Errors.Keys)
            ErrorsChanged.Invoke(this, new DataErrorsChangedEventArgs(key));
        foreach (var key in previousKeys.Except(Errors.Select(x => x.Key)))
            ErrorsChanged.Invoke(this, new DataErrorsChangedEventArgs(key));
    }

    public override void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
    {
        if (propertyName != null && propertyName != nameof(Errors) && propertyName != nameof(HasErrors))
            Validate();
        base.NotifyOfPropertyChange(propertyName);
    }

    public string Name { get; set; }
    public Dictionary<string, List<string>> Errors { get; private set; } = new Dictionary<string, List<string>>();
}

public class MyRecordValidator : FluentValidation.AbstractValidator<MyRecord>
{
    public MyRecordValidator()
    {
        RuleFor(x => x.Name).NotEmpty();
    }
}
}

MainWindow view

<Window x:Class="WpfDataGridValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfDataGridValidation"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <DataGrid
            x:Name="dataGrid"
            ItemsSource="{Binding Records}"
            CanUserAddRows="False"
            CanUserSortColumns="False"
            CanUserResizeColumns="False"
            CanUserResizeRows="False"
            CanUserReorderColumns="False"    
            SelectionMode="Single"
            SelectionUnit="Cell"
            AutoGenerateColumns="False"
            VerticalAlignment="Top"
            HorizontalAlignment="Left"
            GridLinesVisibility="All"
            VirtualizingStackPanel.VirtualizationMode="Standard"
            HeadersVisibility="All"
            RowHeaderWidth="40"
            >
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

MainWindow bode behind

using System.Windows;

namespace WpfDataGridValidation
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();
        }
    }
}

App.xaml

<Application x:Class="WpfDataGridValidation.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfDataGridValidation"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Green.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:16

github_iconTop GitHub Comments

1reaction
danielklechacommented, Nov 27, 2019

Hi @timunie I updated my demo with my custom style and everything is working with INotifyDataErrorInfo (second data grid). I used INotifyDataErrorInfo.HasErrors and custom property Error. Probably style should be moved to separate file. Download changes and check it.

I had a problem to use “MahApps.Brushes.Controls.Validation” and “MahApps.Brushes.Text.Validation” styles so I copied it manually. I’m not a fan of this solutions but I don’t see a better way for INotifyDataErrorInfo.

Btw I didn’t tested it with .NET Core - maybe they fixed this interface.

1reaction
danielklechacommented, Nov 12, 2019

I tested DataGrid with IDataErrorInfo. Everything is working when UpdateSourceTrigger is set to LostFocus. Binding mode can’t be set to TwoWay but we can use Default and control finally use TwoWay as expected. Also I tested it with my implementation of ICustomTypeDescriptor and wverything is correct.

I was not able to get working example for INotifyDataErrorInfo interface. I suspect that something is wrong with this interface ant it can’t work with DataGrid.

For now I think that we can close this thread. @timunie once again thanks for help!

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - DataGridRow error indicator not working with ...
I'm attempting to update my ViewModel to use INotifyDataErrorInfo instead of IDataErrorInfo and am running into the following problem:.
Read more >
How to: Implement Validation with the DataGrid Control
When a user enters an invalid value, a red circle with a white exclamation mark appears in the row header. This occurs for...
Read more >
TableView Properties | WPF Controls
Gets or sets whether column headers are automatically scrolled once a user drags a column header to the View's left or right. This...
Read more >
WinForms DataGrid | DataGridView in C# | Syncfusion
The high performance WinForms datagrid control displays tabular and hierarchical data. It supports sorting, grouping, filtering, dragand drop the rows, etc.
Read more >
ComponentSource News | Grid Components - RSSing.com
INotifyDataErrorInfo Validation Support - The WPF Data Grid and TreeList controls automatically display validation errors within data source objects that ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found