[英]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
中的ViewModel
的enum
值。
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.