Receiving NotifyPropertyChanged events on a non-ui class

Nov 17, 2012 at 3:52 AM

I want to have a non-ui class be notified when the a property in a model is changed. The model already calls NotifyPropertyChanged when the property is changed so it seems another method in an abritrary non-ui class could be notified when this happens. How do you do this?

Thanks

Nov 17, 2012 at 4:06 AM

I wrote a blog post on the message bus, but that was before I refactored it to be leak-proof. So The receiving object needs to implement INotifyable.  ViewModels implement the proxy pattern so that failing to call UnRegister will not cause a memory leak. However, I still recommend that you explicitly call UnRegister for cleaner code and more consistent behavior. Tyically the receiver would call Register on MessageBus.Default in its ctor, and UnRegister in a Dispose method. Download this sample to see it in action.

Enjoy,
Tony

Nov 19, 2012 at 10:26 PM

Tony,

   Thank you for the example. That clears a lot up about the Message Bus.

I am still having trouble getting a trigger from NotifyPropertyChanged

I have a model with this property:

      private string gain;

        public string Gain

        {

            get { return gain; }

            set

            {

                gain = value;

                NotifyPropertyChanged(m => m.Gain);

            }

        }

The receiver:

class AnalogCANData : CANDataBaseClass<AnalogDataMessage>, INotifyable

    {

 

        public AnalogCANData()

        {

            MessageBus.Default.Register("Gain", this);

        }

 

        public void Notify(string token, object sender, NotificationEventArgs e)

        {

        }

The Notify method is not triggered when the model's Gain property is set.

What am I missing?

Thanks

 

Nov 19, 2012 at 11:14 PM

You need to Register to receive the messages in your constructor of your non-UI class.

public AnalogCANData()

        {
            RegisterToReceiveMessages("Gain", SomeMethod);
        }

void SomeMethod()

{

    //Do the work here

}

And in your setter you send the message

private string gain;

        public string Gain

        {

            get { return gain; }

            set

            {

                gain = value;

                NotifyPropertyChanged(m => m.Gain);

                SendMessage("Gain", new NotificationEventArgs();
            }

        }

You can use a static property of MessageTokens for TypeSafe. Else just pass the string as above.

Hope this helps.

Nov 20, 2012 at 12:32 AM

Thank you for the post.

I apologize for not understanding and I really appreciate all the help. 

RegisterToReceiveMessages is a member of ViewModelBase? My class does not derive from ViewModelBase. Is there another way I can use RegisterToReceiveMessages?

 

Also, I really thought I was following Tony's example and that it would work with NotifyPropertyChanged.

 

Thanks

Nov 20, 2012 at 12:46 AM
Edited Nov 20, 2012 at 1:08 AM

I assumed your non-UI class to be a viewmodel. Can you not derive this class from a viewmodelbase? Really need to understand your scenario before I suggest anything else.

MessageBus is a system of communicating between viewmodels. Please read through the documentation if in doubt.

The other thing could be to hold a reference of the model in your "non-UI" class constructor and subscribe to its PropertyChanged event. Check if the changed property is "Gain" and if it is then run a method.

var myModel = new Model();

myModel.PropertyChanged +=  SomeMethod;


private void SomeMethod(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Gain")
    {
         //Do something
    }

}
Nov 20, 2012 at 2:11 AM

Thanks for staying with me on this!

The code in your post above works.

Also, Given Tony's example that he posted above, I know I can use the MessageBus as well. I think the advantage of the MessageBus is looser coupling, in that my non-ui class (which is not a viewmodel), does not need a reference to the model.

Basically I have data coming into the app via CAN (Controller Area Network). That data gets put into the mode which updates the UI. That is nice and easy with Simple MVVM :-)

However, there is configuration data I need to push back down to the CAN objects. That is why I was trying to have the model signal the CAN objects that configuration data had changed.

I thought that since the model classes are calling NotifyPropertyChanged, I could intercept that. Hence my post. I still do not know how to do that. However, since something is putting that configuration it into the model, it could also use the MessageBus to notify objects further down towards the CAN interface. It just is an extra step.

Thanks again for the help!

Nov 27, 2012 at 11:01 PM
tonysneed wrote:

I wrote a blog post on the message bus, but that was before I refactored it to be leak-proof. So The receiving object needs to implement INotifyable.  ViewModels implement the proxy pattern so that failing to call UnRegister will not cause a memory leak. However, I still recommend that you explicitly call UnRegister for cleaner code and more consistent behavior. Tyically the receiver would call Register on MessageBus.Default in its ctor, and UnRegister in a Dispose method. Download this sample to see it in action.

Enjoy,
Tony

 

The example code worked great for me. I have a couple of questions still...

When not using a ViewModel with the MessageBus and you want to send a NotificationEventArgs with the NotificationEventArgs<TOutGoing> form, how do you handle the notify in the receiver? INotifyable seems to only work with the default NotificationEventArgs form. I ended up casting the NotificationEventArgs parameter to make it work but this doesn't seem right. Or is it?

Also, you only get one notification in the receiver, even if you registered multiple messages. So would you then look at the token to figure out which one is actually being triggered?

Thanks

Dec 14, 2012 at 2:22 PM

There is a version of NotificationEventArgs that takes two type arguments:

public class NotificationEventArgs<TOutgoing, TIncoming> 
    : NotificationEventArgs<TOutgoing>
{
    // Completion callback
    public Action<TIncoming> Completed { get; protected set; }
}

The Completed property is used to convey information back in the other direction.  There is some info in this blog post on the subject.  However, you can also open up the MessageBus sample app that is installed with the toolkit to see how this works with the MessageBus.