Search code examples
wpfmvvmobservablecollectionidataerrorinfo

IDataErrorInfo on ObservableCollection


I have a viewmodel that implements IDataError. In the viewmodel I have an ObservableCollection. The ObservableCollection populates a datagrid in my view:

    // the list that populates the datagrid
    public ObservableCollection<ProjectExpenseItemsDto> ListOfProjectExpenseItems
    {
        get { return listOfProjectExpenseItems; }
        set
        {
            if (listOfProjectExpenseItems != value)
            {
                listOfProjectExpenseItems = value;
                NotifyPropertyChanged("ListOfProjectExpenseItems");
            }
        }
    }

I also have a property that represents the selected item in the datagrid (it is based off a Dto):

    // the selected row in the datagrid
    public ProjectExpenseItemsDto SelectedProjectExpenseItem
    {
        get { return selectedProjectExpenseItem; }
        set
        {
            if (selectedProjectExpenseItem != value)
            {
                selectedProjectExpenseItem = value;
                NotifyPropertyChanged("SelectedProjectExpenseItem");
            }
        }
    }

Here is the Dto:

namespace ProjectExpense.Model.Dto
{
    [DataContract]
    public class ProjectExpenseItemsDto
    {
        [DataMember]
        public int RowID { get; set; }
        [DataMember]
        public int ProjectExpenseID { get; set; }
        [DataMember]
        public string ItemNumber { get; set; }
        [DataMember]
        public string ItemDescription { get; set; }
        [DataMember]
        public decimal ItemUnitPrice { get; set; }
        [DataMember]
        public decimal ItemQty { get; set; }
        [DataMember]
        public string SupplierName { get; set; }
        [DataMember]
        public DateTime CreateDate { get; set; }
    }
}

I want to use IDataError to validate values in the selected row of the datagrid (SelectedProjectExpenseItem), but since my grid is bound to the ObservableCollection, I don't have any properties in my viewmodel; therefore, I can only use IDataError against the ObservableCollection, not the individual items in the collection, which doesn't help me because I have know way to see "inside" the collection. I cannot use IDataError for my SelectedProjectExpenseItem either. For example:

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            string result = string.Empty;
            propertyName = propertyName ?? string.Empty;
            if (propertyName == string.Empty || propertyName == "ItemNumber")
            {
                if (string.IsNullOrEmpty(SelectedProjectExpenseItem.ItemNumber))
                {
                    result = "Name cannot be blank!";
                }
            }
            return result;
        }
    }

this doesn't fire because my datagrid column is not bound to the SelectedProjectExpenseItem.ItemNumber, it is bound to the ItemNumber in the ObservableCollection.

I am looking for any guidance as this is really confusing me.

---------------------------- EDIT: ----------------------------

Ok, I created a separate viewmodel for my DTO:

namespace ProjectExpense.ViewModels
{
    public class ProjectExpenseItemsDtoViewModel : ProjectExpenseItemsDto, IDataErrorInfo
    {
        public ProjectExpenseItemsDtoViewModel()
        {
            Initialize();
        }

        private void Initialize()
        {
        }

        #region Validation

        // string method
        static bool IsStringMissing(string value)
        {
            return String.IsNullOrEmpty(value) || value.Trim() == String.Empty;
        }

        #endregion

        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                return this[string.Empty];
            }
        }

        public string this[string propertyName]
        {
            get
            {
                string result = string.Empty;
                if (propertyName == "ItemNumber")
                {
                    if (IsStringMissing(this.ItemNumber))
                        result = "Item number cannot be empty!";
                    if (this.ItemNumber.Length > 50)
                        return "Item number exceeds 50 characters";
                }
                return result;
            }
        }

        #endregion
    }
}

Now, I am having problems with the following line in my main vm:

IList<ProjectExpenseItemsDtoViewModel> iList = projectExpenseItemsRepository.GetProjectExpenseItems(ProjectExpenseID);
foreach (ProjectExpenseItemsDtoViewModel item in iList)
   ListOfProjectExpenseItems.Add(item);

It says:

Cannot implicitly convert type 'System.Collections.Generic.IList' to 'System.Collections.Generic.IList'. An explicit conversion exists (are you missing a cast?)

Any ideas?

---------------------------- EDIT: ----------------------------

I found this link, gonna see if I can copy what the person is doing:

Validation-in-a-WPF-DataGrid


Solution

  • The problems had to do with using Dto's in my implementation, or at least the lack of implementing IDataErrorInfo in them. I decided to dump them and go with straight business objects that implement IDataErroInfo and wa-la, everything works beautifully now.