如何设置View Model View xaml的datacontext?
How to set data context of ViewModela View's xaml?
我正在尝试将视图的数据上下文设置为其 ViewModel 中包含的列表。但是当我测试当前设置时,似乎 ViewModel 和 View 之间的 data context
设置不正确。
为了调试这个问题,我在 View 的构造函数中设置了一个消息框,我收到了以下错误消息,提示数据上下文设置不正确:“对象引用不正确设置为对象的实例"
该列表还在另一个 ViewModel 中使用,这表明该列表不为空,这进一步暗示了数据上下文问题。
有谁知道在 View 和 ViewModel 之间设置数据上下文的缺陷是什么?
这是包含列表的 ViewModel:
namespace LC_Points.ViewModel
{
public class ViewSubjectGradeViewModel
{
public ViewSubjectGradeViewModel()
{
AddedSubjectGradePairs = new ObservableCollection<ScoreModel>();
}
public ObservableCollection<ScoreModel> AddedSubjectGradePairs { get; set; }
}
}
这是View和View背后的代码:
<Page x:Class="LC_Points.View.ViewSubjectGradePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:LC_Points.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vms="using:LC_Points.ViewModel"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding ViewSubjectGradeViewModelProperty1}"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="40*" />
<RowDefinition Height="20*" />
<RowDefinition Height="30*" />
<RowDefinition Height="30*" />
<RowDefinition Height="20*" />
<RowDefinition Height="20*" />
</Grid.RowDefinitions>
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Margin="0,12,0,0"
Style="{ThemeResource TitleTextBlockStyle}"
Text="LC POINTS" />
<TextBlock Margin="0,-6.5,0,26.5"
CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"
Foreground="DarkGreen"
Style="{ThemeResource HeaderTextBlockStyle}"
Text="View Grades" />
</StackPanel>
<!-- TODO: Content should be placed within the following grid -->
<Grid x:Name="ContentRoot"
Grid.Row="1"
Margin="19,9.5,19,0">
<ListBox Height="400"
Margin="0,0,0,-329"
VerticalAlignment="Top"
ItemsSource="{Binding AddedSubjectGradePairs}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Subject}" /><Run Text=" - " /><Run Text="{Binding Points}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Page>
查看后面的代码:
namespace LC_Points.View
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ViewSubjectGradePage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public ViewSubjectGradePage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
var messageDialog = new MessageDialog(DataContext.GetType().ToString());
messageDialog.ShowAsync();
}
/// <summary>
/// Gets the <see cref="NavigationHelper"/> associated with this <see cref="Page"/>.
/// </summary>
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
/// <summary>
/// Gets the view model for this <see cref="Page"/>.
/// This can be changed to a strongly typed view model.
/// </summary>
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
}
private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
}
#region NavigationHelper registration
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedFrom(e);
}
#endregion
}
}
我认为这是由于您分配 DataContext
的方式所致。在您的 XAML 中,您将 DataContext 绑定到 属性。我没有在您的代码中的任何地方看到您实际将数据上下文分配给 defaultViewModel
或其中的属性。
尝试将您的构造函数更新为以下内容。
public ViewSubjectGradePage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
this.DataContext = this.DefaultViewModel;
}
如果您想将其分配给 DefaultViewModel
中的 属性,您也可以在那里进行。然后删除 XAML.
中的 DataContext 分配
您可能想要从您的视图代码中删除模板提供的您未使用的任何生成代码。这可能会导致混淆,因为模板希望您使用本地 ObservableCollection 作为您的 DataContext。
有 3 种主要方法可以将 ViewModel 设置为 View 的 DataContext。
使用视图后面的代码:
public ViewSubjectGradePage()
{
this.InitializeComponent();
this.DataContext = new ViewSubjectGradeViewModel();
}
使用XAML(为便于阅读删除了其他页面属性):
<Page x:Class="LC_Points.View.ViewSubjectGradePage"
xmlns:vms="using:LC_Points.ViewModel">
<Page.DataContext>
<vms:ViewSubjectGradeViewModel/>
</Page.DataContext>
</Page>
使用像 Prism 这样的 MVVM 框架。这将根据标准命名约定自动连接您的视图和视图模型。
我更喜欢选项 3,因为它可以提供更加松散耦合的系统,但对于非常小的项目来说可能有点矫枉过正,而且任何框架都有一个学习曲线,但好处是巨大的。选项 2 是我的第二次投票,因为它使代码更干净。选项 1 是我不再做的事情,但它是一种让它工作的快速方法。
我正在尝试将视图的数据上下文设置为其 ViewModel 中包含的列表。但是当我测试当前设置时,似乎 ViewModel 和 View 之间的 data context
设置不正确。
为了调试这个问题,我在 View 的构造函数中设置了一个消息框,我收到了以下错误消息,提示数据上下文设置不正确:“对象引用不正确设置为对象的实例"
该列表还在另一个 ViewModel 中使用,这表明该列表不为空,这进一步暗示了数据上下文问题。
有谁知道在 View 和 ViewModel 之间设置数据上下文的缺陷是什么?
这是包含列表的 ViewModel:
namespace LC_Points.ViewModel
{
public class ViewSubjectGradeViewModel
{
public ViewSubjectGradeViewModel()
{
AddedSubjectGradePairs = new ObservableCollection<ScoreModel>();
}
public ObservableCollection<ScoreModel> AddedSubjectGradePairs { get; set; }
}
}
这是View和View背后的代码:
<Page x:Class="LC_Points.View.ViewSubjectGradePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:LC_Points.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vms="using:LC_Points.ViewModel"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding ViewSubjectGradeViewModelProperty1}"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="40*" />
<RowDefinition Height="20*" />
<RowDefinition Height="30*" />
<RowDefinition Height="30*" />
<RowDefinition Height="20*" />
<RowDefinition Height="20*" />
</Grid.RowDefinitions>
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Margin="0,12,0,0"
Style="{ThemeResource TitleTextBlockStyle}"
Text="LC POINTS" />
<TextBlock Margin="0,-6.5,0,26.5"
CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"
Foreground="DarkGreen"
Style="{ThemeResource HeaderTextBlockStyle}"
Text="View Grades" />
</StackPanel>
<!-- TODO: Content should be placed within the following grid -->
<Grid x:Name="ContentRoot"
Grid.Row="1"
Margin="19,9.5,19,0">
<ListBox Height="400"
Margin="0,0,0,-329"
VerticalAlignment="Top"
ItemsSource="{Binding AddedSubjectGradePairs}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Subject}" /><Run Text=" - " /><Run Text="{Binding Points}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Page>
查看后面的代码:
namespace LC_Points.View
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ViewSubjectGradePage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public ViewSubjectGradePage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
var messageDialog = new MessageDialog(DataContext.GetType().ToString());
messageDialog.ShowAsync();
}
/// <summary>
/// Gets the <see cref="NavigationHelper"/> associated with this <see cref="Page"/>.
/// </summary>
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
/// <summary>
/// Gets the view model for this <see cref="Page"/>.
/// This can be changed to a strongly typed view model.
/// </summary>
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
}
private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
}
#region NavigationHelper registration
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedFrom(e);
}
#endregion
}
}
我认为这是由于您分配 DataContext
的方式所致。在您的 XAML 中,您将 DataContext 绑定到 属性。我没有在您的代码中的任何地方看到您实际将数据上下文分配给 defaultViewModel
或其中的属性。
尝试将您的构造函数更新为以下内容。
public ViewSubjectGradePage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
this.DataContext = this.DefaultViewModel;
}
如果您想将其分配给 DefaultViewModel
中的 属性,您也可以在那里进行。然后删除 XAML.
您可能想要从您的视图代码中删除模板提供的您未使用的任何生成代码。这可能会导致混淆,因为模板希望您使用本地 ObservableCollection 作为您的 DataContext。
有 3 种主要方法可以将 ViewModel 设置为 View 的 DataContext。
使用视图后面的代码:
public ViewSubjectGradePage() { this.InitializeComponent(); this.DataContext = new ViewSubjectGradeViewModel(); }
使用XAML(为便于阅读删除了其他页面属性):
<Page x:Class="LC_Points.View.ViewSubjectGradePage" xmlns:vms="using:LC_Points.ViewModel"> <Page.DataContext> <vms:ViewSubjectGradeViewModel/> </Page.DataContext> </Page>
使用像 Prism 这样的 MVVM 框架。这将根据标准命名约定自动连接您的视图和视图模型。
我更喜欢选项 3,因为它可以提供更加松散耦合的系统,但对于非常小的项目来说可能有点矫枉过正,而且任何框架都有一个学习曲线,但好处是巨大的。选项 2 是我的第二次投票,因为它使代码更干净。选项 1 是我不再做的事情,但它是一种让它工作的快速方法。