Replacing the editor's right-click context menu

    Mei-Lin works on the sales CRM at a logistics company. Sales reps spend their day inside an account-detail screen attaching notes to customer records: call summaries, follow-up reminders, snippets pasted from emails. She uses WinFormHtmlEditor as the note editor and her product manager came to her with a clear ask: when a rep right-clicks inside a note, they should see two commands that don't exist in the default menu -- Link to Ticket... and Convert Selection to Task.

    The default right-click menu in WinFormHtmlEditor is a reasonable editing menu: cut/copy/paste, table commands, image properties. But it's not the right menu for a CRM note. Mei-Lin wanted to replace it entirely.

    Replace the menu via EditorContextMenuStrip

    The editor exposes a single property for this: EditorContextMenuStrip, of type System.Windows.Forms.ContextMenuStrip. Assign your own strip and the editor uses it whenever the user right-clicks inside the document surface.

    private void NoteEditorForm_Load(object sender, EventArgs e)
    {
        var menu = new ContextMenuStrip();
        var linkToTicket = new ToolStripMenuItem("Link to Ticket...");
        linkToTicket.Click += (s, args) =>
        {
            var ticketId = TicketPicker.Show(this);
            if (ticketId != null)
            {
                string anchor = $"<a href=\"crm://ticket/{ticketId}\">#{ticketId}</a>";
                htmlEditor1.Content.InsertHtmlAtCaret(anchor);
            }
        };
        var convertToTask = new ToolStripMenuItem("Convert Selection to Task");
        convertToTask.Click += (s, args) =>
        {
            string selected = htmlEditor1.Selection.GetSelectedHtml();
            if (!string.IsNullOrWhiteSpace(selected))
                TaskService.CreateFromHtml(currentAccount.Id, selected);
        };
        menu.Items.Add(linkToTicket);
        menu.Items.Add(new ToolStripSeparator());
        menu.Items.Add(convertToTask); // Hand the strip to the editor. From now on, right-clicking inside the    // note shows this menu instead of the built-in one.    htmlEditor1.EditorContextMenuStrip = menu;}
    Private Sub NoteEditorForm_Load(sender As Object, e As EventArgs)
        Dim menu = New ContextMenuStrip()
        Dim linkToTicket = New ToolStripMenuItem("Link to Ticket...")
        linkToTicket.Click += Sub(s, args)
                                  Dim ticketId = TicketPicker.Show(Me)
                                  If ticketId IsNot Nothing Then
                                      Dim anchor As String = $"<a href=""crm://ticket/{ticketId}"">#{ticketId}</a>"
                                      htmlEditor1.Content.InsertHtmlAtCaret(anchor)
                                  End If
                              End Sub
        Dim convertToTask = New ToolStripMenuItem("Convert Selection to Task")
        convertToTask.Click += Sub(s, args)
                                   Dim selected As String = htmlEditor1.Selection.GetSelectedHtml()
                                   If Not String.IsNullOrWhiteSpace(selected) Then TaskService.CreateFromHtml(currentAccount.Id, selected)
                               End Sub
        menu.Items.Add(linkToTicket)
        menu.Items.Add(New ToolStripSeparator())
        menu.Items.Add(convertToTask) ' Hand the strip to the editor. From now on, right-clicking inside the    // note shows this menu instead of the built-in one.    htmlEditor1.EditorContextMenuStrip = menu;}
    End Sub

    [IMAGE: editor-context-menu-strip.png -- right-click menu showing Link to Ticket and Convert to Task in place of the default menu]

    Disable items based on current state

    Mei-Lin's PM came back the next day with a refinement: "Convert to Task" should be greyed out when there's nothing selected. The EditorContextMenuStrip is a normal Windows Forms ContextMenuStrip, so she wired up its Opening event:

    menu.Opening += (s, args) =>
    {
        string selected = htmlEditor1.Selection.GetSelectedHtml();
        convertToTask.Enabled = !string.IsNullOrWhiteSpace(selected);
    };
    menu.Opening += Sub(s, args)
                        Dim selected As String = htmlEditor1.Selection.GetSelectedHtml()
                        convertToTask.Enabled = Not String.IsNullOrWhiteSpace(selected)

    [IMAGE: context-menu-opening-state.png -- menu with Convert to Task disabled because there is no selection]

    The ContextMenuShowing event: a notification hook

    Mei-Lin also wanted to track which parts of a note reps right-click on, so the team could decide where to put the next CRM-specific shortcut. The editor exposes a ContextMenuShowing event that fires whenever the built-in menu is about to appear. The event args give you the cursor position relative to the editor, which is enough for analytics:

    htmlEditor1.ContextMenuShowing += (sender, e) =>
    {
        // e.OffsetMousePosition is the cursor location relative to the editor surface.
        AnalyticsClient.Track(
            "NoteEditorRightClick",
            new { x = e.OffsetMousePosition.X, y = e.OffsetMousePosition.Y });
    };

    One thing worth knowing: ContextMenuShowingEventArgs only carries OffsetMousePosition. There's no Cancel, no MenuItems collection, no way to suppress or mutate the built-in menu from inside this event. If you want a completely different menu (as Mei-Lin did), the supported path is EditorContextMenuStrip. The event is for observation, not modification.

    [IMAGE: context-menu-showing-event.png -- event firing as the built-in menu appears, with the offset coordinates highlighted]

    In production, reps started using Link to Ticket dozens of times a day per account. The cross-references between notes and tickets now flow into the CRM's knowledge graph automatically. Mei-Lin's PM filed a follow-up to add a third item -- Insert Account Name -- and she shipped it the same afternoon by adding one more ToolStripMenuItem to the strip she built on day one.

    Last updated on May 15, 2026