Prism框架,tabcontrol 控件删除最后一个标签时弹出错误提示“找不到源: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1'。”,原始错误信息“Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=TabStripPlacement; DataItem=null; target element is 'TabItem' (Name=''); target property is 'NoTarget' (type 'Object')”
寻求解疑并给出解决办法
Prism 版本 8.1.97
视图代码
<Window x:Class="YW.MainApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:common="clr-namespace:YW.MainApp.Common;assembly=YW.MainApp.Common"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" WindowState="Maximized" Height="700" Width="1300"
Icon="pack://application:,,,/icon/fish.ico"
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#F7F9FA" Offset="0" />
<GradientStop Color="#FFD4EEFB" Offset="1" />
</LinearGradientBrush>
</Window.Background>
<Window.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding DataContext.PageTitle}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="30" Text="{TemplateBinding Header}" />
<Button VerticalAlignment="Center" Command="{Binding DataContext.DataContext.CloseTabCommand, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}">
<Button.Template>
<ControlTemplate>
<materialDesign:PackIcon Background="Transparent" Foreground="Black" Kind="Close" />
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView x:Name="menu" Grid.Column="0" Foreground="White" FontSize="30" ItemsSource="{Binding TreeData}" MinWidth="250">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type common:TreeItemModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<!--<materialDesign:PackIcon Kind="{Binding Icon}" Margin="4 0" />-->
<TextBlock Text="{Binding DisplayName}" Margin="2 0" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding TreeviewSelectedItemChangedCommand}" CommandParameter="{Binding ElementName=menu, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
<TabControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion" HorizontalContentAlignment="Left" />
</Grid>
</Window>
ViewModel 代码
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using System.Collections.ObjectModel;
using YW.MainApp.Common;
namespace YW.MainApp.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public override string PageTitle => string.Empty;
private readonly IRegionManager regionManager;
private readonly IEventAggregator eventAggregator;
public DelegateCommand<TreeItemModel> TreeviewSelectedItemChangedCommand { get; }
public ObservableCollection<TreeItemModel> TreeData { get; } = new ObservableCollection<TreeItemModel>();
public MainWindowViewModel(IEventAggregator _eventAggregator, IRegionManager _regionManager) : base(_eventAggregator, _regionManager)
{
TreeviewSelectedItemChangedCommand = new DelegateCommand<TreeItemModel>(TreeviewSelectedItemChanged);
var secondNodeChildren = new TreeItemModel[] { new TreeItemModel { DisplayName = "一级菜单2的子菜单1", TargetView = "ssss", Children = new TreeItemModel[] { } }, new TreeItemModel { DisplayName = "一级菜单2的子菜单2", TargetView = "fea" } };
var secondNode = new TreeItemModel { DisplayName = "一级菜单2", Children = firstNodeChildren };
foreach (var item in firstNodeChildren)
{
item.Parent = secondNode;
}
TreeData.Add(secondNode);
regionManager = _regionManager;
eventAggregator = _eventAggregator;
}
private void TreeviewSelectedItemChanged(TreeItemModel node)
{
var view = node.TargetView;
if (!view.IsNullOrEmpty())
{
regionManager.RequestNavigate("ContentRegion", view);
}
}
}
}
ViewModelBase 代码
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Regions;
namespace YW.MainApp.Common
{
public abstract class ViewModelBase : BindableBase, INavigationAware
{
public abstract string PageTitle { get; }
public DelegateCommand<object> CloseTabCommand { get; }
private readonly IRegionManager regionManager;
private readonly IEventAggregator eventAggregator;
public ViewModelBase(IEventAggregator _eventAggregator, IRegionManager _regionManager)
{
eventAggregator = _eventAggregator;
regionManager = _regionManager;
CloseTabCommand = new DelegateCommand<object>(CloseTab);
}
private void CloseTab(object obj)
{
regionManager.Regions["ContentRegion"].Remove(obj);
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
}
}
}