0

My issue is that I want to show a user input error. I know about INotifyDataErrorInfo and have it implemented fine, however one outstanding issue is that when I put an invalid value in a DataGrid e.g. an alphabetic character although it shows the red border as it should I would like to have an additional error message when I validate the data on demand by clicking a button.

My idea was to get VisualChildren of DataGridCell type and then extract HasError property, but it does not show any errors.

In other words I want that when I insert invalid character for example a letter and then clicking Validate button it will show me an error message.

the code is as follows:

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;


namespace Get_User_input_Errors_From_DataGrid
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            List<DataGridCell> VisualChildren = Helpers.FindVisualChildren<DataGridCell>(mainWindow).ToList();
            List<DependencyObject> LogicalChildren = Helpers.FindLogicalChildren<DependencyObject>(mainWindow).ToList();
            foreach (DataGridCell cell in VisualChildren)
            {
                bool hasError = Validation.GetHasError(cell);
                if (hasError)
                {
                    MessageBox.Show("Error at:" + cell.Content.ToString());

                }
            }
        }



    }
  
}

MainWindow.xaml

<Window x:Class="Get_User_input_Errors_From_DataGrid.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:Get_User_input_Errors_From_DataGrid"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        x:Name="mainWindow">
    <Window.DataContext>
        <local:ViewModel></local:ViewModel>
    </Window.DataContext>
    <Grid>
        <StackPanel Orientation="Vertical">
            <Button HorizontalAlignment="Left" Click="Button_Click">Validate data</Button>
            <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=People,Mode=TwoWay}">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}"></DataGridTextColumn>
                    <DataGridTextColumn Header="Age">
                        <DataGridTextColumn.Binding>
                            <Binding Path="Age">
                                <Binding.ValidationRules>
                                    <ExceptionValidationRule></ExceptionValidationRule>
                                </Binding.ValidationRules>
                            </Binding>
                        </DataGridTextColumn.Binding>
                    </DataGridTextColumn>
                    <DataGridTextColumn Header="Weight" Binding="{Binding Path=Weight}"></DataGridTextColumn>
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
        
    </Grid>
</Window>

Person.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Get_User_input_Errors_From_DataGrid
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int Weight { get; set; }
    }
}

ViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;


namespace Get_User_input_Errors_From_DataGrid
{
    public class ViewModel
    {
        public ObservableCollection<Person> People { get; set; }

        public ViewModel()
        {
            People = new()
            {
                new()
                {
                    Age = 18,
                    Name = "Alex",
                    Weight = 98
                },
                new()
                {
                    Age = 28,
                    Name = "Olivia",
                    Weight = 76
                },
                new()
                {
                    Age = 78,
                    Name = "Vincent",
                    Weight = 67
                }
            };
        }
    }
}

Helpers.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Media;
using System.Windows;

namespace Get_User_input_Errors_From_DataGrid
{
    public static class Helpers
    {
        public static IEnumerable<T> FindVisualChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
        {
            if (parent == null)
                throw new ArgumentNullException(nameof(parent));

            var queue = new Queue<DependencyObject>(new[] { parent });

            while (queue.Any())
            {
                var reference = queue.Dequeue();
                var count = VisualTreeHelper.GetChildrenCount(reference);

                for (var i = 0; i < count; i++)
                {
                    var child = VisualTreeHelper.GetChild(reference, i);
                    if (child is T children)
                        yield return children;

                    queue.Enqueue(child);
                }
            }
        }
        public static IEnumerable<T> FindLogicalChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
        {
            if (parent == null)
                throw new ArgumentNullException(nameof(parent));

            var queue = new Queue<DependencyObject>(new[] { parent });

            while (queue.Any())
            {
                var reference = queue.Dequeue();
                var children = LogicalTreeHelper.GetChildren(reference);
                var objects = children.OfType<DependencyObject>();

                foreach (var o in objects)
                {
                    if (o is T child)
                        yield return child;

                    queue.Enqueue(o);
                }
            }
        }
    }
}
Piotr Golacki
  • 195
  • 3
  • 11

1 Answers1

0

OK I think that I need to use TextBox instead of DataGridCell

 private void Button_Click(object sender, RoutedEventArgs e)
        {
            List<TextBox> VisualChildren = Helpers.FindVisualChildren<TextBox>(mainWindow).ToList();
            List<DependencyObject> LogicalChildren = Helpers.FindLogicalChildren<DependencyObject>(mainWindow).ToList();
            foreach (TextBox textBox in VisualChildren)
            {
                bool hasError = Validation.GetHasError(textBox);
                if (hasError)
                {
                    MessageBox.Show("Error message");

                }
            }
        }

Then if I want to ensure that my textbox is in fact in a DataGrid I can do check like this and extract the information about the cell:

 if (dependencyObject is TextBox)
                        {
                            TextBox textBox = dependencyObject as TextBox;
                            if (textBox.Parent is DataGridCell)
                            {
                                DataGridCell dataGridCell = textBox.Parent as DataGridCell;
                                errorMessage =error.ErrorContent + "\r\nAt column: " +
                            dataGridCell.Column.Header +
                             "\r\nand row: " + (DataGridRow.GetRowContainingElement(dataGridCell).GetIndex() + 1);
                            }
Piotr Golacki
  • 195
  • 3
  • 11