2009-09-09 11 views
6

Tôi có 'Tệp' MenuItem là tôi muốn hiển thị danh sách các tệp đã mở gần đây.Làm thế nào tôi có thể kết hợp một danh sách dữ liệu bị ràng buộc của MenuItems đến một MenuItem khác trong WPF?

Đây là XAML tôi có bây giờ:

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

Tuy nhiên, khi tôi sử dụng mã này, có một lạ bù đắp xung quanh MenuItem s và có vẻ như có một container xung quanh họ. Làm thế nào tôi có thể thoát khỏi điều đó?

Đây là một ảnh chụp màn hình của những gì nó trông giống như:

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

+0

Bài viết trên blog này cho biết cách tạo menu theo hướng dữ liệu: http://weblogs.asp.net/okloeten/archive/2007/11/14/5149692.aspx –

Trả lời

4

Tôi đã thử sử dụng CompositeCollection như đề xuất của Kent Boogaart, nhưng tôi không thể làm cho nó hoạt động vì bug in wpf không cho phép sử dụng liên kết RelativeSource trong CollectionContainer.

Giải pháp tôi đã sử dụng là có RecentFiles trong menu phụ riêng của nó được liên kết với Bộ sưu tập qua thuộc tính ItemsSource.

Tôi thực sự muốn có danh sách trong menu 'File' nhưng tôi đoán đây là điều tốt nhất tiếp theo ...

Sửa

Lấy cảm hứng từ this article tôi đã xây dựng một phong tục và tổng quát hơn 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); 
     } 
    } 
    } 

} 

Nó hoàn hảo nhưng hoàn toàn phù hợp với tôi. Hãy bình luận về nó ...

1

Hãy thử sử dụng một HierarchicalDataTemplate với một ContentPresenter nội bộ để thay thế. Take a look at this SO answer for more details.

+0

SO câu trả lời bạn đã liên kết thực sự là cho một menu phụ. Những gì tôi muốn là danh sách năng động là một phần của một menu không động ... Bạn có ý tưởng nào khác không? –

+0

Vì vậy, về cơ bản bạn muốn kết hợp một số MenuItems động thành một Menu được định nghĩa tĩnh? AFAIK, khi bạn làm databinding, hoặc bạn ràng buộc một ItemsSource hoàn chỉnh, hoặc bạn xây dựng các nội dung bằng tay (hoặc trong XAML hoặc từ mã). Những gì tôi sẽ làm là để làm cho toàn bộ menu databound đến một MenuItemViewModel, nhưng sau đó hãy chắc chắn thông qua API mà chỉ có danh sách năng động đã được chỉnh sửa. –

6

"Độ lệch bù trừ" là MenuItem. Phụ huynh MenuItem đã tạo một đứa trẻ MenuItem cho bạn, nhưng số DataTemplate của bạn sẽ thêm một con thứ hai. Hãy thử điều này:

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

Lưu ý đơn giản DataTemplate mà chỉ chứa một TextBlock, và ItemContainerStyle để thiết lập các thuộc tính trên tạo MenuItem.

+0

Điều này có vẻ đầy hứa hẹn nhưng tôi nhận được một ngoại lệ cho biết * Không thể áp dụng kiểu dành cho loại 'MenuItem' cho loại 'ContentPresenter' *. Bạn có một ý tưởng làm thế nào để sửa chữa điều đó? –

+0

Không đọc mã của bạn đúng cách. Bạn không thể kết hợp các mục được tạo với không được tạo. Loại bỏ các ItemsControl và ràng buộc các MenuItem cấp cao thay vào đó.Chỉ cần gắn các mục không được tạo ra như Preferences trong bộ sưu tập các mục menu, hoặc sử dụng CompositeCollection để giữ chúng riêng biệt. –

+0

Tôi đã thử sử dụng một CompositeCollection nhưng tôi không thể ràng buộc một CollectionContainer với máy ảo của tôi vì một lỗi trong wpf. Tôi không muốn đặt MenuItem khác trong danh sách bởi vì họ không thuộc về đó. Vì vậy, cuối cùng, tôi vừa tạo ra một menu phụ "Recent Files" (tôi đã có nó làm việc nhưng tôi thực sự muốn có danh sách trong menu 'File'). –