所以我有一个自定义类,旨在包含菜单项和子菜单项,格式如下:
public class ApplicationMenuItem
{
public ImageSource Image { get; set; }
public string Text { get; set; }
public string Tooltip { get; set; }
public ICollection<ApplicationMenuItem> Items { get; set; }
public EventHandler Clicked {get;set;}
public void Click(object sender, EventArgs e)
{
if (Clicked!=null)
Clicked(this, e);
}
public ApplicationMenuItem(string Text)
{
this.Text = Text;
Items = new List<ApplicationMenuItem>();
}
public ApplicationMenuItem()
{
Items = new List<ApplicationMenuItem>();
}
}
在任何人问我为什么不继承Menu
或只是创建一个Menu
对象并绑定它之前,因为这个类可能会用在不需要的平台和框架上必须使用Menu
UI对象,更不用说这个类会驱动导航菜单,上下文菜单,侧边栏,工具栏等....
我的问题是你可以看到我有一个自引用列表Items
以包含子菜单;绑定第一级菜单元素很容易,但是如何在为WPF中的元素创建模板时递归绑定子元素?
答案 0 :(得分:1)
这里是一个使用你的ApplicationMenuItem
类的递归XAML模板的一个例子,与你定义它完全一样(除了我把它放在名为Wobbles
的名称空间中)。这还没有完成,可释放的代码。但它演示了一个递归DataTemplate
,以及一些显示弹出窗口的奖励。您可以在菜单项类中添加IsEnabled
属性,并在XAML中使用设置颜色的附加触发器以及驱动SubmenuPopup.IsOpen
的多重触发器中的附加条件实现该属性。如果要支持水平分隔符,可以添加属性bool ApplicationMenuItem.IsSeparator
并为模板提供触发器,当该属性为True
时,该触发器将水平线替换为下面的网格内容。
RecursiveTemplate.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wbl="clr-namespace:Wobbles"
>
<DataTemplate DataType="{x:Type wbl:ApplicationMenuItem}">
<Grid
Name="RootGrid"
Background="BlanchedAlmond"
Height="Auto"
UseLayoutRounding="True"
SnapsToDevicePixels="True"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="24" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="{Binding Image}"
/>
<Label
Grid.Column="1"
Content="{Binding Text}"
/>
<Border
Name="PopupGlyphBorder"
Grid.Column="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{Binding ElementName=RootGrid, Path=Background}"
>
<Path
Height="10"
Width="5"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Data="M 0,0 L 5,5 L 0,10 Z"
Fill="Black"
/>
</Border>
<Popup
Name="SubmenuPopup"
PlacementTarget="{Binding ElementName=PopupGlyphBorder}"
Placement="Right"
StaysOpen="True"
>
<Border
BorderBrush="DarkGoldenrod"
BorderThickness="1"
>
<ItemsControl
Name="SubmenuItems"
ItemsSource="{Binding Items}"
/>
</Border>
</Popup>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="RootGrid" Property="Background" Value="Wheat" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition SourceName="SubmenuItems" Property="HasItems" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="SubmenuPopup" Property="IsOpen" Value="True" />
</MultiTrigger>
<Trigger SourceName="SubmenuItems" Property="HasItems" Value="False">
<Setter TargetName="PopupGlyphBorder" Property="Visibility" Value="Hidden" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
MainWindow.xaml
<Window
x:Class="RecursiveTemplate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wbl="clr-namespace:Wobbles"
Title="MainWindow"
Height="350"
Width="525"
>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="RecursiveTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<wbl:TestViewModel />
</Window.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<ContentControl
Content="{Binding Menu}"
Width="100"
Height="24"
/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
ViewModel.cs
namespace Wobbles
{
public class TestViewModel
{
public TestViewModel()
{
Menu = CreateMenu();
}
public Wobbles.ApplicationMenuItem Menu { get; protected set; }
protected Wobbles.ApplicationMenuItem CreateMenu()
{
var m = new Wobbles.ApplicationMenuItem("Menu");
var msub = new Wobbles.ApplicationMenuItem("Submenu");
msub.Items.Add(new Wobbles.ApplicationMenuItem("Sub Sub 1"));
msub.Items.Add(new Wobbles.ApplicationMenuItem("Sub Sub 2"));
// LOL
msub.Items.Add(msub);
m.Items.Add(msub);
m.Items.Add(new Wobbles.ApplicationMenuItem("Foo"));
m.Items.Add(new Wobbles.ApplicationMenuItem("Bar"));
m.Items.Add(new Wobbles.ApplicationMenuItem("Baz"));
return m;
}
}
}
Nits,Cavils,Kvetches和简短的Homily
使用XAML,我建议您尝试使用ObservableCollection<T>
代替List<T>
。如果在构建UI后集合中的项目发生更改,ObservableCollection<T>
将导致UI正确更新。出于同样的原因,您希望ApplicationMenuItem
实施INotifyPropertyChanged
。我还希望支持ICommand Command
属性以及Click
事件,并且我还要根据标准进一步命名Click
事件Click
XAML练习。
&#34; XAML会做什么?&#34;如果您尽最大努力编写可能被误认为与您正在使用的环境一起提供的标准库的代码,那么您几乎不会出错。