MVVM: ViewModel inheritance

落花浮王杯 提交于 2020-01-03 05:36:06

问题


I have the following ViewModel:

public class DocumentViewModel : ViewModelBase
{
    public virtual string TabHeader
    {
        get { return "Document"; }
    }

    private ObservableCollection<DATA> data;
    /// <summary>
    /// Source for Grid
    /// </summary>
    public ObservableCollection<DATA> Data
    {
        get { return data; }
        set
        {
            data = value;
            RaisePropertyChanged("Data");
        }
    }
  // ...... a lot of properties and methods ....
}

I want ProcurementViewModel to inherit DocumentViewModel:

 public class ProcurementViewModel : DocumentViewModel
 {
    public override string TabHeader
    {
        get { return "Procurement"; }
    }
 }

Note that I override just one property. The rest of the properties should be taken from base ViewModel.

Now I want to display it by using this DataTemplate:

<DataTemplate DataType="{x:Type vm:ProcurementViewModel}">
    <views:DocumentView />
</DataTemplate>

But no data is shown. TabHeader is bound to Header of TabControl - it shows a value from base ViewModel.

If I specify DataType as {x:Type vm:DocumentViewModel} everything works just fine.

Here is a part of DocumentView, where I use DataTemplates:

<Grid>
    <telerik:RadTabControl Name="rtcTabs"  ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}" SelectedIndex="1">
        <telerik:RadTabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding TabHeader}"/>
            </DataTemplate>
        </telerik:RadTabControl.ItemTemplate>
        <telerik:RadTabControl.Resources>
            <DataTemplate DataType="{x:Type vm:DashboardViewModel}">
                <views:DashboardView />
            </DataTemplate>
            <DataTemplate DataType="{x:Type vm:ProcurementViewModel}">
                <views:DocumentView />
            </DataTemplate>
        </telerik:RadTabControl.Resources>
    </telerik:RadTabControl>
</Grid>

Here is a part of DocumentView.xaml:

<UserControl x:Class="DMRS.Views.DocumentView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
         xmlns:vm="clr-namespace:DMRS.ViewModels;assembly=DMRS.ViewModels"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="358" d:DesignWidth="582" xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation">
<!--<UserControl.DataContext>
    <vm:DocumentViewModel/>
</UserControl.DataContext>-->
<UserControl.Resources>
    <Style x:Key="stlDocViewCombobox" TargetType="{x:Type telerik:RadComboBox}">
        <Setter Property="OpenDropDownOnFocus" Value="True"/>
    </Style>
</UserControl.Resources>

<Grid>
    <telerik:RadGridView AutoGenerateColumns="False" Name="rgvData" 
                         ItemsSource="{Binding Data}" 
                         SelectedItem="{Binding SelectedData}">
        <telerik:RadGridView.Resources>
..................................

回答1:


If I specify DataType as {x:Type vm:DocumentViewModel} everything works just fine. TabHeader is bound to Header of TabControl - it shows a value from base ViewModel.

It sounds like the item in your DataContext is actually a DocumentViewModel, not a ProcurementViewModel

An implicit DataTemplate for a base object should get applied to all objects that inherit from that type as well, however DataTemplates for a child object won't apply to a parent object.

So since you see the base DocumentViewModel.TabHeader when you set the DataType="{x:Type vm:DocumentViewModel}", that means you are probably binding to a DocumentViewModel object, not a ProcurementViewModel object.

To confirm this is the case, you can use a 3rd party tool like Snoop to find out what your DataContext object is at run-time.

Edit:

Based on the new code you added to your question, the most likely cause is your Tabs collection (the DataContext for the item applying the DataTemplate) does not contain a ProcurementViewModel object.

Can you check to be sure that your Tabs collection contains a ProcurementViewModel object, and doesn't just contain DocumentViewModel objects?

(Also since your DataTemplates are the same and ProcurementViewModel inherits from DocumentViewModel, you only need the DataTemplate for the DocumentViewModel)




回答2:


From the limited amount of information available to us, it looks as though this should work! Check that the namespace for your derived VM is the same as the namespace of the base VM (or, if they are different on purpose, that you have an appropriate declaration for the namespace in XAML).

Another possibility is that you don't actually have any instances of ProcurementViewModel at runtime, so the DataTemplate is never used. Double-check the runtime type of your VM instance here.

These things may sound obvious, but since this ought to work, you need to look for some small oversight in your code.




回答3:


One nice way to deal with this sort of problems is to inspect the DataContext property of the content control at runtime.

A good way to do that is to use a tool that you can inspect the bindings at runtime. For example, you can use the wpf inspector (http://wpfinspector.codeplex.com/).



来源:https://stackoverflow.com/questions/14566742/mvvm-viewmodel-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!