I was monitoring my Twitter feed, like I always do, and saw a tweet come through asking about how to validate an object’s property against it’s parent ObservableCollection in the Infragistics XamDataGrid. What this person was trying to accomplish was to validate a duplicate item in his data source. They have a POCO object the implements the IDataErrorInfo interface, and an ObservableColletion<POCO> as the data source. They want to check the ObservableCollection<POCO> for a pre-existing item whenever a property value in a POCO changes. Obviously this is not a XamDataGrid issue, but rather an object design issue. So I decided to whip up a quick and dirty solution to solve this particular issue.
The approach I am going to take is to simply keep track of the parent collection from within the POCO object itself. I really like interfaces, so I started out by defining a simple interface my POCOs will implement.
{
object Parent { get; set; }
}
Now let’s take a look at the simple POCO I will be using in this example.
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged(“FirstName”);
}
} private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged(“LastName”);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public string this[string columnName]
{
get
{
if (columnName == “FirstName”)
{
if (String.IsNullOrWhiteSpace(FirstName))
return “First name cannot be empty”;
}
if (columnName == “LastName”)
{
if (String.IsNullOrWhiteSpace(LastName))
return “Last name cannot be empty”;
}
return String.Empty;
}
}
public string Error
{
get { return String.Empty; }
}
}
Notice that we implement both the INotifyPropertyChanged interfaces as well as the IDataErrorInfo interface. The Next thing we need is a custom ObservableCollection to use as our data source.
{
protected override void InsertItem(int index, T item)
{
//set the parent object when a new item is added to our collection
if (item != null && item is IHasParent)
(item as IHasParent).Parent = this; base.InsertItem(index, item);
}
}
Notice that we are casting our item as the IHasParent interface and setting the Parent property accordingly. The next thing we need to do is have our POCO class implement the IHasPerson interface.
{
public object Parent { get; set; } private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged(“FirstName”);
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged(“LastName”);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public string this[string columnName]
{
get
{
if (columnName == “FirstName”)
{
if (String.IsNullOrWhiteSpace(FirstName))
return “First name cannot be empty”;
}
if (columnName == “LastName”)
{
if (String.IsNullOrWhiteSpace(LastName))
return “Last name cannot be empty”;
if (Parent != null && Parent is IList<Person>)
{
var list = (IList<Person>) Parent;
if (list.Count(x => x.LastName == LastName) > 1)
return “This last name already exists. Please use a different last name.”;
}
}
return String.Empty;
}
}
public string Error
{
get { return String.Empty; }
}
}
Notice how we implemented the check for a duplicate last name. We simply checked to make sure we are dealing with the LastName property. Then we cast the Parent as an IList<Person> so that we can perform a simply LINQ query against it. We check the parent collection for any results that match the LastName property. If more than one is returned we have a duplicate.
So let’s test this baby using the XamDataGrid as the original poster was attempting to do. First create our UI.
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout>
<igDP:Field Name=”FirstName” Label=”First Name” />
<igDP:Field Name=”LastName” Label=”Last Name” />
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
<igDP:XamDataGrid.FieldLayoutSettings>
<igDP:FieldLayoutSettings AddNewRecordLocation=”OnTop”
AllowAddNew=”True”
AutoGenerateFields=”False”
SupportDataErrorInfo=”RecordsAndCells”
DataErrorDisplayMode=”Highlight” />
</igDP:XamDataGrid.FieldLayoutSettings>
</igDP:XamDataGrid>
Next let’s hook up some data to this bad boy:
{
InitializeComponent(); var people = new HasParentObservableCollection<Person>();
people.Add(new Person(){ FirstName = “Brian”, LastName = “Lagunas”});
xamDataGrid1.DataSource = people;
}
Now let’s run the app, type a duplicate last name, and see what we get.
Cool. Works as expected. Now there are a number of ways to accomplish this task. There are even frameworks out there that have already solved this problem for you such as CSLA. I hope this simple approach helps you find a solution that works for you.
Thanks for your answer. That solution works and I like how you used the interface.
We had a followup question. We noticed that the XamlDataGrid calls the IDataErrorInfo.this[string fieldName] method on our object quite a lot on a single column change. When we tab away from the cell, causing it to lose focus, this method got called over a dozen times. Is that to be expected?
The this[] method will get called each time the value is updated. In the case of the XamDataGrid that means everytime you type a key in the cell (UpdateSourcetrigger=PropertyChanged) the method will get called for that single property. Now when you leave the row, the record will leave edit mode and then the this[] method would be called for each property on the object. There are also internal working for the grid that may cause this to get called. You will get the same result with Microsoft’s DataGrid.
I will talk to my developers and see if I can get the underlying cause of the multiple executions.
Thanks for looking into this for us.
Hello Brian,
I found your solution very useful, and i have used the same technique for xamdatagrid cell validation in my project.
But my requirement is that an invalid value which is entered from the new row control should not get added in the above table.
can you please guide me for the same.
Thanks in advance,Rakesh