简体   繁体   中英

WPF MVVM Child ViewModel properties not updating view

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?

I noticed the HomeViewModel constructor gets called once from ViewModelLocator , then again within MainViewModel when the Content binding is set

That's your problem. You are creating another instance of the view model and bind to this one.

Remove the DataContext attribute from the UserControl in Home.xaml :

<UserControl DataContext="{Binding Home, Source={StaticResource Locator}}">

You want to bind to the HomeViewModel instance that you create yourself, not the one that the view model locator creates for you. So you don't need any view model locator here.

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