以编程方式添加的菜单项会导致绑定错误

9
我有一个名为mnuMainMenu的主菜单,其中包含多个子菜单。其中一个子菜单mnuMostRecentDirs本身又是另一个菜单,其项使用绑定到ObservableCollection的ItemSource属性在运行时生成。基本上它显示了最近使用的文件夹列表。
问题在于动态添加的菜单项会生成以下数据绑定错误: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment') 主菜单的XAML声明类似于这样:
 <!-- Main Menu -->
 <Menu x:Name="mnuMainMenu" Height="Auto" IsMainMenu="True">

    <!--- ... more menu declarations before ... -->

    <MenuItem x:Name="mnuitemWorkDirs" Header="Directories">
       <MenuItem x:Name="mnuNewDir"
                 Header="New" 
                 Style="{StaticResource MenuItem}" 
                 Command="{x:Static cmds:Commands.NewDirectory}" />
       <MenuItem x:Name="mnuCurrentDir"
                 Header="Current" 
                 Style="{StaticResource MenuItem}"
                 Command="{x:Static cmds:Commands.CurrentDirectory}" />
       <MenuItem x:Name="mnuMostRecentDirs" 
                 Header="Recent Directories.."
                 Style="{StaticResource MenuItem}"
                 ItemsSource="{Binding ElementName=theMain, Path=MostRecentFoldersList}" />
    </MenuItem>

    <!--- ... more menu declarations after ... -->
 </Menu>

以下代码可以在运行时向可观察集合中添加文件夹:
    private void PopulateMostRecentFoldersList(string[] paths) 
    {
        MostRecentFoldersList.Clear();

        if (paths == null)
           return;

        foreach (var fullPath in paths)
        {                                        
            var mi = new MenuItem();
            mi.Command = Commands.ChooseWorkingDirectory;
            mi.CommandParameter = fullPath;

            string name = System.IO.Path.GetFileName(fullPath);

            mi.Header = name.ToUpper();                

            // removing this style completely
            //     or manually setting the HorizontalContentAlignment property here 
            //     prevents the binding error from happening
            mi.Style = (Style)FindResource("MenuItem");                    

            MostRecentFoldersList.Add(mi);
        }
    }

我成功地把问题追溯到了一个在MenuItem样式中声明但未在我的应用程序中声明的绑定。我猜它是菜单项的默认样式...
其他菜单项似乎没有应用相同的样式时出现任何绑定问题。
所以我认为问题可能出在我的动态添加MenuItems的方式上。更具体地说,似乎是在将项目添加到ItemsControl之前就已经给MenuItem应用了样式。
到目前为止,我只想到了在将MenuItem添加到可观察集合之前,在代码中设置HorizontalContentAlignment属性,但这只是一种权宜之计,实际上掩盖了真正的问题。

1
请查看此处的答案 - LPL
@LPL - 感谢您发布了那个链接。不过您应该把它作为答案发布。 - Mike Dinescu
2个回答

12

正如@LPL指出的那样,这是WPF框架中已知(被认可的)问题。根据在MSDN支持论坛这里这里找到的信息,当将MenuItem的ItemsSource设置为MenuItem集合时,它似乎不能按正确顺序处理事情。

根据MSDN支持人员:

当您将MenuItem.ItemsSource设置为包含MenuItems的Collection时会发生这种情况。此错误已在内部处理,因此您可以将其保持不变。

或者您可以显式设置MenuItem.HorizontalContentAlignment属性和VerticalContentAlignment属性以避免此错误。您可以将以下代码简单地放入application.Resource中来实现此目的。

  <Style TargetType="MenuItem">
   <Setter Property="HorizontalContentAlignment" Value="Left"/>
   <Setter Property="VerticalContentAlignment" Value="Center"/>
  </Style>
我希望这可以帮助其他遇到同样问题的人。


1
和你碰到了完全一样的问题,绑定错误非常恼人,但是现在这个解决方案可行。不知道自2013年以来它是否已经被“修复”了?! - Grim

1

我认为从代码中添加UI元素不是一个好主意或“WPF兼容”的做法。

我建议像这样做:

    <Menu>
        <MenuItem Header="MainMenu" Name="sampleMenu">
            <MenuItem.ItemTemplate>
                <DataTemplate>
                    <MenuItem Header="{Binding Path=PathtoNameOfRecentDocObject}"/>
                </DataTemplate>
            </MenuItem.ItemTemplate>
        </MenuItem>
    </Menu>

并将MainMenu的ItemsSource设置为MostRecentFoldersList

1
我同意你的看法,从哲学角度来看,这并不是100%的WPF。尽管如此,这种行为仍然很奇怪,我试图理解是什么原因导致了这种行为,并且如何更容易地调试这类问题。 - Mike Dinescu
当从分层模型创建菜单层次结构并通过绑定修改菜单时,会出现相同的错误。问题没有得到解决,因为我在调试模式下有一个绑定错误处理程序,以便调查绑定错误,每当我使用不同的用户登录时它都会停止,这只是令人烦恼的。 - Daniel Leiszen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接