wpf 将CompositeCollection用作具有MenuItem上的变量类型的ItemSource时的绑定错误

z0qdvdin  于 2023-04-07  发布在  其他
关注(0)|答案(1)|浏览(233)

简单地说,我有一个WPF MenuItem,其中包含最近打开的文件列表,它使用一个名为“RecentFilesViewModel”的类来填充文件并设置打开它们的命令。但是当我添加一个Seperator和一个最终手动添加的MenuItem时,问题就来了,它会清除最近打开的文件列表。
我的问题是,当使用CompositeCollection来设置ItemSource时,它可以很好地与自定义类提供的最近文件列表的CollectionContainer一起工作,但是一旦我包含Seperator或清除文件MenuItem,我就会遇到绑定问题。令人恼火的是,它实际上像预期的那样工作,没有问题,但我真的想知道为什么会显示绑定错误,并摆脱它们。
下面是我的MenuItem和它的CompositeCollection的XAML:

<MenuItem Header="_Recent files">
    <MenuItem.ItemsSource>
        <CompositeCollection>
            <CollectionContainer Collection="{Binding Source={StaticResource recentFilesViewModel}, Path=RecentFiles}" />
            <Separator Name="Seperator" />
            <MenuItem Name="ClearRecentFilesButton" Header="Clear recent files" Command="{x:Static local:ApplicationMenuHandler.File_RecentFiles_Clear}" />
        </CompositeCollection>
    </MenuItem.ItemsSource>
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Style.Triggers>
                <DataTrigger Value="{x:Null}">
                    <DataTrigger.Binding>
                        <PriorityBinding>
                            <Binding Path="Command"/>
                        </PriorityBinding>
                    </DataTrigger.Binding>
                    <Setter Property="Command" Value="{x:Static local:ApplicationMenuHandler.File_RecentFiles_Open}"/>
                    <Setter Property="CommandParameter" Value="{Binding FilePath}"/>
                    <Setter Property="Header" Value="{Binding FilePath}"/>
                    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </MenuItem.ItemContainerStyle>
</MenuItem>

删除线路后:

<Separator Name="Seperator" />
<MenuItem Name="ClearRecentFilesButton" Header="Clear recent files" Command="{x:Static local:ApplicationMenuHandler.File_RecentFiles_Clear}" />

我根本没有得到任何绑定错误。那么是什么导致了这些错误呢?我会认为CompositeCollection正是允许这样的,一个变量类型的复合集合?
需要注意的事项有:
1.当只向集合中添加Seperator时,绑定错误仅在单击其中一个包含的菜单项后显示。错误如下:
系统.Windows.数据错误:4:找不到引用为“RelativeSource FindAncestor,AncestorType ='System.Windows.Controls. ItemsControl',AncestorLevel ='1''. BindingExpression:Path=HorizontalContentAlignment;String =null;目标元素为“MenuItem”(Name ='');目标属性为“水平内容对齐方式”(键入“水平对齐方式”)
1.当只添加额外的MenuItem时,应用程序加载时就会显示错误。但基本上是相同的错误:
系统.Windows.数据错误:4:找不到引用为“RelativeSource FindAncestor,AncestorType ='System.Windows.Controls. ItemsControl',AncestorLevel ='1''. BindingExpression:Path=HorizontalContentAlignment;String =null;目标元素为“MenuItem”(Name ='ClearRecentFilesButton');目标属性为“水平内容对齐方式”(键入“水平对齐方式”)
我一直在绕圈子试图解决它,我想知道它是否与DataTrigger有关,但在尝试了许多不同的方法只针对具有“Command”属性的MenuItem之后,似乎没有什么改变任何事情,也许我误解了DataTrigger的工作原理,我真的希望我可以只使用后面的代码,因为这似乎是如此不必要的复杂,以实现如此简单的东西,如果它是代码,而不是XAML标记。
真的会感激任何帮助,我非常感谢任何帮助!提前谢谢你。

更新(根据davmos要求)

@davmos建议我添加一些关于ApplicationMenuHandlerRecentFilesViewModel类的更多信息,以及我在哪里示例化它等等。
ApplicationMenuHandler只是一组静态命令,看起来都运行得很好,下面是一个类本身开始的例子:

public static class ApplicationMenuHandler
{
    // File -> New
    public static ICommand File_New { get; } = new RelayCommand((parameter) => {
        // Create new level editor
        LevelEditor levelEditor = new(){ Tag = "New Level.lvl (" + MainWindow.instance?.actionTabsModel.Tabs.Count + ")" };

        // Add a tab with the new level editor
        MainWindow.instance?.actionTabsModel.AddTab(levelEditor);

        // Once level editor is loaded and ready to scroll, scroll to center
        SubRoutines.WaitUntil(() => levelEditor.levelScrollViewer != null && levelEditor.levelScrollViewer.IsLoaded, () => {
            levelEditor.levelScrollViewer.ScrollToCenter();
        });
    });

下面是整个RecentFileViewModel类,它扩展了ViewModelBase(它只有一个PropertyChangedEventHandlerNotifyPropertyChanged方法)。下面是类:

namespace LevelXEditor.Project.RecentFiles
{
    public class RecentFilesViewModel : ViewModelBase
    {
        public static RecentFilesViewModel? Instance { get; private set; }

        private ObservableCollection<RecentFile> recentFiles = new();
        public ObservableCollection<RecentFile> RecentFiles { get => recentFiles; set { recentFiles = value; NotifyPropertyChanged("RecentFiles"); } }

        // Constructor
        public RecentFilesViewModel()
        {
            Instance = this;
            RecentFiles = new ObservableCollection<RecentFile>();
            Refresh();
        }

        public void Refresh()
        {
            // Clear recent files
            RecentFiles.Clear();

            // Add recent files
            foreach (string recentFile in MainWindow.AppDataHandler.Data.recentFiles)
            {
                RecentFiles.Add(new RecentFile() { FilePath = recentFile, IsEnabled = true });
            }

            // If there are no recent files then add a placeholder
            if (RecentFiles.Count == 0)
            {
                RecentFiles.Add(new RecentFile() { FilePath = "No recent files", IsEnabled = false });
            }
        }
    }

    public class RecentFile
    {
        public string FilePath { get; set; } = "";
        public bool IsEnabled { get; set; } = true;
    }
}

我仍然在理解WPF在XAML方面的工作方式,但这就是我在MainWindow XAML中“示例化”RecentFilesViewModel对象的方式:

<Window x:Class="LevelXEditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LevelXEditor"
        xmlns:recentFiles="clr-namespace:LevelXEditor.Project.RecentFiles"
        xmlns:statusBar="clr-namespace:LevelXEditor.Project.StatusBar"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="600" Width="1000">
    <Window.Resources>
        <statusBar:StatusBarViewModal x:Key="statusBarViewModel"/>
        <recentFiles:RecentFilesViewModel x:Key="recentFilesViewModel"/>
        <local:EqualConverter x:Key="EqualConverter" />
    </Window.Resources>

希望这能提供更多有用的信息。

6rvt4ljy

6rvt4ljy1#

似乎是已知的问题,可以通过覆盖2属性在您的应用程序。xaml文件...

<Application.Resources>
    <Style TargetType="MenuItem">
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
    </Style>
</Application.Resources>

如果你想更深入地挖掘,请参阅下面的答案和其中的MSDN支持论坛线程的链接...
MenuItem added programmatically causes Binding error

相关问题