WPF MVVM子ViewModel属性不更新视图

时间:2017-05-31 09:55:11

标签: c# .net wpf xaml mvvm

所以我有一个使用MVVM模式的WPF应用程序,它使用MVVM Light库。 对于INotifyPropertyChanged我正在使用到目前为止运作良好的Fody.Weavers库。

我有一个MainWindow.xaml,它有一个ContentControl,其Content属性绑定到其视图模型上的属性以进行导航。这也很有效。

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;
        }
    }
}

然而,当我在SelectedViewModel上调用CheckAccess方法时,直接从MainViewModel更改CanAccessTestButton属性,或者使用MainViewModels AdminLogin方法中的新HomeViewModel设置SelectedViewModel,它们会在我单步执行代码时看到更新,但是绑定不更新UI。我独立尝试过这些方法。

我认为从父视图模型更改时,Home.xaml没有获取属性。当HomeViewModel的构造函数在第一次加载时初始化时,它正确绑定到CanAccessTestButton所设置的任何内容,来自MainViewModel的任何其他调用似乎都不会更新视图。

有趣的是,当我尝试使用绑定到Home.xaml中的另一个按钮的RelayCommand从HomeViewModel中更改属性时,它可以正常工作。

如何从父母那里得到它?

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();
        }
    }

修改 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
        }
    }

然后在每个视图中,引用要绑定到的视图模型(简化标记为简洁)

MainWindow.xaml:

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

Home.xaml:

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

启动时,HomeViewModel构造函数将首先从ViewModelLocator.cs调用两次,然后再从MainViewModel.cs调用。也许ViewModelLocator具有对我在屏幕上看到的视图的引用。关于如何实现我希望实现的目标的任何想法?

1 个答案:

答案 0 :(得分:4)

  

我注意到HomeViewModel构造函数从ViewModelLocator调用一次,然后在设置MainViewModel绑定时再次在Content内调用

那是你的问题。您正在创建视图模型的另一个实例并绑定到该实例。

DataContext中的UserControl删除Home.xaml属性:

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

您希望绑定到您自己创建的HomeViewModel实例,而不是视图模型定位器为您创建的实例。所以这里你不需要任何视图模型定位器。