So I have a WPF app utilizing the MVVM pattern which uses the MVVM Light library. For INotifyPropertyChanged I am using the Fody.Weavers library which is working well so far.
I have a MainWindow.xaml which has a ContentControl, its Content property is bound to a property on its view model for navigation. This works well also.
MainWindow.xaml:
<ContentControl Content="{Binding SelectedViewModel}"></ContentControl>
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
// Note that Fody library handles INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public object SelectedViewModel { get; set; }
public MainViewModel()
{
SelectedViewModel = new HomeViewModel();
}
public RelayCommand<PasswordBox> AdminLoginCommand => new RelayCommand<PasswordBox>(AdminLogin);
private void AdminLogin(PasswordBox passwordBox)
{
// Login Logic...
// Does not work
SelectedViewModel = new HomeViewModel();
// Does not work either
if (SelectedViewModel is HomeViewModel)
{
((HomeViewModel)SelectedViewModel).CheckAccess();
}
// Does not work either
if (SelectedViewModel is HomeViewModel)
{
((HomeViewModel)SelectedViewModel).CanAccessTestButton = true;
}
}
}
However when I call the CheckAccess method on the SelectedViewModel, directly change the CanAccessTestButton property from MainViewModel, or set SelectedViewModel with a new HomeViewModel from MainViewModels AdminLogin method, they get updated as I see when I step through the code, but the binding does not update the UI. I have tried these methods independently.
I think Home.xaml is not picking up on the property when changed from the parent view model. When the constructor of HomeViewModel is initialized on first load it binds correctly to whatever CanAccessTestButton is set to, any other calls from MainViewModel do not seem to update the view.
Funnily enough, when I try to change the property from within the HomeViewModel using a RelayCommand bound to a another button in Home.xaml, it works fine.
How can I get it to work from the parent?
Home.xaml:
<Button Content="Test" IsEnabled="{Binding CanAccessTestButton}"/>
HomeViewModel.cs:
public class HomeViewModel : ViewModelBase
{
// Note that Fody library handles INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public bool CanAccessTestButton { get; set; }
public HomeViewModel()
{
OtherButtonCommand = new RelayCommand(OtherButtonClick);
CheckAccess();
}
public RelayCommand OtherButtonCommand { get; set; }
private void OtherButtonClick()
{
// WORKS!!!
CheckAccess()
}
public void CheckAccess()
{
CanAccessTestButton = AppContext.Instance.LoggedInUserHasAccess();
}
}
Edit: MVVM Light has a ViewModelLocator.cs class which you need to declare each ViewModel in:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<HomeViewModel>();
}
public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
public HomeViewModel Home => ServiceLocator.Current.GetInstance<HomeViewModel>();
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
Then in each View, you reference the view model you want to bind to in (markup simplified for brevity)
MainWindow.xaml:
<Window DataContext="{Binding Main, Source={StaticResource Locator}}">
Home.xaml:
<UserControl DataContext="{Binding Home, Source={StaticResource Locator}}">
On startup, HomeViewModel constructor gets called twice, first from ViewModelLocator.cs, then again from MainViewModel.cs. Maybe the ViewModelLocator has the reference to the view that I see on screen. Any ideas on how to accomplish what I wish to achieve?