我有一个'文件'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>
然而,当我使用这段代码时,MenuItem
周围有一个奇怪的偏移,看起来周围有一个容器。我怎么能摆脱它?
以下是它的外观截图:
答案 0 :(得分:6)
“奇怪的偏移量”是MenuItem
。父MenuItem
已经为您生成了一个孩子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>
。试试这个:
DataTemplate
请注意仅包含TextBlock的简化ItemContainerStyle
,以及在生成的MenuItem
上设置属性的{{1}}。
答案 1 :(得分:4)
我尝试使用Kent Boogaart建议的CompositeCollection
,但由于bug in wpf不允许在CollectionContainer
中使用RelativeSource绑定,我无法使其工作。< / p>
我使用的解决方案是让自己的子菜单中的RecentFiles
绑定到Collection ,通过 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);
}
}
}
}
它远非完美,但它对我有用。随意评论它......
答案 2 :(得分:1)
尝试使用带有内部ContentPresenter的HierarchicalDataTemplate。 Take a look at this SO answer for more details
答案 3 :(得分:0)
上面提到的错误已得到修复。请注意,我没有将最近的文件列表与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>