C# ViewModel中的数据未从PageLoad事件绑定到XAML

C# ViewModel中的数据未从PageLoad事件绑定到XAML,c#,mvvm,data-binding,uwp,uwp-xaml,C#,Mvvm,Data Binding,Uwp,Uwp Xaml,我不熟悉MVVM体系结构。我正在构建一个UWP应用程序。我基本上有一个视图(XAML)、代码隐藏(XAML.cs)、ViewModel和数据服务 我的视图/XAML如下所示: <Page x:Class="SnapBilling.SyncModule.SyncView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="

我不熟悉MVVM体系结构。我正在构建一个UWP应用程序。我基本上有一个视图(XAML)、代码隐藏(XAML.cs)、ViewModel和数据服务

我的视图/XAML如下所示:

<Page
    x:Class="SnapBilling.SyncModule.SyncView"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" 
    mc:Ignorable="d" Loaded="Page_Loaded"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    
    
    <Grid>
        <Grid.Resources>
            <!--DataTemplate for Published Date column defined in Grid.Resources.  PublishDate is a property on the ItemsSource of type DateTime -->
            <DataTemplate x:Key="ProgressTemplate" >
                <ProgressBar x:Name="progressBar1" Value="{Binding Value.ProgressPercentage}" Maximum="100" Margin="20"/>
            </DataTemplate>
            
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="4*" />
            <RowDefinition Height="2*" />
        </Grid.RowDefinitions>
        <controls:DataGrid x:Name="BackupSummaryDataGrid" 
                            Grid.Row="0"
                            Margin="40"
                            ItemsSource="{x:Bind DataContext.UploadProgressInfoDict}"
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            AlternatingRowBackground="Transparent"
                            AreRowDetailsFrozen="False"
                            AreRowGroupHeadersFrozen="True"
                            AutoGenerateColumns="False"
                            CanUserReorderColumns="True"
                            CanUserResizeColumns="True"
                            ColumnHeaderHeight="32"
                            FrozenColumnCount="0"
                            GridLinesVisibility="None"
                            HeadersVisibility="Column"
                            HorizontalScrollBarVisibility="Visible"
                            IsReadOnly="False"
                            MaxColumnWidth="400"
                            RowDetailsVisibilityMode="Collapsed"
                            RowGroupHeaderPropertyNameAlternative="Range"
                            SelectionMode="Extended"
                            VerticalScrollBarVisibility="Visible"
                            >

            <controls:DataGrid.Columns>
                <controls:DataGridTextColumn Tag="SyncType" Header="Sync Type" Binding="{Binding Key}" IsReadOnly="True"  />
                <controls:DataGridTextColumn Tag="remaining" Header="Pending Items" Binding="{Binding Value.remainingNow}" IsReadOnly="True"  />
                <controls:DataGridTemplateColumn Header="% remaining" CellTemplate="{StaticResource ProgressTemplate}" />
                <controls:DataGridTextColumn Header="Status" Binding="{Binding Value.ProgressMessage}" IsReadOnly="True"/>

            </controls:DataGrid.Columns>
            
        </controls:DataGrid>
    </Grid>
</Page>

如何解决这个问题?

因此代码将执行的顺序是

  • 构造函数调用
  • 视图元素创建
  • 装订
  • 页面加载事件(仅在添加所有子项时发生)
  • 当您在构造函数中设置
    DataContext
    时,
    DataContext
    中的绑定不为空,但是当您在加载的
    页面中设置
    DataContext
    时,
    DataContext
    将为空,因此您在视图中看不到任何数据

    如果可以的话,我建议您在绑定之前在构造函数中初始化视图模型。但是,如果出于某种原因,您需要在
    Page_Loaded
    事件中绑定它,您需要一种方法来告诉视图在其更改时更新值

    幸运的是,框架已经有了解决方案,您可以在ViewModel上使用
    INotifyPropertyChanged
    界面,这样它就可以通知视图属性值的更改,在您的情况下,我猜是
    value.ProgressPercentage

    但是您必须编写一个事件处理程序,它将相应地更新您的视图
    像这样的

     private void OnViewModelPropertyChange(object sender, PropertyChangedEventArgs e)
            {
                switch (e.PropertyName)
                {
                    case nameof(ViewModel.prop1):
                        // do something
                        break;
                    case nameof(ViewModel.prop2):
                        // do something else
                        break;
                }
            }
    

    这就是我建议您改用数据绑定的原因,这并不是非常复杂,但它会为您节省几行额外的编码。

    现在在代码背后:

    public SyncView()
    {
     this.Loaded += SyncView_Loaded;  
    }
    private void SyncView_Loaded(object sender, RoutedEventArgs e)
    {
     this.InitializeComponent();
     DataContext = new SyncViewModel(ServiceLocator.Current.GetService<ICommonServices>());
    }
    
    原因:之前我创建了一个视图模型对象(例如,VM)并将其保存在codebehind类的属性中,然后用该属性设置datacontext。后来,我将itemssource与该对象的UploadProgressInfoDict类绑定

    ItemsSource=“{Binding VM.UploadProgressInfoDict}”


    拆下这一块,效果很好

    我已经使用了INotifyPropertyChanged。但我想,你的解决方案的前两段对我来说是可行的。显然,datacontext没有在较早时正确设置。
     private void OnViewModelPropertyChange(object sender, PropertyChangedEventArgs e)
            {
                switch (e.PropertyName)
                {
                    case nameof(ViewModel.prop1):
                        // do something
                        break;
                    case nameof(ViewModel.prop2):
                        // do something else
                        break;
                }
            }
    
    public SyncView()
    {
     this.Loaded += SyncView_Loaded;  
    }
    private void SyncView_Loaded(object sender, RoutedEventArgs e)
    {
     this.InitializeComponent();
     DataContext = new SyncViewModel(ServiceLocator.Current.GetService<ICommonServices>());
    }
    
    ItemsSource="{Binding UploadProgressInfoDict}"