MenuItem added programmatically causes Binding err

2020-04-06 00:32发布

I have a main menu mnuMainMenu consisting of several sub-menus. One of the sub-menus mnuMostRecentDirs is itself another menu that has it's items generated at run time, using the ItemSource property bound to an ObservableCollection. Basically it's displaying a list of the most recently used folders.

The issue is that the MenuItems added dynamically generate the following data binding error:

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')

The XAML declaration of the main menu goes something like this:

 <!-- 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>

And the following code adds folders to the observable collection at run time:

    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);
        }
    }

I was able to trace the problem to a binding declared in the style for MenuItem which is not declared in my application. I assume it's the default style for menu items..

None of the other menu items appear to be having any binding problems with the same style being applied to all.

So I'm thinking that the problem must be my approach to adding the MenuItems dynamically. More specifically it seems that the style is being applied to the MenuItem before the item is added to the ItemsControl.

So far I could only come up with setting the HorizontalContentAlignment property in code, on the MenuItem before adding it to the observable collection, but that is just a band-aid and it's only really masking the real problem.

2条回答
老娘就宠你
2楼-- · 2020-04-06 01:03

I really think it's not a good idea or ""WPF compatible"" to add UIelemnts from code.

I would suggest something like this:

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

and set MainMenu's ItemsSource to MostRecentFoldersList

查看更多
姐就是有狂的资本
3楼-- · 2020-04-06 01:05

As pointed out by @LPL - it turns out this is a known (acknowledged) problem in the WPF framework. Based on information found on MSDN support forums here and here, it appears that when setting the ItemsSource of a MenuItem to a collection of MenuItems it doesn't handle things in the correct order

Per MSDN Support staff:

This happens when you set MenuItem.ItemsSource to a Collection that contains MenuItems. This error has already been handled internally, so you can just leave it alone.

Or you can explicit set MenuItem.HorizontalContentAlignment property and VerticalContentAlignment property to avoid this error. You can simply put the following code in application.Resource to achieve this.

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

I hope this helps others having the same issue.

查看更多
登录 后发表回答