在wpf開發中,常常會使用到Menu和ContentMenu。可是原生的樣式比較簡陋,對於比較追求界面美好的人來講是十分不友好的。那麼,這就涉及到對Menu的樣式修改了。與此同時,咱們還但願Menu自動Binding到視圖數據模型上,根據數據項自動展開MenuItem。接下來就對這些想法作一簡單實現。前端
假設咱們的菜單項裏有描述意圖的縮略圖和文字須要展現。那麼咱們須要有名字和存有圖片路徑的屬性。額外的,還須要一個Children集合來存放子項,以造成樹形數據。框架
public class CommonTreeModel { /// <summary> /// 名字 /// </summary> public string Name { get; set; } /// <summary> /// 圖片的路徑 /// </summary> public string IconPath { get; set; } /// <summary> /// 子項 /// </summary> public ObservableCollection<CommonTreeModel> Children { get; set; } }
咱們使用Bogus
進行模擬數據的產生。使用nuget搜索Bogus添加便可。dom
/// <summary> /// 菜單項數據集,前端將binding到該屬性上 /// </summary> public ObservableCollection<CommonTreeModel> MenuTreeSource { get; set; } private void InitData() { var general = new Bogus.Faker<CommonTreeModel>() .RuleFor(t => t.Name, t => t.Commerce.Product())//名字:商業產品 .RuleFor(t => t.IconPath, t => t.Image.LoremFlickrUrl(32, 32));//圖片:使用LoremFlick網站的圖片 var rd = new Random(DateTime.Now.Millisecond);//隨機數 MenuTreeSource = GenerateTreeData(general, rd, 10, 3, 10); } private ObservableCollection<CommonTreeModel> GenerateTreeData(Faker<CommonTreeModel> faker, Random rd, int topCount, int subMin, int subMaxm, int level = 0, int levelLimit = 4) { var list = new ObservableCollection<CommonTreeModel>(faker.Generate(level == 0 ? topCount : rd.Next(subMin, subMaxm))); level++; if (level < levelLimit) { foreach (var item in list) { if (rd.Next() % 2 == 0) { item.Children = GenerateTreeData(faker, rd, topCount, subMin, subMaxm, level, levelLimit); } } } return list; }
到這裏,咱們生成了最大層級可能爲4的一棵樹:MenuTreeSource
。ide
咱們先寫前端Menu綁定到數據。網站
<Menu ItemsSource="{Binding MenuTreeSource}" HorizontalAlignment="Center"> <Menu.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <Label Content="{Binding Name}"></Label> </HierarchicalDataTemplate> </Menu.ItemTemplate> </Menu>
原始的樣式展現效果以下,總共有四級,而且第一級和後三級不一致。ui
咱們只需重寫MenuItem的樣式,就可以改變菜單的展現效果:spa
<Style TargetType="MenuItem"> <Setter Property="FontSize" Value="20"></Setter> <Setter Property="Foreground" Value="#b8d00a"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Grid> <Grid.Style> <Style TargetType="Grid"> <Setter Property="Background" Value="#b8d00a"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#f46a56"></Setter> </Trigger> </Style.Triggers> </Style> </Grid.Style> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="auto"></ColumnDefinition> </Grid.ColumnDefinitions> <Image Width="32" Height="32" Margin="5" Source="{Binding IconPath}" /> <Label Content="{Binding Name}" Margin="10 0" Grid.Column="1" VerticalContentAlignment="Center"/> <Label Name="MoreLbl" Content=">>" Grid.Column="2" VerticalContentAlignment="Center"> <Label.Style> <Style TargetType="Label"> <Setter Property="Visibility" Value="Visible"></Setter> <Style.Triggers> <DataTrigger Binding="{Binding Children}" Value="{x:Null}"> <Setter Property="Visibility" Value="Collapsed"></Setter> </DataTrigger> </Style.Triggers> </Style> </Label.Style> </Label> <Popup AllowsTransparency="True" IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false"> <Border x:Name="SubMenuBorder" BorderThickness="1" BorderBrush="Black"> <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/> </Border> </Popup> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
控件Popup
用於展現子級菜單項。其中StackPanel
上的IsItemsHost="True"
保證正確處理子級。設計
名爲MoreLbl的Label控件用於提示是否有子級。這裏簡略用>>標識一下,讀者能夠圖片Image或者Path控件作一個漂亮的樣式。code
最終效果以下,一級和次級依然不統一。xml
爲了統一,咱們須要對Menu的樣式加以調整。最簡單的方式是對子項承載容器進行替換,咱們用StackPanel
控件,並把內容排序設置爲垂直方向便可。
<Menu ItemsSource="{Binding MenuTreeSource}" HorizontalAlignment="Center"> <Menu.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical"></StackPanel> </ItemsPanelTemplate> </Menu.ItemsPanel> <Menu.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"></HierarchicalDataTemplate> </Menu.ItemTemplate> </Menu>
最終效果以下:
ContextMenu與Menu不一樣,他能夠做爲不少控件的Popup形式的鼠標右鍵彈出菜單,好比做爲ListBox,Label,Grid等的內容菜單都是能夠的,咱們只需鼠標右鍵就能夠將其彈出。
咱們用Label
簡單舉例:
<Label Content="鼠標右鍵彈出ContextMenu" HorizontalAlignment="Center"HorizontalContentAlignment="Center" FontSize="25" Background="#221a12"Foreground="#b8d00a" MouseDown="Label_MouseDown"> <Label.ContextMenu> <ContextMenu ItemsSource="{Binding MenuTreeSource}"> <ContextMenu.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}" /> </ContextMenu.ItemTemplate> </ContextMenu> </Label.ContextMenu> </Label>
以前的樣式都是全局樣式,默認會自動使用。效果以下:
可以發現一級的樣式稍微有些不同,咱們須要改ContextMenu
的樣式
<Style TargetType="{x:Type ContextMenu}"> <Setter Property="SnapsToDevicePixels" Value="True"/> <Setter Property="OverridesDefaultStyle" Value="True"/> <Setter Property="Grid.IsSharedSizeScope" Value="true"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ContextMenu}"> <Border BorderBrush="Black" BorderThickness="1" > <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
簡單重寫Template便可。
這裏只簡單介紹如何去重寫樣式模板來改變菜單展現效果。具體使用中你們須要更好看的效果請自行設計。該wpf項目框架使用dotnet core3.1版本。在farmwork中應該同樣。