Deep Linking of Properties

Topics: Conventions, Getting Started
Jul 21, 2012 at 9:43 PM

Hello,

I seem to be going round the houses on this one, and I am hoping that someone will be able to help me out.

I have the following Model Class, LoginCredentialModel, which ultimately inherits from PropertyChangedBase:

    /// <summary>
    /// The login credential model.
    /// </summary>
    public class LoginCredentialModel : BaseModel<LoginCredentialModel>
    {
        /// <summary>
        /// The user name.
        /// </summary>
        private string userName;

        /// <summary>
        /// The password.
        /// </summary>
        private string password;

        /// <summary>
        /// Gets or sets the user name.
        /// </summary>
        public string UserName
        {
            get
            {
                return this.userName;
            }

            set
            {
                this.userName = value;
                this.NotifyOfPropertyChange(() => this.UserName);
            }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get
            {
                return this.password;
            }

            set
            {
                this.password = value;
                this.NotifyOfPropertyChange(() => this.Password);
            }
        }
    }

Then, within my ViewModel class, I have the following property, LoginDetails, that is an instance of the above class:

        /// <summary>
        /// Gets or sets the login details.
        /// </summary>
        public LoginCredentialModel LoginDetails
        {
            get
            {
                return this.loginCredential;
            }

            set
            {
                this.loginCredential = value;
                this.NotifyOfPropertyChange(() => this.LoginDetails);
                this.NotifyOfPropertyChange(() => this.CanAuthenticateUser);
            }
        }

Within my View, I then use deep linking of the properties of the LoginDetails property to bind to the UI:

                    <toolkit:PhoneTextBox x:Name="UsernameText"
                                          LengthIndicatorVisible="False"
                                          Margin="-10,-5,-10,8"
                                          InputScope="EmailUserName"
                                          ActionIcon="/Resources/Images/clearboxicon.png"
                                          Text="{Binding LoginDetails_UserName, Mode=TwoWay}">

Now, to an extent, this seem to work really well.  If I set some breakpoints on the set method of the LoginCredentialModel class, I can see that when I change the values in the UI, the corresponding backing variables get updated.

However, when I do this, I also expect that the set method for the LoginDetails property on the ViewModel should also get called, however, this doesn't seem to be the case.  As a result, the guard clause that I have for CanAuthenticateUser is never getting actioned.

Have I missed something here, or what am I doing wrong?

I did have this working by having individual properties for UserName and Password within my ViewModel, but I wanted to try to combine these into a single object.

Thanks in advance!

Gary

Jul 21, 2012 at 9:45 PM

Hello,

One other thing I should point out, this is a Windows Phone 7 application.

Thanks!

Gary

Jul 21, 2012 at 10:12 PM

Ran into this problem a while back myself....

 

The reason your ViewModel LoginDetails is not getting update is because their was no change. You binding bind's straight through the property. Any change to the internals of LoginDetails will not actually change LoginDetails the property. To get LoginDetails the property to fire you would need to change the object in it's backing store or call NotifyPropertyChange() on it.

A nice way I found, if you are using the event aggregator is to create a NotifyParent event and have it detail the property you wish to change:

// PseudoCode

MyVM : Handles<InpcOnParent>
{
    OnEvent(InpcOnParent context)
    {
        this.OnNotifyPropertyChanged(context.PropName);
    }
}


MyModel
{
    //When you want to notify the parent
    eventAggregator.Publish(new InpcOnParent("PropName");
}

Jul 21, 2012 at 11:18 PM

Interesting...

I think I get what you mean, but I haven't really used the EventAggregator yet, so can't really see how this would piece together.

Have you got any sample code that I could take a look at?  Looks like this quite a "clean" way to do it, so would like to give it a try.

Thanks for your help!

Gary

Jul 25, 2012 at 8:08 AM
Edited Jul 25, 2012 at 8:09 AM

I don't have anything to hand unfortunately but see the sample below for what I am talking about.

 

public class FooModel
{
    IEventAggregator ea;

    public string Bar{get; set; } // Assume This is INPC.
    
    public FooModel(IEventAggregator ea)
    {
        this.ea = ea; // You need to get a reference to a singleton I do it via DI.
    }
    
    // When you call INPC changed call this as well.
    private void AggregatePropertyChangedEvent(string propName)
    {
           ea.Publish(New AggregatePropertyChanged("FooModel", "My Prop Name");
    }
}

public class FooViewModel : IHandle<AggregatePropertyChanged>
{
    IEventAggregator ea;
    
    public FooModel(IEventAggregator ea)
    {
        this.ea = ea; // You need to get a reference to a singleton I do it via DI.
        ea.Subscribe(this); // Give yourself to the ea so it can call your handle methods.
    }
    
    public void Handle(AggregatePropertyChanged apc)
    {
        if (apc.context == "FooModel")
        {
            this.RaisePropertyChanged(apc.PropName)
        }
    }
}

// Event Messages Can Be Any Object.
public class AggregatePropertyChanged
{
    public string Context{get; set; }
    
    public string PropName{get; set; }

    public AggregatePropertyChanged(string context, string propName)
    {
	this.Context = context;
	this.PropName = propName;
    }
}

Jul 28, 2012 at 2:45 PM

Thanks, I will definitely be giving this a try!

Gary