简体   繁体   中英

INotifyPropertyChanged in singleton and binding to the WPF element

I'm bit experimenting with MVVM once again and I'm currently experiencing two issues with my code.
First let me explain the code structure:

I have a class like this (simplified of course):

public abstract class NavigationServiceBase : NotifyPropertyChangedBase, INavigationService
{
   private IView _currentView;
   public IView CurrentView
   {
       get { return _currentView; }
       protected set
       {
           _currentView = value;
           OnPropertyChanged("CurrentView");
       }
   }

   public virtual void DoSomethingFancy()
   { 
       CurrentView = ...; // here I expect it to fire OnPropertyChanged and notify my View
   }
}

And the singleton inheriting from this base class:

public class NavigationService : NavigationServiceBase
{
    private static readonly INavigationService _instance = new NavigationService();
    public static INavigationService Instance { get { return _instance; } }

    ...
}

ViewModel:

private INavigationService _navigationService;

public IView CurrentView { get { return _navigationService.CurrentView; } }
public ICommand NavigateCommand { get; private set; }

public MainWindowViewModel()
{
    _navigationService = NavigationService.Instance;
    NavigateCommand = new RelayCommand(param => Navigate(param));
}

private void Navigate(object requestedPage)
{
    _navigationService.Navigate((string)requestedPage);
    //OnPropertyChanged("CurrentView"); // this works , but...
}

Now the issues:
1.) I'm editing the XAML in Visual Studio 2012 Express. It seems it works, but I get a warning with this message: Unable to load one or more of the requested types. Retrieve the LoaderExceptions for more information. Unable to load one or more of the requested types. Retrieve the LoaderExceptions for more information. It shows in the part when I declare the Resources for the binding the ViewModel. What does it mean? If I get rid of the singleton, the message goes away. Project compiles and runs just fine either way.

2.) it seems my OnPropertyChanged("CurrentView") isn't firing or something, because I have to manually call this method from within the ViewModel itself. If I try it from the base class or from the inheriting singleton, it doesn't work. (binding just ignores the new values). If I do this manually while handing the command, it works. Yes, it's just an extra line of code, but I wonder, is there a way to make it work without "cheating" like this?

The problem is that you're binding to the property in your ViewModel:

public IView CurrentView { get { return _navigationService.CurrentView; } }

The NatigationService is raising PropertyChanged , but this happens in _navigationService , not in the ViewModel itself, so the View never sees the event.

There are two common options -

You can listen for the PropertyChanged event on your navigation service, and handle raising it locally if needed:

_navigationService = NavigationService.Instance;
_navigationService.PropertyChanged += (o,e) => 
{
   // When navigation raises prop changed for this property, raise it too
   if (e.PropertyName == "CurrentView")
     OnPropertyChanged("CurrentView");
}; 
NavigateCommand = new RelayCommand(param => Navigate(param));

The other alternative is to expose the service, and bind directly to it:

public INavigationService Navigation { get { return _navigationService; } }

Then, in your View, bind to the content inside of the service instead of a local property:

<ContentPresenter Content="{Binding Navigation.CurrentView}" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM