简体   繁体   English

WPF MVVM子ViewModel属性无法更新视图

[英]WPF MVVM Child ViewModel properties not updating view

So I have a WPF app utilizing the MVVM pattern which uses the MVVM Light library. 因此,我有一个利用MVVM模式(使用MVVM Light库)的WPF应用程序。 For INotifyPropertyChanged I am using the Fody.Weavers library which is working well so far. 对于INotifyPropertyChanged,我正在使用Fody.Weavers库,该库到目前为止运行良好。

I have a MainWindow.xaml which has a ContentControl, its Content property is bound to a property on its view model for navigation. 我有一个具有ContentControl的MainWindow.xaml,其Content属性绑定到其视图模型上的属性以进行导航。 This works well also. 这也很好。

MainWindow.xaml: MainWindow.xaml:

<ContentControl Content="{Binding SelectedViewModel}"></ContentControl>

MainViewModel.cs 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. 但是,当我在SelectedViewModel上调用CheckAccess方法,直接从MainViewModel更改CanAccessTestButton属性,或者从MainViewModels AdminLogin方法使用新的HomeViewModel设置SelectedViewModel时,它们将按照我在逐步执行代码时看到的方式进行更新,但绑定不会更新用户界面。 I have tried these methods independently. 我已经独立尝试了这些方法。

I think Home.xaml is not picking up on the property when changed from the parent view model. 我认为从父视图模型更改后,Home.xaml不会在该属性上启动。 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. 当HomeViewModel的构造函数在第一次加载时初始化时,它可以正确绑定到设置为CanAccessTestButton的任何对象,MainViewModel的任何其他调用似乎都不会更新视图。

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. 有趣的是,当我尝试使用绑定到Home.xaml中另一个按钮的RelayCommand从HomeViewModel内部更改属性时,它可以正常工作。

How can I get it to work from the parent? 我怎样才能从父母那里得到帮助?

Home.xaml: Home.xaml:

<Button Content="Test" IsEnabled="{Binding CanAccessTestButton}"/>

HomeViewModel.cs: 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: 编辑: MVVM Light有一个ViewModelLocator.cs类,您需要在以下类中声明每个ViewModel:

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: MainWindow.xaml:

<Window DataContext="{Binding Main, Source={StaticResource Locator}}">

Home.xaml: 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. 启动时,两次调用HomeViewModel构造函数,首先是从ViewModelLocator.cs,然后是从MainViewModel.cs。 Maybe the ViewModelLocator has the reference to the view that I see on screen. 也许ViewModelLocator引用了我在屏幕上看到的视图。 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 我注意到HomeViewModel构造函数从ViewModelLocator调用一次,然后在设置Content绑定时在MainViewModel再次调用

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 : Home.xamlUserControl中删除DataContext属性:

<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. 您想绑定到自己创建的HomeViewModel实例,而不是视图模型定位器为您创建的实例。 So you don't need any view model locator here. 因此,您在这里不需要任何视图模型定位器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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