【问题标题】:How to open a popup menu when a button is clicked?单击按钮时如何打开弹出菜单?
【发布时间】:2012-01-22 04:26:00
【问题描述】:

我在工具栏中有一个带有图像作为其内容的按钮。我希望此按钮在单击时打开其下方的菜单。怎么样?

<Toolbar>
            <Button>
                <Button.Content>
                    <Image  Source="../Resources/help.png"></Image>
                </Button.Content>
            </Button>
</Toolbar>

谢谢!!

【问题讨论】:

  • 你有没有……尝试过什么?

标签: wpf


【解决方案1】:

您可以使用附加属性或行为来实现下拉按钮功能,而不是使用子类Button,以获得更类似于 WPF 的方法,因此您不会影响按钮样式:

using System.Windows.Interactivity;

public class DropDownButtonBehavior : Behavior<Button>
{
    private bool isContextMenuOpen;

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.AddHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click), true);
    }

    void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        Button source = sender as Button;
        if (source != null && source.ContextMenu != null)
        {
            if (!isContextMenuOpen)
            {
                // Add handler to detect when the ContextMenu closes
                source.ContextMenu.AddHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed), true);
                // If there is a drop-down assigned to this button, then position and display it 
                source.ContextMenu.PlacementTarget = source;
                source.ContextMenu.Placement = PlacementMode.Bottom;
                source.ContextMenu.IsOpen = true;
                isContextMenuOpen = true;
            }
        }            
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.RemoveHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click));
    }

    void ContextMenu_Closed(object sender, RoutedEventArgs e)
    {
        isContextMenuOpen = false;
        var contextMenu = sender as ContextMenu;
        if (contextMenu != null)
        {
            contextMenu.RemoveHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed));
        }
    }
}

用法:

<!-- NOTE: xmlns:i="schemas.microsoft.com/expression/2010/interactivity‌​" -->
<Button>
    <i:Interaction.Behaviors>
        <local:DropDownButtonBehavior/>
    </i:Interaction.Behaviors>
    <Button.Content>
        <StackPanel Orientation="Horizontal">
            <Image Source="/DropDownButtonExample;component/Assets/add.png" SnapsToDevicePixels="True" Height="16" Width="16" />
            <TextBlock Text="Add"/>
            <Separator Margin="2,0">
                <Separator.LayoutTransform>
                    <TransformGroup>
                        <TransformGroup.Children>
                            <TransformCollection>
                                <RotateTransform Angle="90"/>
                            </TransformCollection>
                        </TransformGroup.Children>
                    </TransformGroup>
                </Separator.LayoutTransform>
            </Separator>
            <Path Margin="2" VerticalAlignment="Center" Width="6" Fill="#FF527DB5" Stretch="Uniform" HorizontalAlignment="Right" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "/>
        </StackPanel>
    </Button.Content>
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Attribute"/>
            <MenuItem Header="Setting"/>
            <Separator/>
            <MenuItem Header="Property"/>
        </ContextMenu>
    </Button.ContextMenu>
</Button>

当前要点来源和示例here。

【讨论】:

  • 这应该被标记为答案,很好的解决方案!
  • 我希望它在点击时打开并在点击时再次关闭,就像 Visual Studio 的拆分按钮上的下拉菜单一样。每次单击时都会打开此实现。我尝试设置 IsOpen = !IsOpen,并更改事件触发的时间(例如在 PreviewMouseDown 上),但似乎上下文菜单在到达点击事件之前已经关闭。你能解开这个谜吗?我什至不确定它是否可以在行为中完成。
  • 行为 在“System.Windows.Interactivity”中
  • @MarqueIV 感谢 cmets。 isContextMenuOpen 实际上更接近 isHandlerAttached 以避免重新附加 Click(或 PreviewMouseDown)事件处理程序。我用PreviewMouseDown 做了一个快速测试,它似乎和你说的完全一样。我会尽快更新答案和要点。再次感谢。
  • 我不得不从 nuget 中获取 Microsoft.Xaml.Behaviors.Wpf 并改用 xmlns:i="http://schemas.microsoft.com/xaml/behaviors"。 (来自stackoverflow.com/a/37906343/438013.)但否则效果很好!
【解决方案2】:

如果您有幸以 .NET 4 或更高版本为目标,新的功能区库有一个 RibbonMenuButton 可以做到这一点。在 4.5 中,就像在项目中引用 System.Windows.Controls.Ribbon 一样简单:

<RibbonMenuButton x:Name="ExampleMenu" SmallImageSource="/Images/Example.png">
    <RibbonMenuItem x:Name="ExampleMenuItem" Header="Save" />
</RibbonMenuButton>

【讨论】:

【解决方案3】:

我搜索后找到了这两个解决方案:

1) Split Button in WPF

2) DropDownButtons in WPF

第二个解决方案是我最喜欢的(来源取自 Andrew Wilkinson 的网站)

public class DropDownButton : ToggleButton
{
  // *** Dependency Properties ***

  public static readonly DependencyProperty DropDownProperty =
    DependencyProperty.Register("DropDown",
                                typeof(ContextMenu),
                                typeof(DropDownButton),
                                new UIPropertyMetadata(null));

  // *** Constructors *** 

  public DropDownButton() {
    // Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property 

    Binding binding = new Binding("DropDown.IsOpen");
    binding.Source = this;
    this.SetBinding(IsCheckedProperty, binding);
  }

  // *** Properties *** 

  public ContextMenu DropDown {
    get { return (ContextMenu)this.GetValue(DropDownProperty); }
    set { this.SetValue(DropDownProperty, value); }
  }

  // *** Overridden Methods *** 

  protected override void OnClick() {
    if (this.DropDown != null) {
      // If there is a drop-down assigned to this button, then position and display it 

      this.DropDown.PlacementTarget = this;
      this.DropDown.Placement = PlacementMode.Bottom;

      this.DropDown.IsOpen = true;
    }
  }
}

用法

<ctrl:DropDownButton Content="Drop-Down">
  <ctrl:DropDownButton.DropDown>
    <ContextMenu>
      <MenuItem Header="Item 1" />
      <MenuItem Header="Item 2" />
      <MenuItem Header="Item 3" />
    </ContextMenu>
  </ctrl:DropDownButton.DropDown>
</ctrl:DropDownButton>

希望对你有所帮助...

【讨论】:

  • 这种方法不像 WPF - 应该使用附加属性,而不是子类化。原因:1. 样式不再起作用 2. 您只能从一个类派生,但在同一个对象上有许多不同的附加属性
  • 作为一个 WPF 初学者,这些也很难上手。缺少太多信息。
  • 与“Command”一起使用时,将“CommandParameter”设置为 ContextMenu:
【解决方案4】:

有很多方法可以完成这项工作,您可以考虑这种方法...

<ToolBar DockPanel.Dock="Top">
    <MenuItem IsSubmenuOpen="{Binding SomeProperty}">
        <MenuItem.Header>
            <Button Height="28">
                <Button.Content>
                    <Image Source="---your image---"></Image>
                </Button.Content>
            </Button>
        </MenuItem.Header>
        <Menu>
            <MenuItem Header="Do this" />
            <MenuItem Header="Do that"/>
        </Menu>
    </MenuItem>
</ToolBar>

这会将您的按钮包装到具有子菜单的MenuItem 中。如此处所示,名为 IsSubMenuOpen 的 MenuItem 属性绑定到 ViewModel 中名为 SomeProperty 的 bool 类型的通知属性。

您必须让 ViewModel 根据您实际尝试执行的操作来切换此属性。您可能需要考虑将您的按钮设置为切换按钮,以便于关闭子菜单,否则您将不得不在 ViewModel 中连接其他行为。

【讨论】:

    猜你喜欢
    • 2017-09-07
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多