有没有办法为 Winforms reportViewer 控件实现自定义上下文菜单?

Is there a way to implement a custom context menu for the Winforms reportViewer control?

我正在尝试为 WinForms.ReportViewer 控件实现自定义上下文菜单。如果用户在控件最顶部的 ReportViewer 控件的“工具栏”区域中单击鼠标右键,我可以让我的自定义菜单显示,但是当单击显示实际报告数据的控件的主要部分时,默认ReportViewer 显示附带的上下文菜单。有没有办法让我的上下文菜单替换默认菜单?

我搜索了 ReportViewer 的控件集合,none 其中有一个 ContextMenuStrip。如果重要,ReportViewer 控件版本为 15.0.0.0.

private void CreateContextMenu()
{
    ContextMenuStrip menuStrip = new ContextMenuStrip();
    ToolStripMenuItem menuItem = new ToolStripMenuItem("Exit");

    menuItem.Click += new EventHandler(MenuItem_Click);
    menuItem.Name = "Exit";
    menuStrip.Items.Add(menuItem);

    // This makes no difference.
    //this.reportViewer1.ContextMenu = null;
    this.reportViewer1.ContextMenuStrip = menuStrip;
}

private void MenuItem_Click(object sender, EventArgs e)
{
    ToolStripItem menuItem = (ToolStripItem)sender;
    if(menuItem.Name == "Exit")
    {
        Application.Exit();
    }
}

是的,您可以替换报告的默认设置 ContextMenuStrip。您需要获取包含并显示默认 CMS 的渲染器容器。 ReportViewer 控件树中唯一具有 CMS 的控件是 ReportPanel 类型的无名控件。我们可以利用这个事实来获得控制权并替换 CMS。

解决方案 1

目标面板没有name/key直接通过ControlCollection.Find方法获取。但是,它的父项确实有一个名称 winRSviewer。所以你可以这样写:

// +
// using System.Linq;
// ...

var c = reportViewer1.Controls.Find("winRSviewer", true)
    .FirstOrDefault()?.Controls.OfType<Control>()
    .FirstOrDefault(x => x.ContextMenuStrip != null);

if (c != null) c.ContextMenuStrip = contextMenuStrip1;

解决方案 2

使用递归函数获取目标控件:

private void SomeCaller()
{
    var c = GetChildren(reportViewer1).FirstOrDefault(x => x.ContextMenuStrip != null);

    if (c != null) c.ContextMenuStrip = contextMenuStrip1;
}

private IEnumerable<Control> GetChildren(Control parent) => parent.Controls
    .Cast<Control>().Concat(parent.Controls
    .Cast<Control>().SelectMany(GetChildren));

解决方案 3

创建一个新的class并继承自ReportViewer控件。添加一个 属性 允许您在默认 CMS 和您选择的 CMS 之间切换。

public class ReportViewerEx : ReportViewer
{
    private readonly Control reportPanel;
    private readonly ContextMenuStrip defaultCMS;

    public ReportViewerEx() : base() 
    {
        reportPanel = GetChildren(this).FirstOrDefault(x => x.ContextMenuStrip != null);
        if (reportPanel != null) defaultCMS = reportPanel.ContextMenuStrip;
    }

    private ContextMenuStrip customCMS;
    [DefaultValue(null)]
    public ContextMenuStrip ReportPanelContextMenuStrip
    {
        get => customCMS;
        set
        {
            customCMS = value;
            if (reportPanel != null) reportPanel.ContextMenuStrip = value ?? defaultCMS;
        }
    }

    private IEnumerable<Control> GetChildren(Control parent) => parent.Controls
            .Cast<Control>().Concat(parent.Controls
            .Cast<Control>().SelectMany(GetChildren));
}

构建和删除一个实例。从下拉列表中找到 window 到 select 属性中的 ReportPanelContextMenuStrip 不同的 CMS。重置 属性 以使用默认 CMS。或者,通过代码设置 属性。 null 重置。