简体   繁体   English

在Wpf中的TabControl中切换到视图时如何摆脱BindingExceptions

[英]How to get rid of BindingExceptions, when switching between to views in a TabControl in Wpf

In my ViewModel I have collection of TabItem . 在我的ViewModel我有TabItem集合。 Each TabItem contains a Name and another ViewModel (inherits from BaseModel with INotifyPropertyChanged ). 每个TabItem包含一个Name和另一个ViewModel (继承自BaseModel具有INotifyPropertyChanged )。 Based on a property of this ViewModel , XAML makes a decission which View should be placed in the ContentTemplate of each tab control item. 基于此ViewModel的属性,XAML决定应将哪个View放置在每个选项卡控件项的ContentTemplate中。

I am working with the mvvm pattern and I got this work with switching between tabs. 我正在使用mvvm模式,并且可以通过在选项卡之间切换来完成这项工作。 The problem is I get binding exceptions. 问题是我收到绑定异常。 It is very difficult to explain. 这很难解释。

The error message looks like this: 错误消息如下所示:

System.Windows.Data Error: 40 : BindingExpression path error: 'ExtraText' property not found on 'object' ''DisplayModel1' (HashCode=8229676)'. System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“ DisplayModel1”(HashCode = 8229676)上找不到“ ExtraText”属性。 BindingExpression:Path=ExtraText; BindingExpression:路径= ExtraText; DataItem='DisplayModel1' (HashCode=8229676); DataItem ='DisplayModel1'(HashCode = 8229676); target element is 'Label' (Name=''); 目标元素是'标签'(名称=''); target property is 'Content' (type 'Object') 目标属性为“内容”(类型为“对象”)

To reproduce the error you can use the following code, with sample data provided. 要重现该错误,可以使用下面的代码,并提供示例数据。 This is the XAML of my starting window: 这是我的起始窗口的XAML:

<Window.Resources>
    <DataTemplate x:Key="Model1Template" DataType="{x:Type local:BaseModel}">
        <local:DisplayModel1View />
    </DataTemplate>
    <DataTemplate x:Key="Model2Template" DataType="{x:Type local:BaseModel}">
        <local:DisplayModel2View />
    </DataTemplate>
</Window.Resources>
<Grid>
    <TabControl ItemsSource="{Binding TabItems}">
        <TabControl.Resources>
            <Style TargetType="{x:Type TabItem}">
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
            </Style>
        </TabControl.Resources>
        <TabControl.ItemTemplate>
            <DataTemplate DataType="{x:Type local:TabModel}">
                <Label DockPanel.Dock="Left" Content="{Binding Name}"></Label>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate DataType="{x:Type local:BaseModel}">
                <ContentControl Content="{Binding ViewModel, Mode=TwoWay}">
                    <ContentControl.Style>
                        <Style TargetType="{x:Type ContentControl}">
                            <Setter Property="ContentTemplate" Value="{StaticResource Model1Template}" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Path=ViewModel.ModelType}" Value="Model2">
                                    <Setter Property="ContentTemplate" Value="{StaticResource Model2Template}" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Grid>

I defined two Templates which are holding the specific View. 我定义了两个保存特定视图的模板。 In the TabControl.ContentTemplate I am checking the enum value of the ViewModel located in my TabModel . TabControl.ContentTemplate我正在检查位于TabModel中的ViewModelenum值。

These are the possible Views: 这些是可能的视图:

DisplayModel1View DisplayModel1View

<Grid>
    <Label Content="{Binding Name}"></Label>        
</Grid>

DisplayModel2View DisplayModel2View

<StackPanel>
        <Label Content="{Binding Name}"></Label>
        <Label Content="{Binding ExtraText}"></Label>
    </StackPanel>

You can see, the underlying view models have not the same properties. 您可以看到,基础视图模型具有不同的属性。 In DisplayModel2View you can see the property ExtraText , mentioned in the error message. DisplayModel2View您可以看到错误消息中提到的ExtraText属性。

Last but not least my models: 最后但并非最不重要的一点是:

public abstract class BaseModel : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    public abstract ModelType ModelType { get; }

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

public class DisplayModel1 : BaseModel
{
    public string Name { get; set; }

    public override ModelType ModelType => ModelType.Model1;
}

public class DisplayModel2 : BaseModel
{
    public string Name { get; set; }

    public string ExtraText { get; set; }

    public override ModelType ModelType => ModelType.Model2;
}

public class TabModel
{
    private bool isSelected;

    public string Name { get; set; }

    public bool IsSelected
    {
        get { return isSelected; }
        set { isSelected = value; }
    }

    public BaseModel ViewModel { get; set; }
}

//MainViewModel
public class ViewModel
{
    public ObservableCollection<TabModel> TabItems { get; set; }

    public ViewModel()
    {
        TabItems = new ObservableCollection<TabModel>();
        TabItems.Add(new TabModel()
        {
            Name = "Tab1",
            ViewModel = new DisplayModel1() { Name = "ModelOne" },
            IsSelected = true
        });

        TabItems.Add(new TabModel()
        {
            Name = "Tab1",
            ViewModel = new DisplayModel2() { Name = "ModelTwo", ExtraText = "ExtraTwo" },
            IsSelected = false
        });
    }
}

//Decission, which View should be used
public enum ModelType
{
    Model1,
    Model2
}

Actually, you don't need that ModelType property. 实际上,您不需要那个ModelType属性。 Let WPF find out which view to use for which view-model based on the view-model's type. 让WPF根据视图模型的类型找出用于哪个视图模型的视图。

Remove the DataTemplate s from Window.Resources first. 首先从Window.Resources删除DataTemplate Remove the trigger. 卸下扳机。 Place your DataTemplate s for concrete view-model types (not for ViewModelBase ) in your TabControl 's resources (this is to make those DataTemplate s local for the tab control - we only want them to apply here). 请将您DataTemplate S代表具体的视图模型类型(不适用于ViewModelBase )在您TabControl的资源(这是为了让那些DataTemplate的本地选项卡控件-我们只希望他们能在这里适用)。

Then, you will get something like: 然后,您将得到类似:

<TabControl ItemsSource="{Binding TabItems}">
    <TabControl.Resources>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
        </Style>
        <DataTemplate DataType="{x:Type local:DisplayModel1}">
            <local:DisplayModel1View />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:DisplayModel2}">
            <local:DisplayModel2View />
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:TabModel}">
            <Label DockPanel.Dock="Left" Content="{Binding Name}"></Label>
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type local:BaseModel}">
            <ContentControl Content="{Binding ViewModel}"/>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM