如何设置ViewModela视图的数据上下文?

本文关键字:视图 数据 上下文 ViewModela 何设置 设置 | 更新日期: 2023-09-27 17:54:03

我试图将视图的数据上下文设置为包含在它的ViewModel中的列表。但是当我测试当前设置时,似乎ViewModel和View之间的data context设置不正确。

为了调试这个问题,我在View的构造函数中设置了一个消息框,我得到了以下错误消息,它提示数据上下文没有正确设置:"对象引用没有设置为对象的实例"

列表也在另一个ViewModel中使用,显示列表不是空的,这进一步暗示了数据上下文问题。

有人知道在视图和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
    }
}

如何设置ViewModela视图的数据上下文?

您可能想要删除视图代码后面模板提供的您没有使用的任何生成的代码。这可能会引起混淆,因为模板希望你使用本地ObservableCollection作为DataContext。

设置ViewModel到视图的DataContext的主要方法有三种。

  1. 使用View后面的代码:

    public ViewSubjectGradePage()
    {
        this.InitializeComponent();
        this.DataContext = new ViewSubjectGradeViewModel();
    }
    
  2. 使用XAML(删除其他页面属性以方便阅读):

    <Page x:Class="LC_Points.View.ViewSubjectGradePage"
        xmlns:vms="using:LC_Points.ViewModel">
        <Page.DataContext>
            <vms:ViewSubjectGradeViewModel/>
        </Page.DataContext>
    </Page>
    
  3. 使用MVVM框架,如Prism。这将根据标准的命名约定自动连接你的View和ViewModel。

我更喜欢选项3,因为它可以提供一个更加松散耦合的系统,但对于一个非常小的项目来说可能是多余的,而且使用任何框架都有一个学习曲线,但好处是巨大的。选项2是我的第二票,因为它使后面的代码更干净。选项1是我不再做的事情,但这是一个让它工作的好方法。

我认为这是由于您分配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赋值