yuwan1994 2024-05-15 16:25 采纳率: 66.7%
浏览 17

prism wpf 删除tabcontrol删除最后一个标签弹出错误信息 找不到源: RelativeSource FindAncestor

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)
        {
        }
    }
}
  • 写回答

2条回答 默认 最新

  • 阿里嘎多学长 2024-05-15 16:26
    关注

    以下内容由CHATGPT及阿里嘎多学长共同生成、有用望采纳:


    【您想要解决的问题】:您提问的目的是解决在使用Prism框架开发WPF应用程序时,当删除TabControl中的最后一个TabItem时,出现的“找不到源: RelativeSource FindAncestor”错误信息的问题。

    【问题出现原因】:该错误信息通常与绑定有关。在您提供的XAML代码中,TabItemCloseTabCommand绑定使用了RelativeSource来查找其祖先类型的TabControl。当删除最后一个TabItem时,可能因为已经没有TabItem作为祖先,导致无法找到绑定的源,从而触发了这个错误。

    【问题解决方案】:为了解决这个问题,您需要确保在执行删除操作时,RelativeSource能够正确地找到其祖先类型的TabControl。一个可能的解决方案是修改CloseTabCommand的绑定,使其直接引用TabControl,而不是通过TabItemDataContext。此外,您还需要确保在删除最后一个TabItem时,TabControl能够正确地处理这种情况。

    【提供代码】:以下是修改后的TabItemStyle部分,其中CloseTabCommand直接绑定到TabControlItems集合:

    <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 Path=PlacementTarget.CloseTabCommand, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}">
                            <Button.Template>
                                <ControlTemplate>
                                    <materialDesign:PackIcon Background="Transparent" Foreground="Black" Kind="Close" />
                                </ControlTemplate>
                            </Button.Template>
                        </Button>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    请注意,这里使用了PlacementTarget来引用TabControl,这是假设您的TabControl有一个名为CloseTabCommand的命令。您可能需要根据您的实际代码进行调整。

    【代码运行方式】:您需要在WPF项目中使用Prism框架,并将上述修改后的XAML代码替换到您的项目中相应的位置。确保您的项目已经设置了正确的命名空间和引用。

    【代码预期运行结果】:修改后的代码应该能够解决删除最后一个TabItem时出现的错误信息问题。当您删除TabControl中的最后一个TabItem时,应用程序将不会崩溃,也不会出现错误信息。

    【推荐相关链接】:

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 5月15日