我正在使用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构造函数中。
我尝试过的各种方法最终导致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/