1
votes

I have a datagrid which I validate using IDataErrorInfo .Now I want to show the error messages if validation fail when the save button is clicked.My problem is how do I get the validation errors ,I tried lot of pages on the web I am really tired of these anybody know the solution ? My datagrid is :

 <my:DataGrid Name="dgDamagedProducts" ItemsSource="{Binding}"   SelectionUnit="Cell"  BeginningEdit="dgDamagedProducts_BeginningEdit"  AutoGenerateColumns="False" Margin="13,75,9,117" RowEditEnding="dgDamagedProducts_RowEditEnding" GotFocus="dgDamagedProducts_GotFocus">
                    <my:DataGrid.Columns>
                        <!--0-ProductTransaction ID Column-->
                        <my:DataGridTextColumn  Header="ProductTransaction ID"  Visibility="Hidden" Width="0" Binding="{Binding ProductTransactionID}"></my:DataGridTextColumn>
                        <!--1-Item incID Column-->
                        <my:DataGridTextColumn  Header="ItemID" Visibility="Hidden" Width="0" Binding="{Binding ItemID}"></my:DataGridTextColumn>
                        <!--2-Product Code Column-->
                        <my:DataGridTextColumn  Header="Code" Width="100" Binding="{Binding ProductCode}"></my:DataGridTextColumn>
                        <!--3-Product Column-->
                        <my:DataGridTemplateColumn Header="Product Name" Width="200">
                            <my:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Padding="3" Text="{Binding ProductName,NotifyOnValidationError=True,ValidatesOnDataErrors=True}" Style="{StaticResource TextBlockInError}"></TextBlock>
                                </DataTemplate>
                            </my:DataGridTemplateColumn.CellTemplate>
                            <my:DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <TextBox x:Name="txtbxProduct" Style="{StaticResource TextBoxInError}" Text="{Binding Path=ProductName,Mode=TwoWay,NotifyOnValidationError=True,ValidatesOnDataErrors=True}" lc:FocusAttacher.Focus="True" TextChanged="txtbxProduct_TextChanged" PreviewKeyDown="txtbxProduct_PreviewKeyDown" ></TextBox>
                                </DataTemplate>
                            </my:DataGridTemplateColumn.CellEditingTemplate>
                        </my:DataGridTemplateColumn>

                        <!--7-Purchase Rate Column-->
                        <my:DataGridTextColumn Header="Purchase Rate" Width="100" Binding="{Binding PurchaseRate}" IsReadOnly="True"></my:DataGridTextColumn>
                        <!--8-Stock  Column-->
                        <my:DataGridTextColumn Header="Stock"  Binding="{Binding AvailableQty}" IsReadOnly="True" Visibility="Hidden"></my:DataGridTextColumn>
                        <!--9-Qty Column-->
                        <my:DataGridTemplateColumn Header="Qty" Width="100">
                            <my:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Qty,ValidatesOnDataErrors=True}" Style="{StaticResource TextBlockInError}"></TextBlock>
                                </DataTemplate>
                            </my:DataGridTemplateColumn.CellTemplate>
                            <my:DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <RH:IntTextBox  x:Name="txtbxQty" Style="{StaticResource TextBoxInError}" Text="{Binding Qty,Mode=TwoWay,UpdateSourceTrigger=LostFocus,ValidatesOnDataErrors=True}" lc:FocusAttacher.Focus="True" LostFocus="txtbxQty_LostFocus" PreviewKeyDown="txtbxQty_PreviewKeyDown"></RH:IntTextBox>
                                </DataTemplate>
                            </my:DataGridTemplateColumn.CellEditingTemplate>
                        </my:DataGridTemplateColumn>
                        <!--10-Amount Column-->
                        <my:DataGridTextColumn Header="Amount" Width="100" Binding="{Binding Amount}" IsReadOnly="True" ></my:DataGridTextColumn>

                    </my:DataGrid.Columns>
                </my:DataGrid>

Edit

My class is as follows :

 class clsDamagedProducts : INotifyPropertyChanged,IDataErrorInfo
{
    private int _ProductTransactionID;
    private int _ItemID;
    private string _ProductCode;
    private string _ProductName;
    private string _Batch;
    private int _UnitID;
    private string _UnitName;
    private decimal _PurchaseRate;
    private int _AvailableQty;
    private int _Qty;
    private decimal _Amount;
    private string _Remark;

    #region Property Getters and Setters

    public int ProductTransactionID 
    {
        get { return _ProductTransactionID; }
        set
        {
            _ProductTransactionID = value;
            OnPropertyChanged("ProductTransactionID");
        } 
    }

    public int ItemID
    {
        get { return _ItemID; }
        set
        {
            _ItemID = value;
            OnPropertyChanged("ItemID");
        }
    }

    public string ProductCode
    {
        get { return _ProductCode; }
        set
        {
            _ProductCode = value;
            OnPropertyChanged("ProductCode");
        }
    }

    public string ProductName
    {
        get { return _ProductName; }
        set
        {
            _ProductName = value;
            OnPropertyChanged("ProductName");
        }
    }

    public string Batch
    {
        get { return _Batch; }
        set
        {
            _Batch = value;
            OnPropertyChanged("Batch");
        }
    }

    public int UnitID
    {
        get { return _UnitID; }
        set
        {
            _UnitID = value;
            OnPropertyChanged("UnitID");
        }
    }

    public string UnitName
    {
        get { return _UnitName; }
        set
        {
            _UnitName = value;
            OnPropertyChanged("UnitName");
        }
    }

    public decimal PurchaseRate
    {
        get { return _PurchaseRate; }
        set
        {
            _PurchaseRate = value;
            OnPropertyChanged("PurchaseRate");
        }
    }

    public int AvailableQty
    {
        get { return _AvailableQty; }
        set
        {
            _AvailableQty = value;
            OnPropertyChanged("AvailableQty");
        }
    }

    public int Qty
    {
        get { return _Qty; }
        set
        {
            _Qty = value;
            this._Amount = this._Qty * this._PurchaseRate;
            OnPropertyChanged("Qty");
            OnPropertyChanged("Amount");
        }
    }

    public decimal Amount
    {
        get { return _Amount; }
        set
        {
            _Amount = value;
            OnPropertyChanged("Amount");
        }
    }

    public string Remark
    {
        get { return _Remark; }
        set
        {
            _Remark = value;
            OnPropertyChanged("Remark");
        }
    }

    #endregion

    #region IDataErrorInfo Members

    public string Error
    {
        get
        {
            return "";
        }
    }

    public string this[string name]
    {
        get
        {
            string result = null;

            if (name == "ProductName")
            {
                int count = Global.ItemExist(this._ProductName);
                if (count == 0) 
                {
                    result = "Invalid Product";

                }
            }

            else if (name == "Qty")
            {
                if (this._Qty >this._AvailableQty)
                {
                    result = "Qty must be less than Available Qty . Avaialble Qty : "+this._AvailableQty;
                }
            }

            return result;
        }
    }

    #endregion

    #region INotifyPropertyChanged Members

    // Declare the event
    public event PropertyChangedEventHandler PropertyChanged;

    //// Create the OnPropertyChanged method to raise the event
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    #endregion
}
1

1 Answers

0
votes

The IDataErrorInfo interface exposes a property named Error. This is the property that contains the textual error message that you set in the indexer that you have to implement as part of the interface.

Please note that this interface will only show one error message at a time without user customisation. It is difficult to give you an accurate example because you didn't tell us any details of your class that implements this interface. However, it goes a bit like this:

Your indexer:

public override string this[string propertyName]
{
    if (propertyName = "Name" && Name == string.Empty) return "Please add the name";
    return string.Empty;
}

When the interface sees a change to a property, it calls this indexer. If the name of the property is found in here with an error message, that message is set in the Error property of the data class that implements the interface. Therefore, to see the message, you simply Bind to the Error property:

<TextBlock Text="{Binding Error}" Foreground="Red" />
<TextBlock Text="{Binding Name}" />

(Of course, the colour is optional).

UPDATE >>>

The IDataErrorInfo interface is implemented by the model/data type class(es). Therefore, the Error property is in the data type class (as you have shown in your code) and you can access it in just the same way that you would access any other property from that class:

clsDamagedProducts instance = new clsDamagedProducts();
instance.UnitName = "Some Name";
MessageBox.Show(instance.Error);

UPDATE 2 >>>

There are many ways to get these validation errors. One way is to simply pass each property name that you want to validate to the indexer method and return the result:

public string Error
{
    get
    {
        if (this["Name"] != string.Empty) return this["Name"];
        ...
        else if (this["OtherProperty"] != string.Empty) return this["OtherProperty"];
        return string.Empty;
    }
}

You mentioned the Validation.Errors property, which you can also use, but in order to do that, you need to set the ValidatesOnDataErrors and/or the NotifyOnValidationError properties to True. Please refer to the WPF: Validation made easy with IDataErrorInfo and WPF Validation with Attributes and IDataErrorInfo interface in MVVM articles for more information on these.

I personally prefer not to use those properties, or the Error property directly. Instead, I have an abstract collection property named Errors in my base class which is overridden in derived classes:

public override ObservableCollection<string> Errors
{
    get
    {
        errors = new ObservableCollection<string>();
        errors.AddUniqueIfNotEmpty(this["Name"]);
        errors.AddUniqueIfNotEmpty(this["EmailAddresses"]);
        errors.AddUniqueIfNotEmpty(this["StatementPrefixes"]);
        return errors;
    }
}

As you can see... this enables me to view multiple errors at once.