Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF- datagrid row validation errors to a textblock/label?

In WPF i have a DataDrid with different validations.

So far i used ToolTip in the DataGrid to show the error validation:

<DataGrid.RowValidationErrorTemplate>
   <ControlTemplate>
      <Grid Margin="0,-2,0,-2" ToolTip="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}},Path=(Validation.Errors)[0].ErrorContent}">
         <Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}" Height="{TemplateBinding FontSize}" />
         <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
      </Grid>
   </ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

Now the client asked to show the validation error in a different TextBlock/Label in the page- how do i do that? How do i use the: (Validation.Errors)[0].ErrorContent outside the DataGrid?

like image 269
DasDas Avatar asked Aug 06 '26 13:08

DasDas


1 Answers

Assuming you have a list (e.g. MyList) as your DataGrid.ItemsSource which is an ObservableCollection<ItemVM> where ItemVM represents the view-model of each DataGridRow then You can use a MultiBinding with a multi-value converter to save the last error message in a property of currently invalid ItemVM (e.g. ErrorMessage) and then retrieve it in your desired TextBlock.Text through binding.

You can say this is a trick for 3-way binding where TextBlock.Text and CurrentItem.ErrorMessage and (Validation.Errors)[0].ErrorContent are all bound together.

The following codes will clear things up:

In MainWindow:

public ObservableCollection<ItemVM> MyList { get; set; }
public ItemVM CurrentItem { get; set; }

In ItemVM:

public class ItemVM : INotifyPropertyChanged
{
    //implementation of INotifyPropertyChanged ...

    string _errorMessage;
    public string ErrorMessage
    {
        get
        {
            return _errorMessage;
        }
        set
        {
            _errorMessage = value;
            OnPropertyChanged("ErrorMessage");
        }
    }
    // Other Properties ...
}

In Xaml:

<Window.Resources>
    <local:MultiConverter x:Key="multiConv"/>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <DataGrid Grid.Row="0" ItemsSource="{Binding MyList}" >
        <DataGrid.RowValidationErrorTemplate>
            <ControlTemplate >
                <Grid Margin="0,-2,0,-2">
                    <Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}" Height="{TemplateBinding FontSize}" />
                    <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
                    <Grid.ToolTip>
                        <MultiBinding Converter="{StaticResource multiConv}">
                            <Binding Path="(Validation.Errors)[0].ErrorContent" RelativeSource="{RelativeSource Mode=FindAncestor,
                                                AncestorType=DataGridRow}"/>
                            <Binding Path="DataContext.CurrentItem" RelativeSource="{RelativeSource Mode=FindAncestor,
                                                AncestorType=Window}"/>
                        </MultiBinding>
                    </Grid.ToolTip>
                </Grid>
            </ControlTemplate>
        </DataGrid.RowValidationErrorTemplate>
    </DataGrid>
    <TextBox Grid.Row="1" Text="{Binding CurrentItem.ErrorMessage}"/>
</Grid>

And finally in MultiValueConverter:

public class MultiConverter : IMultiValueConverter
{
    public object Convert(
        object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        ((ItemVM)values[1]).ErrorMessage = values[0].ToString();
        return values[0].ToString();
    }

    public object[] ConvertBack(
        object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

EDIT

my DataGrid.ItemsSource is DataSet DefaultView- its DB based

Ok, since you are using DataSet.DefaultView as the ItemsSource do it like this:

1) First define a very small simple class that implements INotifyPropertyChangedto wrap your error message:

public class ErrorContainer: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    string _message;
    public string Message
    {
        get
        {return _message;}
        set
        {_message = value; OnPropertyChanged("Message");}
    }
}

2) Then use an instance of this class as a property in your MainWindow:

public partial class MainWindow : Window
{

....

    public ErrorContainer Error { get; set; }

        public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        // initialize ErrorContainer
        Error = new ErrorContainer();

        // some dummy test data (you're probably getting your data from DB instead)
        DataSet ds = new DataSet("MyDataSet");
        DataTable dt = new DataTable("MyDataTable");

        DataColumn propertyName = new DataColumn("Property");
        DataColumn propertyValue = new DataColumn("Value");
        DataColumn propertyDate = new DataColumn("Date", typeof(DateTime));

        dt.Columns.Add(propertyName);
        dt.Columns.Add(propertyValue);
        dt.Columns.Add(propertyDate);

        dt.Rows.Add("Name1", 1, DateTime.Now);
        dt.Rows.Add("Name2", 2, DateTime.Now);
        dt.Rows.Add("Name3", 3, DateTime.Now);

        ds.Tables.Add(dt);

        // bind DataGrid.ItemsSource (do NOT set this again in xaml)
        var srcBinding = new Binding();
        srcBinding.Source = ds.Tables[0].DefaultView;
        myDataGrid.SetBinding(DataGrid.ItemsSourceProperty, srcBinding);

    }
}

3) Now change the second path in the multi-binding to DataContext.Error and also change TextBox.Text binding to Error.Message:

<Grid>
     <DataGrid x:Name=x:Name="myDataGrid">
    ....

                        <MultiBinding Converter="{StaticResource multiConv}">
                            <Binding Path="(Validation.Errors)[0].ErrorContent" RelativeSource="{RelativeSource Mode=FindAncestor,
                                                AncestorType=DataGridRow}"/>
                            <Binding Path="DataContext.Error" RelativeSource="{RelativeSource Mode=FindAncestor,
                                                AncestorType=Window}"/>
                        </MultiBinding>
                    </Grid.ToolTip>
                </Grid>
            </ControlTemplate>
        </DataGrid.RowValidationErrorTemplate>
    </DataGrid>
    <TextBox Grid.Row="1" Text="{Binding Error.Message}"/>
</Grid>

4) And finally change your MultiValueConverter.Convert method to this:

    public object Convert(
        object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        ((ErrorContainer)values[1]).Message = values[0].ToString();
        return values[0].ToString();
    }
like image 88
Bahman_Aries Avatar answered Aug 09 '26 03:08

Bahman_Aries



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!