简单地说,我有一个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建议我添加一些关于ApplicationMenuHandler
和RecentFilesViewModel
类的更多信息,以及我在哪里示例化它等等。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
(它只有一个PropertyChangedEventHandler
和NotifyPropertyChanged
方法)。下面是类:
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>
希望这能提供更多有用的信息。
1条答案
按热度按时间6rvt4ljy1#
似乎是已知的问题,可以通过覆盖2属性在您的应用程序。xaml文件...
如果你想更深入地挖掘,请参阅下面的答案和其中的MSDN支持论坛线程的链接...
MenuItem added programmatically causes Binding error