Scuzzlebutt
Scuzzlebutt

Reputation: 505

How to share a binding value between multiple views?

I am new to WPF and MVVM (coming in from WinForms and Events), so please bear with me!

I am trying to figure out how to use the same INotifyPropertyChanged value binding between multiple views. I am using MVVM Light. I have ViewModels that inherit from ViewModelBase backing my Views (with no code behind). I'm not sure how to explain the issue, but I think an example will make it clear what I'm after.

I have a main window. It has a standard TabControl. On the Login TabItem I have a custom login control. Below the TabControl, I have a custom status bar control. The desired behavior is that when the user logs in, the status bar is updated with their login status and name, and the other TabItems on the main window become enabled (they should be disabled when not logged in).

So to summarize I have:

Here is what my StatusBarViewModel looks like:

public class StatusBarViewModel : ViewModelBase, IStatusBarViewModel
{
    private bool _isLoggedIn;
    public bool IsLoggedIn
    {
        get { return _isLoggedIn; }
        set { Set(ref _isLoggedIn, value); RaisePropertyChanged(); }
    }
    // other properties follow
}

I inject (using Ninject) the (singleton) concrete instance of IStatusBarViewModel into the LoginViewModel via constructor injection:

public class LoginViewModel : ViewModelBase
{
    private IStatusBarViewModel _statusBar;

    public LoginViewModel(IStatusBarViewModel statusBar)
    {
        _statusBar = statusBar;
    }
}

And I do the same for the MainWindowViewModel:

public class MainWindowViewModel : ViewModelBase
{
    private IStatusBarViewModel _statusBar;

    public bool IsLoggedIn => _statusBar.IsLoggedIn;

    public MainWindowViewModel(IStatusBarViewModel statusBar)
    {
        _statusBar = statusBar;
    }
}

Note: I think this is where my problem is... Not sure if MVVM Light interprets this as a bindable property and applies the proper change notifications. If I add a setter (which I don't need here), that won't work because A property or indexer may not be passed as an out or ref parameter. So I'm unclear on what is going on when I do this.

Back on track here, so when the login is successful, I am able to update the IsLoggedIn property from the LoginViewModel like so:

_statusBar.IsLoggedIn = true;

I set up the binding in my MainWindow XAML like so:

<TabItem Header="Event" IsEnabled="{Binding IsLoggedIn}">
    <views:Events/>
</TabItem>

The binding works correctly when the view is first loaded, but subsequent changes to the property don't trigger a change in IsEnabled. The StatusBar (view) however does update accordingly.

I had tossed around the idea of injecting references to both the StatusBarViewModel and the MainWindowViewModel in to my LoginViewModel (and then having to set two properties after login), but that made me think that I'm not approaching this correctly because I'm creating dependencies.

So basically the question is:

Upvotes: 1

Views: 505

Answers (1)

RogerN
RogerN

Reputation: 3821

Your guess is correct. The problem is here:

public bool IsLoggedIn => _statusBar.IsLoggedIn;

... because it's not going to generate the change notification. What you could do is just expose the IStatusBarViewModel via a public property and then bind to its own IsLoggedIn property directly.

In the ViewModel:

public class MainWindowViewModel : ViewModelBase
{
    private IStatusBarViewModel _statusBar;
    public IStatusBarViewModel StatusBar => _statusBar;
    public MainWindowViewModel(IStatusBarViewModel statusBar)
    {
        _statusBar = statusBar;
    }
}

And in the View:

<TabItem Header="Event" IsEnabled="{Binding StatusBar.IsLoggedIn}">
    <views:Events/>
</TabItem>

Upvotes: 2

Related Questions