Question

There are some similar questions on SO, but they weren't quiet the same, so I'm posting this instead. I'm new to MVVM, so I'm trying to figure out how I can create a class that can hold properties that can be shared among views. So, if I set a property in one view, all the other views would get notified if its changed and would adjust their properties accordingly.

What I have now is rather very crude and is definitely not something I want to use. This is my common class that will hold all the properties:

public static class Common
{
    private static string _title;
    public static string Title
    {
        get { return _title; }
        set
        {
            if (_title == value)
            {
                return;
            }

            _title = value;
            OnPropertyChanged("Title");
        }
    }

    public static void Load()
    {
        // set properties here
    }

    public static event PropertyChangedEventHandler PropertyChanged;

    private static void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(typeof(SettingsWorker), new PropertyChangedEventArgs(name));
        }
    }
}

...and I have to subscribe to it from each ViewModel:

Common.PropertyChanged += Common_PropertyChanged;

private void Common_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    Title = Common.Title;
}

But this is where the breakdown happens. I can get the property name from the PropertyChangedEventArgs, but I've no idea how to get the value. Therefore, I'm forced to update all the properties, and that can get nasty to maintain. The code is becoming a mess.

I'm basically trying to get properties that ViewModels can share. How can I accomplish this?

Was it helpful?

Solution

It looks like you just have some global data you want to show in multiple places. The most straightforward way to do this is to just make it like and normal ViewModel class and then make it available to each of your other ViewModels and expose it from them to bind to directly (rather than copying the property into each of them). You can do this using IOC, or make it available statically, more similar to how you have it now.

If you go the static direction, the key change you need to make is to use a singleton rather than a static class in order to allow property change notification to work. Bindings work with INPC on instances but not static classes. The Common class would look more like this:

public class Common : INotifyPropertyChanged
{
    private static Common _instance = null;

    protected Common()
    {
    }

    public static Common GetInstance()
    {
        if (_instance == null)
            _instance = new Common();

        return _instance;
    }

    private string _title;
    public string Title
    {
        get { return _title; }
        set
        {
            if (_title == value)
                return;
            _title = value;
            OnPropertyChanged("Title");
        }
    }

    public void Load()
    {
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventArgs ea = new PropertyChangedEventArgs(propertyName);
        if (PropertyChanged != null)
            PropertyChanged(this, ea);
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

There are a lot of different ways you can then use this. Here's one of the more direct ones:

public class SomeViewModel : ViewModelBase
{
    public Common CommonData { get; private set; }

    public SomeViewModel()
    {
        CommonData = Common.GetInstance();
    }
}

Then in XAML you can bind to the properties from the common class and get change notification, even across the different VM usages.

<TextBox Text="{Binding Path=CommonData.Title}"/>

There's also the option of making the singleton accessible as a property and binding to it directly from XAML using x:Static but that's a little different direction that what you were asking.

OTHER TIPS

So if you have other views that want to get notified when a property changes I would assume you have a separate viewmodel for each of those views. In that case what you would be asking for is "How can viewmodels talk to other viewmodels?" For a good learning experience, I would recommend looking into the observer pattern. If you don't like that style, then I would recommend you look into using a MVVM Framework like "SimpleMVVM, Catel, or many others" (Just need to look some up). Then once you have that framework in you project, I would create a baseviewmodel class that all your viewmodels will inherit. Once that is done you can take advantage of the frameworks messenger system. Basically it would look something like:

    public YourViewModel()
    {           
        RegisterToReceiveMessages(MessageTokens.SomeToken, OnChangeCallYourMethodToAddress); //The MessageTokens is something you generally create ur self.
    }

    #region Notifications
    void OnChangeCallYourMethodToAddress(object sender, NotificationEventArgs<SlideShowLocale> e)
    {
        SomeProperty = e.Data;
    }

Then to send a message to "YourViewModel" From another ViewModel:

    public AnotherViewModel
    {
        SendMessage(MessageTokens.SomeToken, new NotificationEventArgs(WhateverYouWantToSend));
    }

So basically, by declaring the token you want, it then finds the viewmodel that is registered with that token and listens for any messages to come in.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top