WPF+MVVM案例实战(十五)- 实现一个下拉式菜单(上)

文章目录


1 案例效果

`提示

2、图标资源下载

阿里矢量素材官网下载需要的菜单图片,如下所示:

3、功能实现

1.文件创建

打开 Wpf_Examples 项目,在Views 文件夹下创建 窗体 DropDownMenuWindow.xaml 文件。这里我们拆解一下菜单结构,如下图所示

在WPF中菜单控件为 Menu 菜单项为 MenuItem。如果仅从菜单结构描述,那么如下所示即可实现三级结构。

csharp 复制代码
 <MenuItem Header="一级菜单" Width="80" Height="30">
     <MenuItem Header="二级菜单名1" Width="80" Height="30">
         <MenuItem Header="三级菜单" Width="80" Height="30">
         </MenuItem>
     </MenuItem>
     <MenuItem Header="二级菜单名2" Width="80" Height="30">
     </MenuItem>
     <MenuItem Header="二级菜单名3" Width="80" Height="30">
     </MenuItem>
 </MenuItem>

这就是三级菜单的框架了,我们要做的其实就是美化样式和点击效果了。

2、菜单原理分析

看了上面,我们明白了菜单就是 MenuItem 的嵌套,每一层就是 MenuItem 的样式实现,那么这个样式有规律吗,答案是有的。首先观察一级菜单,发现一级菜单的面板是在菜单的正下方出现,而二级菜单是在菜单项的右侧出现。每个菜单分两种情况,有子菜单项和无子菜单项两种情况。到这里我们已经明白了,菜单其实只有四种样式,一级菜单无子项样式、一级菜单有子项样式、二级菜单无子项样式和二级菜单有子项样式。而不管多少级菜单,只要是一级菜单就使用 一级菜单无子项样式或一级菜单有子项样式,二级及以上菜单就使用二级菜单无子项样式和二级菜单有子项样式。明白了以上道理,我们来逐个实现样式。

3、一级菜单两种样式实现

1、一级菜单无子项样式实现

首先样式按照下图分割:

整个样式 MenuItemStyle 实现如下:

csharp 复制代码
 <Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}">
     <Setter Property="Template">
         <Setter.Value>
             <ControlTemplate TargetType="{x:Type MenuItem}">
                 <Border x:Name="border" 
                         BorderThickness="0" 
                         CornerRadius="5"
                         Background="{TemplateBinding Background}"
                         Height="{TemplateBinding Height}"
                         Width="{TemplateBinding Width}" >
                     <Grid>
                         <ContentPresenter ContentSource="Icon" HorizontalAlignment="Left"
                                           VerticalAlignment="Center" Height="16"
                                           Width="16" Margin="8 0 0 0"/>
                         <ContentPresenter ContentSource="Header" HorizontalAlignment="Left"
                                           VerticalAlignment="Center" TextBlock.Foreground="{TemplateBinding Foreground}"
                                           TextBlock.FontFamily="{TemplateBinding FontFamily}"
                                           TextBlock.FontSize="{TemplateBinding FontSize}"
                                           Margin="30 0 0 0"/>
                     </Grid>

                 </Border>
                 <ControlTemplate.Triggers>
                     <Trigger Property="IsHighlighted" Value="True">
                         <Setter Property="Background" TargetName="border" Value="#524E4F"/>
                         <Setter Property="BorderBrush" TargetName="border" Value="#524E4F"/>
                     </Trigger>
                 </ControlTemplate.Triggers>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
 </Style>

结合图示,很容易就能理解每个样式属性的含义,最后加上样式触发效果,IsHighlighted 选中后背景色改变即可。

我们应用样式后效果如下:

csharp 复制代码
 <MenuItem Header="一级菜单" Width="100" Height="30" Style="{StaticResource MenuItemStyle}">
         <MenuItem.Icon>
             <Image Source="/Assets/Images/bianji.png" />
         </MenuItem.Icon>
     <MenuItem Header="二级菜单名1" Width="80" Height="30">
         <MenuItem Header="三级菜单" Width="80" Height="30">
         </MenuItem>
     </MenuItem>
     <MenuItem Header="二级菜单名2" Width="80" Height="30">
     </MenuItem>
     <MenuItem Header="二级菜单名3" Width="80" Height="30">
     </MenuItem>
 </MenuItem>

是不是有感觉了,由于样式是不带子项的,所以子项菜单在应用无子项菜单样式就没办法看到子项面板了。那么接下来实现有子项菜单的样式。

2、一级菜单有子项样式实现

分析一下有子项的菜单,其实就是在一级菜菜单下方增加一个弹出的面板。然后再弹出的面板上实现样式。粉衣及时不难发现应该和无子项样式差不多,只需要稍微改造一下,增加一个弹出面板。MenuItemStyleDropDown 示意图如下所示:

按照上述示意图,很容易在一级无子项菜单的样式上改造,改造后的代码如下:

csharp 复制代码
 <Style x:Key="MenuItemStyleDropDown" TargetType="MenuItem">
     <Setter Property="Template">
         <Setter.Value>
             <ControlTemplate TargetType="{x:Type MenuItem}">
                 <Border x:Name="border" Background="{TemplateBinding Background}"
                         BorderThickness="0" Height="{TemplateBinding Height}"
                         Width="{TemplateBinding Width}" CornerRadius="5">
                     <Grid>
                         <ContentPresenter ContentSource="Icon" HorizontalAlignment="Left"
                                           VerticalAlignment="Center" Height="16"
                                           Width="16" Margin="8 0 0 0"/>
                         <ContentPresenter ContentSource="Header" HorizontalAlignment="Left"
                                           VerticalAlignment="Center" TextBlock.Foreground="{TemplateBinding Foreground}"
                                           TextBlock.FontFamily="{TemplateBinding FontFamily}"
                                           TextBlock.FontSize="{TemplateBinding FontSize}"
                                           Margin="30 0 0 0"/>
                         <Image Source="/Assets/Images/small_down.png" Stretch="Uniform" HorizontalAlignment="Right"
                                VerticalAlignment="Center" Margin="0 0 10 0" Width="15" Height="15"/>
                         <Popup x:Name="PART_Popup" AllowsTransparency="True" IsOpen="{Binding IsSubmenuOpen,RelativeSource={RelativeSource TemplatedParent}}"
                                Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimation}}">
                             <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding Background}">
                                 <ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden">
                                     <Grid>
                                         <ItemsPresenter x:Name="ItemsPresenter"/>
                                     </Grid>
                                 </ScrollViewer>
                             </Border>
                         </Popup>
                     </Grid>

                 </Border>
                 <ControlTemplate.Triggers>
                     <Trigger Property="IsHighlighted" Value="True">
                         <Setter Property="Background" TargetName="border" Value="#524E4F"/>
                         <Setter Property="BorderBrush" TargetName="border" Value="#524E4F"/>
                     </Trigger>
                 </ControlTemplate.Triggers>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
 </Style>

对比如下所示:

想要二级菜单显示,我们需要把二级菜单样式写出来才能看到,这里我们和一级菜单逻辑一样,先实现无子项的菜单样式 ChildMenuItemStyle 。同样二级面板和一级的无子项样式类似,复制改造后 ChildMenuItemStyle 样式如下所示:

csharp 复制代码
 <Style x:Key="ChildMenuItemStyle" TargetType="{x:Type MenuItem}">
     <Setter Property="Template">
         <Setter.Value>
             <ControlTemplate TargetType="{x:Type MenuItem}">
                 <Border x:Name="border" 
                      BorderThickness="0"
                      Background="{Binding Path=Background,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Menu}}}"
                      Height="{TemplateBinding Height}"
                      Width="{TemplateBinding Width}" >
                     <Grid>
                         <ContentPresenter ContentSource="Icon" HorizontalAlignment="Left"
                                        VerticalAlignment="Center" Height="16"
                                        Width="16" Margin="8 0 0 0"/>
                         <ContentPresenter ContentSource="Header" HorizontalAlignment="Left"
                                        VerticalAlignment="Center" TextBlock.Foreground="{TemplateBinding Foreground}"
                                        TextBlock.FontFamily="{TemplateBinding FontFamily}"
                                        TextBlock.FontSize="{TemplateBinding FontSize}"
                                        Margin="30 0 0 0"/>
                         <!--菜单选中时左侧增加高亮竖线效果-->
                         <Grid x:Name="HeightLine" Height="{TemplateBinding Height}" Width="2" Background="White" HorizontalAlignment="Left" VerticalAlignment="Center" Visibility="Collapsed"/>
                     </Grid>

                 </Border>
                 <ControlTemplate.Triggers>
                     <Trigger Property="IsHighlighted" Value="True">
                         <Setter Property="Background" TargetName="border" Value="#524E4F"/>
                         <Setter Property="BorderBrush" TargetName="border" Value="#524E4F"/>
                         <Setter Property="Visibility" TargetName="HeightLine" Value="Visible"/>
                     </Trigger>
                 </ControlTemplate.Triggers>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
 </Style>

然后我们使用有子项菜单样式看看例子效果:

4、总结

到这,我么其实已经实现了一个二级菜单的全部功能,无非就是 MenuItem 的嵌套。对WPF上位机开发感兴趣的小伙伴可以点击关注和收藏,又想要实现的功能特效欢迎留言,本人不定时按照需求推出更多案例,下一节,将讲完整个下拉菜单的实现。

相关推荐
晚安苏州4 小时前
WPF DataTemplate 数据模板
wpf
甜甜不吃芥末1 天前
WPF依赖属性详解
wpf
Hat_man_1 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州2 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定
wangnaisheng2 天前
【WPF】RenderTargetBitmap的使用
wpf
dotent·3 天前
WPF 完美解决改变指示灯的颜色
wpf
orangapple5 天前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
ysdysyn5 天前
wpf mvvm 数据绑定数据(按钮文字表头都可以),根据长度进行换行,并把换行的文字居中
c#·wpf·mvvm
orangapple5 天前
WPF 使用LibVLCSharp.WPF实现视频播放、停止、暂停功能
wpf
晚安苏州5 天前
WPF ControlTemplate 控件模板
wpf