动态ViewModel的绑定值在WPF中为空

时间:2019-04-25 07:53:53

标签: c# wpf

我跟随一些在线教程来在ListView的动态视图之间进行切换。

  1. 在我的MainWindow中,左窗格中有一个ListView,其中包含子ViewModel列表的ItemsSource。 (每个子ViewModel都实现一个接口)
  2. 每个ViewModel都有自己的View作为DataTemplate。
  3. 我正在从MainWindow调用所选视图的GenerateReport()方法。但是所选视图的值将为空。

Github下载我的源代码。

重现此问题的步骤:

  1. 运行该应用程序,然后在“学生报告”的ID和名称中键入文本。 (StudentReportViewModels属性中的断点已正确命中,并且值已更新。)
  2. 然后单击“生成报告”按钮。 StudentReportViewModels的属性值将为null。

如何解决此问题?请帮忙。

来源: MainWindow.xaml

<Window.Resources>
    <DataTemplate DataType="{x:Type vm:StudentsReportViewModel}">
        <view:StudentsReport/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:MarksReportViewModel}">
        <view:MarksReport />
    </DataTemplate>
</Window.Resources>
<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <ListView ItemsSource="{Binding Reports}" SelectedItem="{Binding SelectedReport, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <GridSplitter Grid.Row="1" Grid.Column="1" Width="5" ResizeBehavior="PreviousAndNext" HorizontalAlignment="Stretch"/>
    <Grid Grid.Column="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <ContentControl Content="{Binding SelectedReport.ViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </ScrollViewer>
        <Button Content="Generate Report" Grid.Row="2" Margin="5" HorizontalAlignment="Right" Command="{Binding GenerateReportCommand}"/>
    </Grid>
</Grid>

MainWindowViewModel:

public class MainWindowViewModel : INotifyPropertyChanged
{
    private ICommand _generateReportCommand;

    private Report selectedReport;
    public Report SelectedReport
    {
        get
        {
            return this.selectedReport;
        }

        set
        {
            if (value != this.selectedReport)
            {
                this.selectedReport = value;
                NotifyPropertyChanged();
            }
        }
    }

    public List<Report> Reports { get; set; }

    public ICommand GenerateReportCommand
    {
        get
        {
            if (_generateReportCommand == null)
            {
                _generateReportCommand = new RelayCommand(
                    p => GenerateReport()
                    );
            }

            return _generateReportCommand;
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public MainWindowViewModel()
    {
        Reports = new List<Report>
        {
            new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
            new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
        };

        SelectedReport = Reports[0];
    }

    public void GenerateReport()
    {
        SelectedReport.ViewModel.GenerateReport();
    }
}

StudentsReport.xaml

<TextBox Height="25" Width="100" Margin="5" Text="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Height="25" Width="100" Margin="5" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

StudentsReportViewModel:

public class StudentsReportViewModel : INotifyPropertyChanged, IReportViewModel
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string id;
    public string Id
    {
        get
        {
            return this.id;
        }

        set
        {
            if (value != this.id)
            {
                this.id = value;
                NotifyPropertyChanged();
            }
        }
    }

    private string name;
    public string Name
    {
        get
        {
            return this.name;
        }

        set
        {
            if (value != this.name)
            {
                this.name = value;
                NotifyPropertyChanged();
            }
        }
    }

    public StudentsReportViewModel()
    {

    }

    public void GenerateReport()
    {
        System.Windows.MessageBox.Show($"Id = {Id}, Name = {Name}");
    }
}

接口:

public interface IReportViewModel
{
    void GenerateReport();
}

型号:

public class Report
{
    public string Name { get; set; }

    public IReportViewModel ViewModel { get; set; }
}

1 个答案:

答案 0 :(得分:2)

您的StudentsReport.xaml UserControl绑定到在XAML中创建的StudentsReportViewModel实例:

<UserControl.DataContext>
    <vm:StudentsReportViewModel/>
</UserControl.DataContext>

但是,“生成报告”按钮正在调用在MainWindowVieModel构造函数中创建并存储在Report类中的StudentReportViewModel的另一个实例。

Reports = new List<Report>
{
    new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
    new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};

您需要删除这些实例之一,以便UserControl的DataContext绑定到您要从中生成报告消息的同一视图模型实例。 我建议从StudentsReport.xaml删除此代码:

<UserControl.DataContext>
    <vm:StudentsReportViewModel/>
</UserControl.DataContext>