代码之家  ›  专栏  ›  技术社区  ›  Julien Poulin

如何将menuitem的数据绑定列表合并到WPF中的另一个menuitem?

  •  6
  • Julien Poulin  · 技术社区  · 15 年前

    我有一个“文件” MenuItem 是否要显示最近打开的文件列表。

    这是我现在拥有的XAML:

    <MenuItem Header="File}">
      <MenuItem Header="Preferences..." Command="{Binding ShowOptionsViewCommand}" />
      <Separator />
      <ItemsControl ItemsSource="{Binding RecentFiles}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <MenuItem Header="{Binding DisplayPath}" CommandParameter="{Binding}"
                Command="{Binding Path=DataContext.OpenRecentFileCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
            </MenuItem>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
      <Separator />
      <MenuItem Header="Exit" Command="{Binding CloseCommand}" />
    </MenuItem>
    

    但是,当我使用此代码时,在 菜单项 看起来他们周围有一个容器。我怎样才能摆脱这个问题?

    以下是它的屏幕截图:

    alt text http://www.cote-soleil.be/FileMenu.png

    4 回复  |  直到 6 年前
        1
  •  6
  •   Tim Cooper    13 年前

    “奇怪的偏移”是 MenuItem . 亲本 菜单项 已生成子级 菜单项 为你,但你的 DataTemplate 添加第二个。试试这个:

    <MenuItem Header="File}">
      <MenuItem Header="Preferences..." Command="{Binding ShowOptionsViewCommand}" />
      <Separator />
      <ItemsControl ItemsSource="{Binding RecentFiles}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding DisplayPath}"/>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
          <Style TargetType="MenuItem">
            <Setter Property="Command" Value="{Binding DataContext.OpenRecentFileCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
            <Setter Property="CommandParameter" Value="{Binding}"/>
          </Style>
        </ItemsControl.ItemContainerStyle>
      </ItemsControl>
      <Separator />
      <MenuItem Header="Exit" Command="{Binding CloseCommand}" />
    </MenuItem>
    

    注意简化的 数据模板 它只包含一个文本块, ItemContainerStyle 在生成的 菜单项 .

        2
  •  4
  •   Julien Poulin    12 年前

    我试着用 CompositeCollection 正如肯特·布加特建议的,但我不能让它工作,因为 bug in wpf 不允许在 CollectionContainer .

    我使用的解决方案是 RecentFiles 在绑定到集合的子菜单中 通过 这个 ItemsSource 财产。

    我真的想把名单放在“文件”菜单里,但我想这是下一个最好的选择…

    编辑

    受到启发 this article 我建立了一个习惯,更一般 MenuItemList 以下内容:

    public class MenuItemList : Separator {
    
      #region Private Members
    
      private MenuItem m_Parent;
      private List<MenuItem> m_InsertedMenuItems;
    
      #endregion
    
      public MenuItemList() {
        Loaded += (s, e) => HookFileMenu();
      }
    
      private void HookFileMenu() {
        m_Parent = Parent as MenuItem;
        if (m_Parent == null) {
          throw new InvalidOperationException("Parent must be a MenuItem");
        }
        if (ParentMenuItem == m_Parent) {
          return;
        }
        if (ParentMenuItem != null) {
          ParentMenuItem.SubmenuOpened -= _FileMenu_SubmenuOpened;
        }
        ParentMenuItem = m_Parent;
        ParentMenuItem.SubmenuOpened += _FileMenu_SubmenuOpened;
      }
    
      private void _FileMenu_SubmenuOpened(object sender, RoutedEventArgs e) {
        DataBind();
      }
    
      #region Properties
    
      public MenuItem ParentMenuItem { get; private set; }
    
      #region ItemsSource
    
      /// <summary>
      /// ItemsSource Dependency Property
      /// </summary>
      public static readonly DependencyProperty ItemsSourceProperty =
          DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MenuItemList),
              new FrameworkPropertyMetadata(null,
                  new PropertyChangedCallback(OnItemsSourceChanged)));
    
      /// <summary>
      /// Gets or sets a collection used to generate the content of the <see cref="MenuItemList"/>. This is a dependency property.
      /// </summary>
      public IEnumerable ItemsSource {
        get { return (IEnumerable) GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
      }
    
      /// <summary>
      /// Handles changes to the ItemsSource property.
      /// </summary>
      private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        ((MenuItemList) d).OnItemsSourceChanged(e);
      }
    
      /// <summary>
      /// Provides derived classes an opportunity to handle changes to the ItemsSource property.
      /// </summary>
      protected virtual void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e) {
        DataBind();
      }
    
      #endregion
    
      #region ItemContainerStyle
    
      /// <summary>
      /// ItemsContainerStyle Dependency Property
      /// </summary>
      public static readonly DependencyProperty ItemContainerStyleProperty =
          DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(MenuItemList),
              new FrameworkPropertyMetadata((Style) null));
    
      /// <summary>
      /// Gets or sets the <see cref="System.Windows.Style"/> that is applied to the container element generated for each item. This is a dependency property.
      /// </summary>
      public Style ItemContainerStyle {
        get { return (Style) GetValue(ItemContainerStyleProperty); }
        set { SetValue(ItemContainerStyleProperty, value); }
      }
    
      #endregion
    
      #endregion
    
      private void DataBind() {
        RemoveMenuItems();
        InsertMenuItems();
      }
    
      private void RemoveMenuItems() {
        if (m_InsertedMenuItems != null) {
          foreach (var menuItem in m_InsertedMenuItems) {
            ParentMenuItem.Items.Remove(menuItem);
          }
        }
      }
    
      private void InsertMenuItems() {
        if (ItemsSource == null) {
          return;
        }
        if (ParentMenuItem != null) {
          m_InsertedMenuItems = new List<MenuItem>();
          int iMenuItem = ParentMenuItem.Items.IndexOf(this);
          foreach (var item in ItemsSource) {
            var menuItem = new MenuItem();
            menuItem.DataContext = item;
            menuItem.Style = ItemContainerStyle;
            ParentMenuItem.Items.Insert(++iMenuItem, menuItem);
            m_InsertedMenuItems.Add(menuItem);
          }
        }
      }
    
    }
    

    它远不是完美的,但对我很有用。请对此发表评论…

        3
  •  1
  •   Community datashaman    7 年前

    尝试将HierarchicalDataTemplate与内部ContentPresenter一起使用。 Take a look at this SO answer for more details .

        4
  •  0
  •   David    6 年前

    上面提到的错误已经修复。注意,我已经将最近的文件列表和close命令混合在一起了。最近的文件列表在它自己的菜单中。这工作:

    <MenuItem Header="Recent Files" ItemsSource="{Binding RecentFiles}">
    <MenuItem.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding DisplayPath}" ToolTip="{Binding FullPath}" />
        </DataTemplate>
    </MenuItem.ItemTemplate>
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Command" Value="{Binding DataContext.SelectRecentFileCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
            <Setter Property="CommandParameter" Value="{Binding}"/>
            <Setter Property="IsChecked" Value="{Binding IsChecked}" />
        </Style>
    </MenuItem.ItemContainerStyle>
    </MenuItem>