我正在使用WPF创建应用并尝试使用MVVM模式。
我有一个主窗口设置了一些在应用程序中使用的默认控件 在这个主窗口中,我有一个ContentControl设置来显示不同的子视图,具体取决于按下的导航按钮。
在大多数情况下,这很好用。实际的导航功能正常,所以我不太关心该代码。将显示主窗口,当我按下按钮时,将显示所需的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引用)帮助器类。具体来说,它指向:
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
和状态&#34;对象引用未设置为对象的实例&#34;。
我怀疑问题是当我加载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的示例应用程序正常运行。但是,它没有连接到数据库。这进一步表明我的问题在于何时和/或如何将我的数据加载到主视图中。
我现在要做的就是让主人在显示MainWindow时显示为初始ContentControl。
编辑:更改&#34; currentViewModel&#34; (根据ibebbs的建议,大写到小写)解决了这个问题。 现在构造函数读取:
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));
}
我不确定这是否会更好地处理空值,但似乎可能会这样。
答案 0 :(得分:0)
我担心问题中没有足够的信息可以肯定地回答(例如,您是否正在使用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)将初始视图模型分配给字段,而不是将不会导致触发属性更改事件的属性。因此构造函数应该是:
public MainWindowVM()
{
NavCommand = new NavigationCommand<string>(OnNav);
currentViewModel = masterViewVM; // Note lower case 'c' on currentViewModel
}
希望它有所帮助。