我正在使用WPF创建一个应用程序,并尝试使用MVVM模式。

我有一个主窗口,其中设置了一些在整个应用程序中使用的默认控件。
在此主窗口中,我设置了一个ContentControl,以显示不同的 subview ,具体取决于所按下的导航按钮。

在大多数情况下,这可以正常工作。实际的导航功能正常运行,因此我不太在乎该代码。显示主窗口,当我按一个按钮时,将显示所需的ContentControl以及来自数据库的正确信息。

我遇到的问题是试图获取要显示的默认ContentControl。第一次加载MainWindow时,我希望在不按下按钮的情况下显示“Master” ContentControl。

这是我现在所拥有的:

MainWindowVM:

public class MainWindowVM : ViewModelBase
{
    public MainWindowVM()
    {
        NavCommand = new NavigationCommand<string>(OnNav);
    }

    private MasterViewVM masterViewVM = new MasterViewVM();
    private Room1ViewVM room1ViewVM = new Room1ViewVM();
    private Room2ViewVM room2ViewVM = new Room2ViewVM();

    private ObservableObject currentViewModel;
    public ObservableObject CurrentViewModel
    {
        get { return currentViewModel; }
        set { SetProperty(ref currentViewModel, value); }
    }

    public NavigationCommand<string> NavCommand { get; private set; }

    private void OnNav(string destination)
    {
        switch (destination)
        {
            case "master":
                CurrentViewModel = masterViewVM;
                break;
            case "room1":
                CurrentViewModel = room1ViewVM;
                break;
            case "room2":
                CurrentViewModel = room2ViewVM;
                break;
        }
}

主窗口XAML:
<Window.Resources>
    <DataTemplate DataType="{x:Type viewModels:MasterViewVM}">
        <views:MasterView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewModels:Room1ViewVM}" >
        <views:Room1View />
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewModels:Room2ViewVM}" >
        <views:Room2View />
    </DataTemplate>
</Window.Resources>

<Grid>
    <StackPanel Name="stackPage" Orientation="Vertical" Grid.Column="1">

        <StackPanel Name="stackNavButtons"
                Orientation="Horizontal"
                Grid.Column="1" Grid.ColumnSpan="4"
                Grid.Row="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                >
            <Button Name="btnMaster"
            Content="Master"
            Height="50"
            Width="75"
            Command="{Binding NavCommand}"
            CommandParameter="master"
            />
            <Button Name="btnRoom1"
            Content="Room 1"
            Height="50"
            Width="75"
            Command="{Binding NavCommand}"
            CommandParameter="room1"
            />
            <Button Name="btnRoom2"
            Content="Room 2"
            Height="50"
            Width="75"
            Command="{Binding NavCommand}"
            CommandParameter="room2"
            />
        </StackPanel>

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

    </StackPanel>
</Grid>

我尝试将这些行(分别)添加到MainWindowVM构造函数中。
  • CurrentViewModel = masterViewVM;
  • CurrentViewModel = new MasterViewVM();
  • OnNav(“master”);

  • 我尝试过的各种方法最终导致System.NullReferenceException指向ObservableObject(由ViewModelBase引用)帮助程序类。具体来说,它指向:



    并指出“对象引用未设置为对象的实例”。

    我怀疑问题是当我加载MainWindow时,尚未填充Master的数据。我认为这就是给我空引用的原因。

    这是打开MainWindow的代码(通过主页上的按钮):
        public ICommand OpenMainCommand
        {
            get { return new RelayCommand(p => OpenMainWindow()); }
        }
    
        private void OpenMainWindow()
        {
            MainWindow mainWin = new MainWindow();
            MainWindowVM mainVM = new MainWindowVM();
            mainWin.DataContext = mainVM;
            mainWin.Show();
        }
    

    我尝试将以下内容添加到上述方法中,以尝试在打开主窗口(并尝试显示主窗口)之前加载数据。
            MasterView masterView = new MasterView();
            MasterViewVM masterVM = new MasterViewVM();
            masterView.DataContext = masterVM;
    

    我尝试的最后一件事是完全按照Rachel Lim博客中有关Navigation in MVVM的指导完全重建程序。
    我不会在这里包括修改后的代码,因为我最终得到了相同的结果。

    我能够使Rachel的示例应用程序正常工作。但是,它没有与数据库的连接。这进一步表明,我的问题在于何时和/或如何将数据加载到“主” View 中。

    我现在要做的只是在显示MainWindow时让Master显示为初始ContentControl。

    编辑:根据ibebbs的建议更改“currentViewModel”的大小写(从大写到小写)解决了该问题。
    现在,构造函数显示为:
        public MainWindowVM()
        {
            NavCommand = new NavigationCommand<string>(OnNav);
            currentViewModel = masterViewVM;
        }
    

    由于这也是有问题的,因此这是我的ObservableObject的代码:
    public class ObservableObject : INotifyPropertyChanged
    {
    
        protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName] string propertyName = null)
        {
            if (object.Equals(member, val)) return;
    
            member = val;
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    我将最后一部分(也根据ibebbs)修改为:
    (注意:我的问题无需进行修改即可解决。)
        protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged == null)
                return;
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    

    我不确定这是否会更好地处理null,但似乎可以。

    最佳答案

    恐怕问题中没有足够的信息可以肯定地回答(即您是否正在使用Microsoft.VisualStudio.PlatformUI库中的ObservableObject?ViewModelBase定义在哪里?),但我猜想是ViewModelBase不会检查在调用PropertyChanged事件之前为null,该事件将由SetProperty(ref currentViewModel, value)调用调用。

    要解决此问题,我将:

    a)确保ViewModelBase在调用PropertyChanged事件之前检查是否为null,该事件通常如下执行(取自ObservableObject代码):

    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
      PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
      if (propertyChanged == null)
        return;
      propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    

    b)将初始 View 模型分配给不是属性的字段,这不会导致触发属性更改事件。因此,构造函数应为:
    public MainWindowVM()
    {
        NavCommand = new NavigationCommand<string>(OnNav);
        currentViewModel = masterViewVM; // Note lower case 'c' on currentViewModel
    }
    

    希望能帮助到你。

    关于c# - MVVM无法在主页上显示默认用户控件,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39338096/

    10-13 07:06