如何在WPF中制作右键按钮上下文菜单?

9
我正在开发一个WPF错误记录应用程序,在这个应用程序中,当用户输入新的连接字符串时,连接按钮将被创建并显示在侧边栏上作为堆叠列表。
我想为那些连接按钮制定右键单击事件,以显示一个按钮上下文菜单,包括"查看(view)", "编辑(edit)"和"删除(delete)"选项。
我的MainWindow.xaml网格布局如下:
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="318*" />

        </Grid.ColumnDefinitions>
        <ScrollViewer VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled">
            <StackPanel Name="listConnections" Grid.Column="0" Background="#4682b4" Margin="0,0,0,-0.2" >
            </StackPanel>
        </ScrollViewer>

        </TabControl>
    </Grid> 

我正在MainWindow.xaml.cs中调用名为listConnectionsStackpanel,代码如下:

public MainWindow()
{
    InitializeComponent();

    GetUserData();
    //Button to create new connection
    listConnections.Children.Add(new NewConnectionButton(this));
    this.Closed += new EventHandler(MainWindow_Close);
}

WPF 右键单击事件 我尝试按照这个链接创建右键单击事件,但是对我来说并没有起作用。请有人能帮我解决一下吗?


3
在过深地学习使用代码后台编写WPF之前,考虑学习MVVM模式。使用MVVM模式让WPF的使用变得显著更容易。然而如果你以前从未使用过它,这将是一次相当大的思维转变,因此准备好需要一定的学习曲线。但是你以后会感谢自己的选择。 - Bradley Uffner
谢谢你提醒我。我对WPF非常陌生,很难理解.xaml和.xaml cs。但现在我只是卡在完成这个最终的WPF项目上。 - StraightUp
2个回答

8
我会在这里做以下事情:
  • 单独创建上下文菜单,并将其分配给UI上的每个“连接”对象
  • 处理每个菜单项的MenuItem.Click点击事件
  • 在列表中解析所选项目并相应处理
MVVM和所有这些当然很好,但是这种直接的方法至少是一个好的起点。
<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- Having CommandParameter is crucial here -->
        <ContextMenu x:Key="contextMenu">
            <MenuItem Header="View"
                      Click="View_OnClick"
                      CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
            <MenuItem Header="Edit"
                      Click="Edit_OnClick"
                      CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" />
            <MenuItem Header="Delete"
                      Click="Delete_OnClick"
                      CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
        </ContextMenu>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="318*" />
        </Grid.ColumnDefinitions>
        <ScrollViewer VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled">
            <StackPanel Name="listConnections" Grid.Column="0" Background="#4682b4" Margin="0,0,0,-0.2" >
                <Button Click="BtnAdd_OnClick">New Connection</Button>
            </StackPanel>
        </ScrollViewer>
    </Grid>
</Window>

代码后台:

using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication7
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private static Label FindClickedItem(object sender)
        {
            var mi = sender as MenuItem;
            if (mi == null)
            {
                return null;
            }

            var cm = mi.CommandParameter as ContextMenu;
            if (cm == null)
            {
                return null;
            }

            return cm.PlacementTarget as Label;
        }

        private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
        {
            listConnections.Children.Add(new Label
            {
                Content = "New Connection",
                ContextMenu = (ContextMenu)Resources["contextMenu"]
            });
        }

        private void View_OnClick(object sender, RoutedEventArgs e)
        {
            var clickedItem = FindClickedItem(sender);
            if (clickedItem != null)
            {
                MessageBox.Show(" Viewing: " + clickedItem.Content);
            }
        }

        private void Edit_OnClick(object sender, RoutedEventArgs e)
        {
            var clickedItem = FindClickedItem(sender);
            if (clickedItem != null)
            {
                string newName = "Connection edited on " + DateTime.Now.ToLongTimeString();
                string oldName = Convert.ToString(clickedItem.Content);
                clickedItem.Content = newName;
                MessageBox.Show(string.Format("Changed name from '{0}' to '{1}'", oldName, newName));
            }
        }

        private void Delete_OnClick(object sender, RoutedEventArgs e)
        {
            var clickedItem = FindClickedItem(sender);
            if (clickedItem != null)
            {
                string oldName = Convert.ToString(clickedItem.Content);
                listConnections.Children.Remove(clickedItem);
                MessageBox.Show(string.Format("Removed '{0}'", oldName));
            }
        }
    }
}

这是它的外观:

about to click edit

after clicking on edit

希望这可以帮到您。

致谢 kenwarner此回答中提到的关于MenuItem中CommandParameter的解决方案。 - alex.b
我不明白右键菜单是如何显示的。 - Scott Hutchinson

6
你应该将上下文菜单放在按钮的资源里面,像这样:
<NewConnectionButton.Resources>
    <ContextMenu x:Key="connectionButtonContext"  StaysOpen="true">
        <MenuItem Header="Add" Click="InternalAddButton_Click"/>
        <MenuItem Header="Delete" Click="InternalDeleteButton_Click"/>
        <MenuItem Header="Edit" Click="InternalEditButton_Click"/>
    </ContextMenu>
</NewConnectionButton.Resources>

这段代码应该放在NewConnectionButtonUserControl中。在UserControl的C#代码中,订阅这些事件并将它们暴露出来(InternalAddButton_ClickInternalDeleteButton_ClickInternalEditButton_Click),以供使用Button的人使用。然后,在您的MainWindow中订阅它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接