New Posts New Posts RSS Feed: Question on Asynchronous Query
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Question on Asynchronous Query

 Post Reply Post Reply
Author
BillG View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
Post Options Post Options   Quote BillG Quote  Post ReplyReply Direct Link To This Post Topic: Question on Asynchronous Query
    Posted: 02-Nov-2010 at 12:31pm
When the user clicks on a row in the MemberListView screen, I get the selectedItem and pass it to my model. In my model for the detailscreen, I Fetch the individual member selected and .Include his/her job histories, dues records. In the callback completed method I loop through the job histories and add them to a ObservableCollection Jobs. This displays them in the grid on the member form. That works fine. What doesn't work is that in my Domain Model class for JobHistory I have the following to display the Employer Name
 
public string EmployerName
{
    get{
           return EmplSite.Employer.EmployerName;
    }
}
 
public string JobName
{
    get{
           return EmplSite.JobName;
    }
}
 
These are not displaying in the datagrid which uses the ItemsSource="{Binding Jobs}" which is the collection of JobHistory records for that member. How do I get the above to appear. I do the following for the binding    Binding="{Binding EmployerName, Mode = OneWay}" and Binding="{Binding JobName, Mode = OneWay}"
 
Back to Top
BillG View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
Post Options Post Options   Quote BillG Quote  Post ReplyReply Direct Link To This Post Posted: 02-Nov-2010 at 1:05pm
Ok if I get out of that record and then go back in, then the names appear. But not on the first time and not even if I sit and wait on the open record.
Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 02-Nov-2010 at 7:58pm
Bindings work by adding an event handler to the INotifyPropertyChanged event of any object they want to monitor.
In your case this event may be firing against the Employer object, but your EmployerName property is on a different object, so you will have to raise the NotifyPropertyChanged for the JobHistory object yourself.
I often do this by intercepting the Setter of the Employer object, add an event handler programatically that checks for parameter "EmployerName" on the nested object being changed, and in turn raise the change event for "EmployerName" in the JobHistory object.
It works the second time because the object is already updated, so your interface is not reacting to the RaisePropertyChanged, but to the fact it is reading values for its initial bind.
This is always an issue when you have properties reading values from nested objects, and it becomes your responsibility to relay the change events, as it's clearly impractical for the system to bubble property change events up entire trees, and .NET has no awareness of the dependency a property definition has on any code implementation it contains.

These issues have been present in every aspect of .NET's property event handling, but has become far more high-profile as lazy loading with UI level binding in Silverlight and WPF has become more common.  In the past we would often see cases of people 'rebinding' or 'refreshing' their screens to reflect changes to these nested objects, rather than addressing the real issue that you have found.

Cheers,
 Jason
Back to Top
BillG View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
Post Options Post Options   Quote BillG Quote  Post ReplyReply Direct Link To This Post Posted: 03-Nov-2010 at 7:43am
I am officially stumped at this point on where to raise the notifiedPropertyChanged on the JobHistory class in the MemberViewModel. In my MemberViewModel class I have the following:
 
public Member currentMember                              //represents the member currently being worked on in the view
public ObservableCollection<JobHistory> jobs {get; set;}   //represents all the jobs that the member has had displayed in a grid
 
Here is my retrieval of the member and their jobs.
 

public void GetMember()

{

string ssn = memberToBeReteived.SocSecNo;

var query = from Member in Mgr.Members.Include("JobHistories")

where Member.SocSecNo == ssn

select Member;

var op = query.ExecuteAsync();

op.Completed += MemberQueryCompleted;

}

public void MemberQueryCompleted(Object sender, EntityQueriedEventArgs<Member> e)

{

var results = e.Results;

Member = results.First<Member>();

foreach (JobHistory jh in Member.JobHistories)

{

Jobs.Add(jh);

}

OnPropertyChanged("Jobs");

}
 
I also have the following in the class
 

private void Jobs_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

{

OnPropertyChanged("JobHistory");

}

but that didn't seem to work. So I can't figure out why it is failing to bind to the following propery in the JobHistory class.
 
public string EmployerName
{
      get
      {
          return this.EmplSite.Employer.EmployerName;
       }
}
 
and yet if I close the member record and open it the second time, it is displaying the employername in the grid.
 
Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 03-Nov-2010 at 3:29pm
Without  sample app it's a bit tricky, but from what I can see...

foreach (JobHistory jh in Member.JobHistories)

{

Jobs.Add(jh);

}

OnPropertyChanged("Jobs");

if Jobs is an ObservableCollection then you don't need to raise the PropertyChanged.
basically, it looks like you need to have something like this in the EmplSite setter:

this.EmplSite.Employer
.PropertyChanged
                    += delegate(object sender, PropertyChangedEventArgs o)
                                    { if (o.PropertyName == "EmployerName") RaisePropertyChanged("EmployerName"); };

I think this will check for the contents of the nested object being changed, and reflect that change in your top level object.

You have to remember that your collection doesn't change when the objects are async received from the database, as DF is simply replacing an object into an existing one, not adding or removing objects from the collection.

Also, why do you have a Jobs collection? This is the same as Member.JobHistories so you can bind straight to that collection.

In fact, you could bypass this whole problem by binding to Member.EmplSite.Employer.EmployerName in your XAML code, so unless you have a good reason for replicating the property to the top object I would do it this way.

Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down