Silverlight cross-child validation & property change notification

Silverlight cross-child validation & property change notification

Old forum URL: forums.lhotka.net/forums/t/7287.aspx


esaulsberry posted on Thursday, July 16, 2009

I'm working on a set of classes where a validation rule in the root considers the children as a group to make a decision. My difficulty is getting notification to the root when a property on a child changes.

The classes look like this:
User
|--UserRoleList
|--UserRole
The user role list contains a UserRole object for each possible role. The UserRole has a "IsSelectedForUser" property. The validation rule says at least one role must be selected for a user. The custom validation rule in the User root object needs to run when a the property on the child UserRole changes. I anticipate this situation (a root object looking across children for validation) will be fairly common.

I've attempted to hook the property changed event for each child, but the handler isn't firing on the client.

In the User (root object)
ValidationRules.AddRule(MustHaveRole, new RuleArgs(UserRolesProperty));

internal void UserRole_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
ValidationRules.CheckRules(UserRolesProperty);
}

///
/// Each user must have a role.
///
private static bool MustHaveRole(T target, RuleArgs e) where T : User
{
bool isValid = false;
isValid = target.UserRoles.GetSelectionCount() > 0;
if (!isValid)
{
e.Description = "A user must have a role";
}
return isValid;
}


protected void DataPortal_Create(SingleCriteria criteria)
...
LoadProperty(UserRolesProperty, UserRoleList.GetUserRoleList(roles, UserRole_PropertyChanged));
--------------------------------------------------------
In UserRoleList
--------------------------------------------------------
private void Child_Fetch(IEnumerable roleInfoList, System.ComponentModel.PropertyChangedEventHandler h)
{
RaiseListChangedEvents = false;
UserRole r;
foreach (Data.GetRoleSelectionForUserResult i in roleInfoList)
{
r = UserRole.GetUserRole(i);
r.PropertyChanged += h;
Add(r);
}
...

RockfordLhotka replied on Thursday, July 16, 2009

Have you looked at the ChildChanged event?

PropertyChanged requires that you hook every object (and hook CollectionChanged on every list) which is a lot of work and it becomes easy to cause memory leaks, etc.

ChildChanged cascades to the root, so any object in the graph will cause a ChildChanged on the root.

esaulsberry replied on Thursday, July 16, 2009

Missed that one, but I'm not getting the handler to fire on the client.

I've added to the root object
void User_ChildChanged(object sender, Csla.Core.ChildChangedEventArgs e)
{
ValidationRules.CheckRules(UserRolesProperty);
ValidationRules.CheckRules(SitesProperty);
}

and in the DataPortal_Fetch and DataPortal_Create
this.ChildChanged += new EventHandler(User_ChildChanged);

The handler isn't called on the client side.

RockfordLhotka replied on Friday, July 17, 2009

Remember that DataPortal_XYZ methods run on the server. So your event hookup occurs on the server.

Event hookups don't serialize over the wire, so it is necessary to do the event hookup on the client too (or only - the server hookup may not be terribly valuable). To do this, you'll want to override OnDeserialized() and do the hookup there.

sergeyb replied on Thursday, July 16, 2009

I personally like to have explicit validation in this case instead of relying on events. I usually have a attach a rule that checks for number of checked to a property in the parent, such as User name or something. The reason I do that is to that you can bind PropertyStatus to it and have the error message bleed into UI. Then I have an internal method (like ValidateCount) in the parent that just runs validation rules. I call this method from the list (removeitem and add new core) as well as from a rule attached to IsSelectedForUser property. The rule will return true and call this.Parent.ParentUser.ValidateCount. This way I do not rely on events.
Sergey Barskiy
Principal Consultant
office: 678.405.0687 | mobile: 404.388.1899

Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

-----Original Message-----
From: esaulsberry [mailto:cslanet@lhotka.net]
Sent: Thursday, July 16, 2009 11:10 AM
To: Sergey Barskiy
Subject: [CSLA .NET] Silverlight cross-child validation & property change notification

I'm working on a set of classes where a validation rule in the root considers the children as a group to make a decision. My difficulty is getting notification to the root when a property on a child changes.

The classes look like this:
User
|--UserRoleList
|--UserRole
The user role list contains a UserRole object for each possible role. The UserRole has a "IsSelectedForUser" property. The validation rule says at least one role must be selected for a user. The custom validation rule in the User root object needs to run when a the property on the child UserRole changes. I anticipate this situation (a root object looking across children for validation) will be fairly common.

I've attempted to hook the property changed event for each child, but the handler isn't firing on the client.

In the User (root object)
ValidationRules.AddRule(MustHaveRole, new RuleArgs(UserRolesProperty));

internal void UserRole_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
ValidationRules.CheckRules(UserRolesProperty);
}

///
/// Each user must have a role.
///
private static bool MustHaveRole(T target, RuleArgs e) where T : User
{
bool isValid = false;
isValid = target.UserRoles.GetSelectionCount() > 0;
if (!isValid)
{
e.Description = "A user must have a role";
}
return isValid;
}


protected void DataPortal_Create(SingleCriteria criteria)
...
LoadProperty(UserRolesProperty, UserRoleList.GetUserRoleList(roles, UserRole_PropertyChanged));
--------------------------------------------------------
In UserRoleList
--------------------------------------------------------
private void Child_Fetch(IEnumerable roleInfoList, System.ComponentModel.PropertyChangedEventHandler h)
{
RaiseListChangedEvents = false;
UserRole r;
foreach (Data.GetRoleSelectionForUserResult i in roleInfoList)
{
r = UserRole.GetUserRole(i);
r.PropertyChanged += h;
Add(r);
}
...

Michael replied on Wednesday, November 18, 2009

Using WinForms and CSLA 3.7, I'm explicitly calling the parent's ValidateMyProperty() from a child BLB, which calls ValidationRules.CheckRules(MyProperty) but I've found that the error provider does not refresh unless I call OnPropertyChanged(MyProperty.Name). Am I missing something?

Thanks in advance
Michael

ajj3085 replied on Thursday, November 19, 2009

No, you're not.  WinForms databinding doesn't look at IErrorInfo unless a PropertyChanged event for the bound property is raised.

Copyright (c) Marimer LLC