如何实现“超级菜单”在 WPF 中?

发布于 2024-10-30 14:56:08 字数 375 浏览 4 评论 0原文

我正在尝试使用 WPF 实现“Mega Menu”样式菜单。要查看网页设计中的大型菜单示例,请参阅此处

到目前为止,我已经尝试通过使用 TextBlocks 作为菜单的最高级别来创建类似的界面,然后使用鼠标悬停事件来显示位于文本块下方的附加窗口。这是麻烦且不灵活的,将来的更改将需要动态添加/删除 TextBlock。

我考虑过使用 WPF 菜单控件,因为我知道样式可以进行显着修改,但我还没有看到任何方法可以使用菜单控件使用的分层模型生成多列布局。

有更好的方法吗?我是否必须坚持使用自定义窗口和相对定位?有人可以给我举一个已经实施的例子吗?

I'm trying to implement "Mega Menu" style menus using WPF. To see examples of mega menus in web design, see here.

So far, I've tried creating a similar interface by using TextBlocks as the highest level of the menu, and then using the mouse hover event to display an additional window that appears positioned below the text block. This is cumbersome and inflexible, future changes would require adding/removing TextBlocks dynamically.

I have considered using the WPF Menu control, because I know the styles can be dramatically modified, but I haven't seen any way to produce multi-column layouts with the hierarchical model that the Menu control uses.

Is there a better way to do this? Am I going to have to stick with custom windows and relative positioning? Can someone point me to an example of this that has already been implemented?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

因为看清所以看轻 2024-11-06 14:56:08

您可以使用 Popup 控件,而不是使用自定义窗口和定位。您可以使用 StaysOpen=false 设置在用户单击屏幕外时将其关闭。

如果您可以满足于单击菜单项而不是悬停,则以下自定义控件将起作用:

[TemplatePart(Name="PART_HoverArea", Type=typeof(FrameworkElement))]
[TemplatePart(Name="PART_Popup", Type=typeof(Popup))]
public class MegaMenuItem : HeaderedContentControl
{
    private FrameworkElement hoverArea;
    private Popup popup;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // Unhook old template
        if (hoverArea != null)
        {
            hoverArea.PreviewMouseUp -= ShowPopupOnMouseDown;
        }

        hoverArea = null;
        popup = null;

        if (Template == null)
            return;

        // Hook up new template
        hoverArea = (FrameworkElement)Template.FindName("PART_HoverArea", this);
        popup = (Popup)Template.FindName("PART_Popup", this);
        if (hoverArea == null || popup == null)
            return;

        hoverArea.PreviewMouseUp += ShowPopupOnMouseDown;
    }

    private void ShowPopupOnMouseDown(object sender, MouseEventArgs e)
    {
        popup.PlacementTarget = hoverArea;
        popup.Placement = PlacementMode.Bottom;
        popup.StaysOpen = false;
        popup.IsOpen = true;
    }
}

您需要一个样式来显示它 - 类似这样。请注意 PART_ 模板部分名称:

<Style TargetType="WpfApplication14:MegaMenuItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="WpfApplication14:MegaMenuItem">
                <Grid>
                    <Border Name="PART_HoverArea" Background="#fb9c3b" BorderBrush="White" BorderThickness="0,0,1,0">
                        <ContentPresenter Content="{TemplateBinding Header}" />
                    </Border>

                    <Popup 
                        Name="PART_Popup" 
                        PlacementTarget="{Binding ElementName=HoverArea}"
                        >
                        <Border MinWidth="100" MaxWidth="400" MinHeight="40" MaxHeight="200" Background="#0d81c3">
                            <ContentPresenter Content="{TemplateBinding Content}" />
                        </Border>
                    </Popup>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>    
</Style>

菜单的 XAML 将是:

<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
    <WpfApplication14:MegaMenuItem Header="Parent 1">
        <WrapPanel Margin="5">
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
        </WrapPanel>
    </WpfApplication14:MegaMenuItem>
    <WpfApplication14:MegaMenuItem Header="Parent 2">
        <WrapPanel Margin="5">
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
        </WrapPanel>
    </WpfApplication14:MegaMenuItem>
</StackPanel>

让菜单在悬停时显示要困难得多,因为弹出窗口会窃取焦点(您可以显示菜单,但如果它们出现,则无法轻松隐藏它)将鼠标悬停在另一个菜单上)。为此,自定义窗口可能会更好。

Instead of using custom Windows and positioning, you could use a Popup control. Your can use the StaysOpen=false setting to have it close when the user clicks off-screen.

If you can settle for clicking a menu item instead of hovering, the following custom control will work:

[TemplatePart(Name="PART_HoverArea", Type=typeof(FrameworkElement))]
[TemplatePart(Name="PART_Popup", Type=typeof(Popup))]
public class MegaMenuItem : HeaderedContentControl
{
    private FrameworkElement hoverArea;
    private Popup popup;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // Unhook old template
        if (hoverArea != null)
        {
            hoverArea.PreviewMouseUp -= ShowPopupOnMouseDown;
        }

        hoverArea = null;
        popup = null;

        if (Template == null)
            return;

        // Hook up new template
        hoverArea = (FrameworkElement)Template.FindName("PART_HoverArea", this);
        popup = (Popup)Template.FindName("PART_Popup", this);
        if (hoverArea == null || popup == null)
            return;

        hoverArea.PreviewMouseUp += ShowPopupOnMouseDown;
    }

    private void ShowPopupOnMouseDown(object sender, MouseEventArgs e)
    {
        popup.PlacementTarget = hoverArea;
        popup.Placement = PlacementMode.Bottom;
        popup.StaysOpen = false;
        popup.IsOpen = true;
    }
}

You would need a style to display it - something like this. Note the PART_ template part names:

<Style TargetType="WpfApplication14:MegaMenuItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="WpfApplication14:MegaMenuItem">
                <Grid>
                    <Border Name="PART_HoverArea" Background="#fb9c3b" BorderBrush="White" BorderThickness="0,0,1,0">
                        <ContentPresenter Content="{TemplateBinding Header}" />
                    </Border>

                    <Popup 
                        Name="PART_Popup" 
                        PlacementTarget="{Binding ElementName=HoverArea}"
                        >
                        <Border MinWidth="100" MaxWidth="400" MinHeight="40" MaxHeight="200" Background="#0d81c3">
                            <ContentPresenter Content="{TemplateBinding Content}" />
                        </Border>
                    </Popup>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>    
</Style>

The XAML for your menu would then be:

<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
    <WpfApplication14:MegaMenuItem Header="Parent 1">
        <WrapPanel Margin="5">
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
        </WrapPanel>
    </WpfApplication14:MegaMenuItem>
    <WpfApplication14:MegaMenuItem Header="Parent 2">
        <WrapPanel Margin="5">
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
            <TextBlock Text="Put any content you want here" Margin="5" />
        </WrapPanel>
    </WpfApplication14:MegaMenuItem>
</StackPanel>

Making the menu appear on hover is much harder, because of the way Popups steal focus (you can show the menu, but you can't easily hide it if they mouse over another menu). For that a custom window might work better.

悲歌长辞 2024-11-06 14:56:08

您可以使用 HeaderedItemsControl 并更换 Panel 以满足您的需求;默认情况下,它使用 StackPanelWrapPanel 可能更适合您。弹出和鼠标悬停行为默认情况下不存在,需要实现。

更多 稳健的方法是利用自定义的扩展器;因为它提供了您想要的弹出行为,并且链接到演练提供了鼠标悬停行为。

You could use a HeaderedItemsControl and swap out the Panel to suit your needs; by default it uses a StackPanel however a WrapPanel may suit you better. The pop out and mouse over behavior do not exist by default and would need to be implemented.

A more robust approach would be to leverage a custom Expander; as it provides the pop out behavior you are after and the linked to walkthrough provides the mouse over behavior.

自由如风 2024-11-06 14:56:08

我想知道是否可以改装 Ribbon 控件来做到这一点?它提供选项卡、标签、列等等。

请谨慎使用此 UI 设计,并确保它仅在用户明确请求时打开和关闭。当我正在查看的网站上出现弹出式巨型菜单时,我无法将其关闭,除非我想单击它,然后它就会消失,这非常烦人。

I wonder if the Ribbon control can be retrofitted to do this? It provides tabs, labels, columns and all that.

Please use this UI design sparingly and make sure that it only opens and closes when the user specifically requests such. It's tremendously annoying when a popup mega-menu appears over a website I'm viewing, and I can't get it to close, except for when I want to click on it and it goes away.

孤君无依 2024-11-06 14:56:08

自定义窗口和相对位置本质上是 WPF Menu/MenuItem 控件的工作方式...但正如您所发现的,它并不简单。最好的办法是重新模板化 Menu/MenuItem 控件以满足您的需求。

Custom windows and relative position are essentially how the WPF Menu/MenuItem control works... but as you've found, it's non-trivial. Best bet would be to retemplate the Menu/MenuItem controls to meet your need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文