如何在WPF中创建具有Microsoft Web应用程序样式的菜单

时间:2021-10-28 21:02:32

We have been tasked with designing an enterprise application in WPF that will replace a battleship grey Winforms application with a modern look and feel.

我们的任务是在WPF中设计一个企业应用程序,它将取代具有现代外观的战舰灰色Winforms应用程序。

We like the look and feel that Microsoft web applications currently possess:

我们喜欢Microsoft Web应用程序目前拥有的外观和感觉:

如何在WPF中创建具有Microsoft Web应用程序样式的菜单

We could create these menus in WPF in the usual way:

我们可以通常的方式在WPF中创建这些菜单:

<DockPanel>
    <Menu DockPanel.Dock="Top">
        <MenuItem Header="_File">
            <MenuItem Header="_New" />
            <MenuItem Header="_Open" />
            <MenuItem Header="_Save" />
            <Separator />
            <MenuItem Header="_Exit" />
        </MenuItem>
    </Menu>
    <TextBox AcceptsReturn="True" />
</DockPanel>

But we'd get something that looks like a Winforms menu.

但是我们会得到一些看起来像Winforms菜单的东西。

I've seen some rather impressive styling efforts like this one, but they all seem to have the same familiar Winforms shape. I've also seen menus in libraries like MahApps Metro, but these strike us as too spartan.

我已经看到了一些像这样的令人印象深刻的造型努力,但它们似乎都具有相同熟悉的Winforms形状。我也看过像MahApps Metro这样的图书馆的菜单,但这些菜单让我们觉得太简单了。

Is the WPF Menu control flexible enough to be styled as shown in the picture above, or should we go another route like building a custom menu control from Stack Panels and Lists? What are the tradeoffs?

WPF菜单控件是否足够灵活,可以如上图所示进行样式设置,还是应该从堆栈面板和列表构建自定义菜单控件等其他路径?有什么权衡?

Bonus points (i.e. a bounty) will be awarded for xaml/code that does exactly this.

奖金积分(即赏金)将被授予xaml /代码,这正是这样做的。

An example of this menu style in action:
https://www.visualstudio.com/

此菜单样式的一个示例:https://www.visualstudio.com/

5 个解决方案

#1


5  

WPF was really designed with the possibility to change the look and behavior of every out-of-the-box mechanisms / controls (buttons, menus, treeview, etc.). So, in general, it's better to do this rather than to rewrite everything. For example, if you redesign your custom menu, you'll have to think about keyboard, UI automation, etc...

WPF的设计真的可以改变每个开箱即用的机制/控件(按钮,菜单,树视图等)的外观和行为。所以,一般来说,最好这样做而不是重写一切。例如,如果您重新设计自定义菜单,则必须考虑键盘,UI自动化等...

So, I gave a shot at your example and tried to build a minimum working sample - pure XAML - that mimics VS online menu (I've also added the hover background color change that's not by default in WPF's menu).

所以,我给你的例子做了一个镜头并尝试构建一个最小的工作样本 - 纯XAML - 模仿VS在线菜单(我还添加了悬浮背景颜色变化,默认情况下不在WPF的菜单中)。

Here is the result and, as you see, it looks very similar:

结果如下,如您所见,它看起来非常相似:

如何在WPF中创建具有Microsoft Web应用程序样式的菜单

Here is the XAML. I've chosen to use a custom control template per each MenuItem. I think it's quite practical when you need to really customize every item.

这是XAML。我选择为每个MenuItem使用自定义控件模板。我认为当你需要真正定制每件商品时它非常实用。

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="550" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="btv" />

        <!-- style a top level menu item -->
        <ControlTemplate x:Key="VsMenuTop" TargetType="MenuItem">
            <StackPanel TextBlock.FontSize="15px" Height="40">
                <!-- label, icons, etc. -->
                <Label Content="{TemplateBinding Header}" Margin="5" Foreground="White" />

                <!-- sub items -->
                <Popup IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False">
                    <Border BorderThickness="1" Background="White" BorderBrush="#E0E0E0">
                        <StackPanel IsItemsHost="True" />
                    </Border>
                </Popup>
                <StackPanel.Style>
                    <Style TargetType="StackPanel">
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver"  Value="True">
                                <Setter Property="Background" Value="#106EBE" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </StackPanel.Style>
            </StackPanel>
        </ControlTemplate>

        <!-- style a non-top level menu item -->
        <ControlTemplate x:Key="VsMenuSub" TargetType="MenuItem">
            <DockPanel TextBlock.FontSize="15px" x:Name="panel">
                <!-- label, icons, etc. -->
                <Image Source="{Binding Icon, RelativeSource={RelativeSource TemplatedParent}}" Width="20" Margin="5,0" />
                <Label Content="{TemplateBinding Header}" Foreground="Black" Margin="0,5,5,5" />

                <!-- draw the right arrow only if this menu item has sub items -->
                <Image Source="icon_right.png" Visibility="{Binding HasItems, Converter={StaticResource btv}, RelativeSource={RelativeSource TemplatedParent}}" />

                <!-- sub items -->
                <Popup IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False" Placement="Right" >
                    <Border BorderThickness="1" Background="White" BorderBrush="#E0E0E0">
                        <StackPanel IsItemsHost="True" />
                    </Border>
                </Popup>
            </DockPanel>
            <ControlTemplate.Triggers>
                <Trigger Property="IsHighlighted" Value="True">
                    <Setter Property="Background" TargetName="panel" Value="#EFF6FC" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

        <!-- style the separator -->
        <ControlTemplate x:Key="VsMenuSep" TargetType="Separator">
            <Border Height="1" Background="#E0E0E0" />
        </ControlTemplate>

        <!-- style the VSOnline -->
        <ControlTemplate x:Key="VsOnline" TargetType="MenuItem">
            <StackPanel TextBlock.FontSize="15px" Height="40" Orientation="Horizontal" Background="#005A9E">
                <Label Content="{TemplateBinding Header}" Margin="5" Foreground="White" />
                <Image Source="icon_down.png" Height="20" Margin="0,0,5,0" />

                <!-- sub items -->
                <Popup IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False">
                    <Border BorderThickness="1" Background="White" BorderBrush="#E0E0E0">
                        <StackPanel IsItemsHost="True" />
                    </Border>
                </Popup>
            </StackPanel>
        </ControlTemplate>

        <!-- some base stuff -->
        <Style TargetType="Menu">
            <Setter Property="Background" Value="#0078D7" />
            <Setter Property="Height" Value="40px" />
        </Style>

    </Window.Resources>

    <!-- the real app and real menu -->
    <StackPanel>
        <Menu IsMainMenu="True">
            <MenuItem Header="_VSOnline" Template="{StaticResource VsOnline}" >
                <MenuItem Header="_Whatever" Template="{StaticResource VsMenuSub}" />
            </MenuItem>
            <MenuItem Header="_Dashboards" Template="{StaticResource VsMenuTop}">
                <MenuItem Header="_Overview" Template="{StaticResource VsMenuSub}" />
            </MenuItem>
            <MenuItem Header="_Code" Template="{StaticResource VsMenuTop}">
                <MenuItem Header="_Files" Template="{StaticResource VsMenuSub}" />
                <MenuItem Header="_Commits" Template="{StaticResource VsMenuSub}" />
                <MenuItem Header="_Pushes" Template="{StaticResource VsMenuSub}" />
            </MenuItem>
            <MenuItem Header="_Work" Template="{StaticResource VsMenuTop}">
                <MenuItem Header="_Backlogs" Template="{StaticResource VsMenuSub}" Icon="icon_backlogs.png" />
                <MenuItem Header="_Queries" Template="{StaticResource VsMenuSub}" Icon="icon_queries.png" />
                <Separator Template="{StaticResource VsMenuSep}" />
                <MenuItem Header="_New Work Item" Template="{StaticResource VsMenuSub}">
                    <MenuItem Header="_Epic" Template="{StaticResource VsMenuSub}" Icon="icon_epic.png" />
                    <MenuItem Header="_Feature" Template="{StaticResource VsMenuSub}" Icon="icon_feature.png" />
                    <MenuItem Header="_Issue" Template="{StaticResource VsMenuSub}" Icon="icon_issue.png" />
                    <MenuItem Header="_Task" Template="{StaticResource VsMenuSub}" Icon="icon_task.png" />
                    <MenuItem Header="_Test Case" Template="{StaticResource VsMenuSub}" Icon="icon_testcase.png" />
                    <MenuItem Header="_User Story" Template="{StaticResource VsMenuSub}" Icon="icon_userstory.png" />
                </MenuItem>
                <MenuItem Header="_Bug" Template="{StaticResource VsMenuSub}" Icon="icon_bug.png" />
            </MenuItem>
        </Menu>
    </StackPanel>
</Window>

The project is available here on github.

该项目可在github上找到。

#2


3  

You are probably looking for some Wpf Theme to start with.

您可能正在寻找一些Wpf主题。

Such as http://brianlagunas.com/free-metro-light-and-dark-themes-for-wpf-and-silverlight-microsoft-controls/ or have a look at that SO answer: Any free WPF themes?

比如http://brianlagunas.com/free-metro-light-and-dark-themes-for-wpf-and-silverlight-microsoft-controls/或者看看那个SO答案:任何免费的WPF主题?

Then you probably would want to customize it even more with look and feel you are looking for.

然后你可能想要用你想要的外观和感觉来定制它。

#3


2  

You could use something like: http://materialdesigninxaml.net

你可以使用类似的东西:http://materialdesigninxaml.net

#4


2  

This is the default menu, but white, you can change the background color, text font, height, and everything else you want just by editing this style.

这是默认菜单,但是白色,您可以通过编辑此样式来更改背景颜色,文本字体,高度以及您想要的所有其他内容。

Here your MainWindow.xaml

在这里你的MainWindow.xaml

<Menu IsMainMenu="True" HorizontalContentAlignment="Left" Background="Transparent"   >
    <MenuItem Header="File" HorizontalContentAlignment="Left"  >
        <MenuItem Header="Menu Item 01" Command="{Binding MenuCommand}" CommandParameter="Param01"  >
            <MenuItem.Icon>
                <Image Source="Images/settings.png" Height="20"/>
            </MenuItem.Icon>
        </MenuItem>
        <MenuItem Header="Menu Item 02" Command="{Binding MenuCommand}" CommandParameter="Param02"  >
            <MenuItem.Icon>
                <Image Source="Images/settings.png" Height="20"/>
            </MenuItem.Icon>
        </MenuItem>
        <Separator />
        <MenuItem Header="LogOff"   Command="{Binding MenuCommand}" CommandParameter="LogOff" >
            <MenuItem.Icon>
                <Image Source="Images/logoff.png" Height="20"/>
            </MenuItem.Icon>
        </MenuItem>
    </MenuItem>
</Menu>

style in app.xaml file

app.xaml文件中的样式

<!-- Menu Begin -->
<!-- Menu --> 
<Style  TargetType="{x:Type Menu}">
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}"/>
    <Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.MenuFontFamilyKey}}"/>
    <Setter Property="FontSize" Value="{DynamicResource {x:Static SystemFonts.MenuFontSizeKey}}"/>
    <Setter Property="FontStyle" Value="{DynamicResource {x:Static SystemFonts.MenuFontStyleKey}}"/>
    <Setter Property="FontWeight" Value="{DynamicResource {x:Static SystemFonts.MenuFontWeightKey}}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Menu}">
                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- Level 0 -->
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelItemTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid x:Name="Grid" VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16" Visibility="Collapsed"/>
            <Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
            <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" >
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="White" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid x:Name="Grid1" RenderOptions.ClearTypeHint="Enabled">
                            <Canvas x:Name="Canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle x:Name="Rectangle" Fill="Transparent" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter"  KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- Level 0 -->
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid x:Name="Grid" VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16" Visibility="Collapsed"/>
            <Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
            <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" >
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.MenuColorKey}}" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid x:Name="Grid1" RenderOptions.ClearTypeHint="Enabled">
                            <Canvas x:Name="Canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle x:Name="Rectangle" Fill="Transparent" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter"  KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- Level 1,2,3,4,... -->
<ControlTemplate x:Key="{x:Static MenuItem.SubmenuHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid Margin="-1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition MinWidth="22" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="13"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="30"/>
                <ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
            <Border x:Name="GlyphPanel" BorderBrush="#FF26A0DA" BorderThickness="1" Background="#3D26A0DA" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22" >
                <Path x:Name="Glyph" Data="{DynamicResource ŧ}" Fill="#FF212121" FlowDirection="LeftToRight" Height="11" Width="9"/>
            </Border>
            <ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="2" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
            <TextBlock Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
            <Path x:Name="RightArrow" Grid.Column="5" Data="M0,0L4,3.5 0,7z" Fill="#FF212121" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Center"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" HorizontalOffset="-2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="-3">
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.MenuColorKey}}" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid RenderOptions.ClearTypeHint="Enabled">
                            <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <!--Fill="{Binding Background, ElementName=SubMenuBorder}" White  #FFF0F0F0-->
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="Transparent"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="Glyph" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="RightArrow" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
<!-- Level 1,2,3,4,... -->
<ControlTemplate x:Key="{x:Static MenuItem.SubmenuItemTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid Margin="-1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition MinWidth="22" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="13"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="30"/>
                <ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
            <Border x:Name="GlyphPanel" BorderBrush="#FF26A0DA" BorderThickness="1" Background="#3D26A0DA" ClipToBounds="False" HorizontalAlignment="Center" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22">
                <Path x:Name="Glyph" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="#FF212121" FlowDirection="LeftToRight" Height="11" Width="10"/>
            </Border>
            <ContentPresenter x:Name="menuHeaderContainer" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="2" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
            <TextBlock x:Name="menuGestureText" Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="Glyph" Value="#FF707070"/>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsHighlighted" Value="True"/>
                <Condition Property="IsEnabled" Value="False"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" TargetName="templateRoot" Value="#0A000000"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#21000000"/>
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- Menu End -->

#5


0  

I would create the menu with XAML

我会用XAML创建菜单

XAML of Page something like this:

页面的XAML是这样的:

<Window.Resources>
    <Style TargetType="TextBlock">
        <Setter Property="Margin" Value="10" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="Foreground" Value="White" />
    </Style>
</Window.Resources>
<Grid x:Name="Content">
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Background="CornflowerBlue">
            <TextBlock Text="Menu1" MouseLeftButtonDown="OnMouseLeftButtonDown" />
            <TextBlock Text="Menu2" MouseLeftButtonDown="OnMouseLeftButtonDown" />
            <TextBlock Text="Menu3" MouseLeftButtonDown="OnMouseLeftButtonDown" />
        </StackPanel>

        <Border Grid.Row="1" Background="Bisque"></Border>
    </Grid>
</Grid>

And in Code:

在代码中:

    private void OnMouseLeftButtonDown(object sender, MouseEventArgs e) {
        var textBlock = sender as TextBlock;
        if (textBlock == null) return;
        var relativePoint = VisualTreeHelper.GetOffset(textBlock);//textBlock.TransformToAncestor(Content).Transform(new Point(0, 0));

        //Almost invisible Background - If the user clicks it the menu is hidden
        var back = new Border{Background = new SolidColorBrush(Color.FromArgb(1,255,255,255))};
        back.MouseLeftButtonDown += (o, args) => {
            Content.Children.Remove(back.Tag as UIElement);
            Content.Children.Remove(back);
        };
        Content.Children.Add(back);

        //Build menu
        var menu = new Border{Background = new SolidColorBrush(Colors.White), Width = 200, Height = 500, BorderBrush = new SolidColorBrush(Colors.Gray), BorderThickness = new Thickness(1), HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top};
        back.Tag = menu;
        menu.RenderTransform = new TranslateTransform(relativePoint.X, relativePoint.Y + textBlock.ActualHeight + 5);
        //Do some more styling
        //Fill Menu here according to the tag of the TextBlock (switch(textBlock.Tag){...} or use a predefined view
        Content.Children.Add(menu);

        //Hide the Menu on click - TODO Implement logic to hide menu if a MenuItem was clicked...
        menu.MouseLeftButtonDown += (o, args) => {
            Content.Children.Remove(back.Tag as UIElement);
            Content.Children.Remove(back);
        };
    }

OK, this is a very basic example and needs a lot of improvement but I think you can work on it...

好的,这是一个非常基本的例子,需要很多改进,但我认为你可以继续...

#1


5  

WPF was really designed with the possibility to change the look and behavior of every out-of-the-box mechanisms / controls (buttons, menus, treeview, etc.). So, in general, it's better to do this rather than to rewrite everything. For example, if you redesign your custom menu, you'll have to think about keyboard, UI automation, etc...

WPF的设计真的可以改变每个开箱即用的机制/控件(按钮,菜单,树视图等)的外观和行为。所以,一般来说,最好这样做而不是重写一切。例如,如果您重新设计自定义菜单,则必须考虑键盘,UI自动化等...

So, I gave a shot at your example and tried to build a minimum working sample - pure XAML - that mimics VS online menu (I've also added the hover background color change that's not by default in WPF's menu).

所以,我给你的例子做了一个镜头并尝试构建一个最小的工作样本 - 纯XAML - 模仿VS在线菜单(我还添加了悬浮背景颜色变化,默认情况下不在WPF的菜单中)。

Here is the result and, as you see, it looks very similar:

结果如下,如您所见,它看起来非常相似:

如何在WPF中创建具有Microsoft Web应用程序样式的菜单

Here is the XAML. I've chosen to use a custom control template per each MenuItem. I think it's quite practical when you need to really customize every item.

这是XAML。我选择为每个MenuItem使用自定义控件模板。我认为当你需要真正定制每件商品时它非常实用。

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="550" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="btv" />

        <!-- style a top level menu item -->
        <ControlTemplate x:Key="VsMenuTop" TargetType="MenuItem">
            <StackPanel TextBlock.FontSize="15px" Height="40">
                <!-- label, icons, etc. -->
                <Label Content="{TemplateBinding Header}" Margin="5" Foreground="White" />

                <!-- sub items -->
                <Popup IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False">
                    <Border BorderThickness="1" Background="White" BorderBrush="#E0E0E0">
                        <StackPanel IsItemsHost="True" />
                    </Border>
                </Popup>
                <StackPanel.Style>
                    <Style TargetType="StackPanel">
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver"  Value="True">
                                <Setter Property="Background" Value="#106EBE" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </StackPanel.Style>
            </StackPanel>
        </ControlTemplate>

        <!-- style a non-top level menu item -->
        <ControlTemplate x:Key="VsMenuSub" TargetType="MenuItem">
            <DockPanel TextBlock.FontSize="15px" x:Name="panel">
                <!-- label, icons, etc. -->
                <Image Source="{Binding Icon, RelativeSource={RelativeSource TemplatedParent}}" Width="20" Margin="5,0" />
                <Label Content="{TemplateBinding Header}" Foreground="Black" Margin="0,5,5,5" />

                <!-- draw the right arrow only if this menu item has sub items -->
                <Image Source="icon_right.png" Visibility="{Binding HasItems, Converter={StaticResource btv}, RelativeSource={RelativeSource TemplatedParent}}" />

                <!-- sub items -->
                <Popup IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False" Placement="Right" >
                    <Border BorderThickness="1" Background="White" BorderBrush="#E0E0E0">
                        <StackPanel IsItemsHost="True" />
                    </Border>
                </Popup>
            </DockPanel>
            <ControlTemplate.Triggers>
                <Trigger Property="IsHighlighted" Value="True">
                    <Setter Property="Background" TargetName="panel" Value="#EFF6FC" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

        <!-- style the separator -->
        <ControlTemplate x:Key="VsMenuSep" TargetType="Separator">
            <Border Height="1" Background="#E0E0E0" />
        </ControlTemplate>

        <!-- style the VSOnline -->
        <ControlTemplate x:Key="VsOnline" TargetType="MenuItem">
            <StackPanel TextBlock.FontSize="15px" Height="40" Orientation="Horizontal" Background="#005A9E">
                <Label Content="{TemplateBinding Header}" Margin="5" Foreground="White" />
                <Image Source="icon_down.png" Height="20" Margin="0,0,5,0" />

                <!-- sub items -->
                <Popup IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False">
                    <Border BorderThickness="1" Background="White" BorderBrush="#E0E0E0">
                        <StackPanel IsItemsHost="True" />
                    </Border>
                </Popup>
            </StackPanel>
        </ControlTemplate>

        <!-- some base stuff -->
        <Style TargetType="Menu">
            <Setter Property="Background" Value="#0078D7" />
            <Setter Property="Height" Value="40px" />
        </Style>

    </Window.Resources>

    <!-- the real app and real menu -->
    <StackPanel>
        <Menu IsMainMenu="True">
            <MenuItem Header="_VSOnline" Template="{StaticResource VsOnline}" >
                <MenuItem Header="_Whatever" Template="{StaticResource VsMenuSub}" />
            </MenuItem>
            <MenuItem Header="_Dashboards" Template="{StaticResource VsMenuTop}">
                <MenuItem Header="_Overview" Template="{StaticResource VsMenuSub}" />
            </MenuItem>
            <MenuItem Header="_Code" Template="{StaticResource VsMenuTop}">
                <MenuItem Header="_Files" Template="{StaticResource VsMenuSub}" />
                <MenuItem Header="_Commits" Template="{StaticResource VsMenuSub}" />
                <MenuItem Header="_Pushes" Template="{StaticResource VsMenuSub}" />
            </MenuItem>
            <MenuItem Header="_Work" Template="{StaticResource VsMenuTop}">
                <MenuItem Header="_Backlogs" Template="{StaticResource VsMenuSub}" Icon="icon_backlogs.png" />
                <MenuItem Header="_Queries" Template="{StaticResource VsMenuSub}" Icon="icon_queries.png" />
                <Separator Template="{StaticResource VsMenuSep}" />
                <MenuItem Header="_New Work Item" Template="{StaticResource VsMenuSub}">
                    <MenuItem Header="_Epic" Template="{StaticResource VsMenuSub}" Icon="icon_epic.png" />
                    <MenuItem Header="_Feature" Template="{StaticResource VsMenuSub}" Icon="icon_feature.png" />
                    <MenuItem Header="_Issue" Template="{StaticResource VsMenuSub}" Icon="icon_issue.png" />
                    <MenuItem Header="_Task" Template="{StaticResource VsMenuSub}" Icon="icon_task.png" />
                    <MenuItem Header="_Test Case" Template="{StaticResource VsMenuSub}" Icon="icon_testcase.png" />
                    <MenuItem Header="_User Story" Template="{StaticResource VsMenuSub}" Icon="icon_userstory.png" />
                </MenuItem>
                <MenuItem Header="_Bug" Template="{StaticResource VsMenuSub}" Icon="icon_bug.png" />
            </MenuItem>
        </Menu>
    </StackPanel>
</Window>

The project is available here on github.

该项目可在github上找到。

#2


3  

You are probably looking for some Wpf Theme to start with.

您可能正在寻找一些Wpf主题。

Such as http://brianlagunas.com/free-metro-light-and-dark-themes-for-wpf-and-silverlight-microsoft-controls/ or have a look at that SO answer: Any free WPF themes?

比如http://brianlagunas.com/free-metro-light-and-dark-themes-for-wpf-and-silverlight-microsoft-controls/或者看看那个SO答案:任何免费的WPF主题?

Then you probably would want to customize it even more with look and feel you are looking for.

然后你可能想要用你想要的外观和感觉来定制它。

#3


2  

You could use something like: http://materialdesigninxaml.net

你可以使用类似的东西:http://materialdesigninxaml.net

#4


2  

This is the default menu, but white, you can change the background color, text font, height, and everything else you want just by editing this style.

这是默认菜单,但是白色,您可以通过编辑此样式来更改背景颜色,文本字体,高度以及您想要的所有其他内容。

Here your MainWindow.xaml

在这里你的MainWindow.xaml

<Menu IsMainMenu="True" HorizontalContentAlignment="Left" Background="Transparent"   >
    <MenuItem Header="File" HorizontalContentAlignment="Left"  >
        <MenuItem Header="Menu Item 01" Command="{Binding MenuCommand}" CommandParameter="Param01"  >
            <MenuItem.Icon>
                <Image Source="Images/settings.png" Height="20"/>
            </MenuItem.Icon>
        </MenuItem>
        <MenuItem Header="Menu Item 02" Command="{Binding MenuCommand}" CommandParameter="Param02"  >
            <MenuItem.Icon>
                <Image Source="Images/settings.png" Height="20"/>
            </MenuItem.Icon>
        </MenuItem>
        <Separator />
        <MenuItem Header="LogOff"   Command="{Binding MenuCommand}" CommandParameter="LogOff" >
            <MenuItem.Icon>
                <Image Source="Images/logoff.png" Height="20"/>
            </MenuItem.Icon>
        </MenuItem>
    </MenuItem>
</Menu>

style in app.xaml file

app.xaml文件中的样式

<!-- Menu Begin -->
<!-- Menu --> 
<Style  TargetType="{x:Type Menu}">
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}"/>
    <Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.MenuFontFamilyKey}}"/>
    <Setter Property="FontSize" Value="{DynamicResource {x:Static SystemFonts.MenuFontSizeKey}}"/>
    <Setter Property="FontStyle" Value="{DynamicResource {x:Static SystemFonts.MenuFontStyleKey}}"/>
    <Setter Property="FontWeight" Value="{DynamicResource {x:Static SystemFonts.MenuFontWeightKey}}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Menu}">
                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- Level 0 -->
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelItemTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid x:Name="Grid" VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16" Visibility="Collapsed"/>
            <Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
            <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" >
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="White" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid x:Name="Grid1" RenderOptions.ClearTypeHint="Enabled">
                            <Canvas x:Name="Canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle x:Name="Rectangle" Fill="Transparent" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter"  KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- Level 0 -->
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid x:Name="Grid" VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16" Visibility="Collapsed"/>
            <Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
            <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" >
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.MenuColorKey}}" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid x:Name="Grid1" RenderOptions.ClearTypeHint="Enabled">
                            <Canvas x:Name="Canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle x:Name="Rectangle" Fill="Transparent" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter"  KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- Level 1,2,3,4,... -->
<ControlTemplate x:Key="{x:Static MenuItem.SubmenuHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid Margin="-1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition MinWidth="22" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="13"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="30"/>
                <ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
            <Border x:Name="GlyphPanel" BorderBrush="#FF26A0DA" BorderThickness="1" Background="#3D26A0DA" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22" >
                <Path x:Name="Glyph" Data="{DynamicResource ŧ}" Fill="#FF212121" FlowDirection="LeftToRight" Height="11" Width="9"/>
            </Border>
            <ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="2" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
            <TextBlock Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
            <Path x:Name="RightArrow" Grid.Column="5" Data="M0,0L4,3.5 0,7z" Fill="#FF212121" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Center"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" HorizontalOffset="-2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="-3">
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.MenuColorKey}}" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid RenderOptions.ClearTypeHint="Enabled">
                            <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <!--Fill="{Binding Background, ElementName=SubMenuBorder}" White  #FFF0F0F0-->
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="Transparent"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="Glyph" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="RightArrow" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
<!-- Level 1,2,3,4,... -->
<ControlTemplate x:Key="{x:Static MenuItem.SubmenuItemTemplateKey}" TargetType="{x:Type MenuItem}">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid Margin="-1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition MinWidth="22" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="13"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="30"/>
                <ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
            <Border x:Name="GlyphPanel" BorderBrush="#FF26A0DA" BorderThickness="1" Background="#3D26A0DA" ClipToBounds="False" HorizontalAlignment="Center" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22">
                <Path x:Name="Glyph" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="#FF212121" FlowDirection="LeftToRight" Height="11" Width="10"/>
            </Border>
            <ContentPresenter x:Name="menuHeaderContainer" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="2" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
            <TextBlock x:Name="menuGestureText" Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="Glyph" Value="#FF707070"/>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsHighlighted" Value="True"/>
                <Condition Property="IsEnabled" Value="False"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" TargetName="templateRoot" Value="#0A000000"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#21000000"/>
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- Menu End -->

#5


0  

I would create the menu with XAML

我会用XAML创建菜单

XAML of Page something like this:

页面的XAML是这样的:

<Window.Resources>
    <Style TargetType="TextBlock">
        <Setter Property="Margin" Value="10" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="Foreground" Value="White" />
    </Style>
</Window.Resources>
<Grid x:Name="Content">
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Background="CornflowerBlue">
            <TextBlock Text="Menu1" MouseLeftButtonDown="OnMouseLeftButtonDown" />
            <TextBlock Text="Menu2" MouseLeftButtonDown="OnMouseLeftButtonDown" />
            <TextBlock Text="Menu3" MouseLeftButtonDown="OnMouseLeftButtonDown" />
        </StackPanel>

        <Border Grid.Row="1" Background="Bisque"></Border>
    </Grid>
</Grid>

And in Code:

在代码中:

    private void OnMouseLeftButtonDown(object sender, MouseEventArgs e) {
        var textBlock = sender as TextBlock;
        if (textBlock == null) return;
        var relativePoint = VisualTreeHelper.GetOffset(textBlock);//textBlock.TransformToAncestor(Content).Transform(new Point(0, 0));

        //Almost invisible Background - If the user clicks it the menu is hidden
        var back = new Border{Background = new SolidColorBrush(Color.FromArgb(1,255,255,255))};
        back.MouseLeftButtonDown += (o, args) => {
            Content.Children.Remove(back.Tag as UIElement);
            Content.Children.Remove(back);
        };
        Content.Children.Add(back);

        //Build menu
        var menu = new Border{Background = new SolidColorBrush(Colors.White), Width = 200, Height = 500, BorderBrush = new SolidColorBrush(Colors.Gray), BorderThickness = new Thickness(1), HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top};
        back.Tag = menu;
        menu.RenderTransform = new TranslateTransform(relativePoint.X, relativePoint.Y + textBlock.ActualHeight + 5);
        //Do some more styling
        //Fill Menu here according to the tag of the TextBlock (switch(textBlock.Tag){...} or use a predefined view
        Content.Children.Add(menu);

        //Hide the Menu on click - TODO Implement logic to hide menu if a MenuItem was clicked...
        menu.MouseLeftButtonDown += (o, args) => {
            Content.Children.Remove(back.Tag as UIElement);
            Content.Children.Remove(back);
        };
    }

OK, this is a very basic example and needs a lot of improvement but I think you can work on it...

好的,这是一个非常基本的例子,需要很多改进,但我认为你可以继续...