Skip to content

Add the same menu items for subscriptions as for queues#880

Open
ErikMogensen wants to merge 2 commits into
paolosalvatori:mainfrom
ErikMogensen:subscription-menu
Open

Add the same menu items for subscriptions as for queues#880
ErikMogensen wants to merge 2 commits into
paolosalvatori:mainfrom
ErikMogensen:subscription-menu

Conversation

@ErikMogensen
Copy link
Copy Markdown
Collaborator

There are several menu items that are missing from the message grid for dead-letter queue messages in subscriptions. This PR brings makes them available for subscriptions too.

Removed the word 'shared' from some menu items variable names since they weren't shared.

This causes some code duplication, but that will be handled in a separate PR.

Copilot AI review requested due to automatic review settings April 28, 2026 20:26
@ErikMogensen
Copy link
Copy Markdown
Collaborator Author

@claude, do a review of this handsome pr

@claude
Copy link
Copy Markdown

claude Bot commented Apr 28, 2026

Claude finished @ErikMogensen's task in 2m 7s —— View job


PR Review: Add the same menu items for subscriptions as for queues

  • Gather context and understand the request
  • Read changed files and analyze the diff
  • Review for bugs, correctness, and consistency
  • Review for code quality and best practices
  • Post final review feedback

Overall this is a clean, well-scoped PR that achieves its goal. The renames are sensible, the new code follows the established patterns in HandleQueueControl, and the author is transparent about the intentional code duplication. A few items worth noting:


Observations

1. Missing "Select All" in the subscription deadletter context menu

HandleQueueControl includes a selectAllSharedDeadletterMessagesToolStripMenuItem (Ctrl+A) in its deadletter context menu. This PR adds selectAllMessagesToolStripMenuItem to the subscription's messages menu, but the subscription's deadletter context menu (deadletterContextMenuStrip) doesn't get a corresponding "Select All". This looks like an oversight.

HandleSubscriptionControl.Designer.cs line ~1695 — the deadletter context menu items don't include a select-all entry, even though the queue's equivalent does (HandleQueueControl.Designer.cs line ~2180). Fix this →


2. Indentation inconsistency at end of HandleSubscriptionControl.Designer.cs

The last two field declarations and the closing braces have incorrect indentation relative to the rest of the file:

// Current (incorrectly indented)
        private System.Windows.Forms.PropertyGrid deadletterCustomPropertyGrid;
                 private System.Windows.Forms.ToolStripMenuItem resubmitDeadletterToolStripMenuItem;
                 private System.Windows.Forms.ToolStripMenuItem resubmitMessageToolStripMenuItem;

            }
        }

resubmitDeadletterToolStripMenuItem and resubmitMessageToolStripMenuItem appear to have been moved from an old position with extra leading whitespace, and the closing braces are at the wrong depth. Doesn't affect compilation, but is inconsistent. Fix this →


3. selectAllMessagesToolStripMenuItem order is slightly inconsistent with queue

In the subscription messages menu, the new item is ordered as:

Repair And Resubmit → Resubmit → **Select All** → Resubmit In Batch → separator → Save...

But in HandleQueueControl's deadletter menu, the equivalent order is:

Repair And Resubmit → Resubmit → Resubmit In Batch → **Select All** → separator → Save...

Minor UX inconsistency between the two controls' menus.


4. Pre-existing filename splitting bug (carried forward, not introduced here)

Both the queue and subscription multi-save handlers use this pattern:

// HandleSubscriptionControl.cs ~line 2953
var fileNameParts = saveFileDialog.FileName.Split('.').ToList();
var fileExtension = fileNameParts.Last();
fileNameParts.RemoveAt(fileNameParts.IndexOf(fileExtension));  // BUG: finds first match, not last

IndexOf finds the first occurrence, but Last() returns the last element. For a file like my.txt.file.txt, this would remove the wrong segment. This isn't introduced here (it's already in HandleQueueControl), but since the code was copied, it's worth flagging for the eventual refactor PR.


What's Good

  • The rename of sharedDeadletterContextMenuStripdeadletterContextMenuStrip and removal of Shared from field names is a clean, correct cleanup. Event handler method names are intentionally preserved.
  • New handler implementations in HandleSubscriptionControl.cs follow the same pattern as HandleQueueControl.cs faithfully.
  • CreateFileNameAutoRecognize() helper correctly mirrors the queue control.
  • The PR description is honest about the duplication and the follow-up plan.
  • selectAllMessagesToolStripMenuItem correctly wires up Ctrl+A.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns the subscription (including subscription DLQ) message grid context menus with the queue message grid by adding missing actions (notably “save message text as file” and “select all”), and it also renames several queue deadletter menu item variables to drop the misleading “shared” wording.

Changes:

  • Add context-menu actions in HandleSubscriptionControl to save selected message body text to file (single + multi) and to select all messages.
  • Add corresponding ToolStrip menu items/wiring in the subscription control designer.
  • Rename queue deadletter context menu strip + menu item fields to remove “Shared” from variable names.

Reviewed changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/ServiceBusExplorer/Controls/HandleSubscriptionControl.cs Adds “save body as file” (single/multi) for messages and DLQ messages; adds “select all messages”; updates context-menu visibility logic.
src/ServiceBusExplorer/Controls/HandleSubscriptionControl.Designer.cs Adds ToolStrip items and click wiring for the new subscription context-menu actions.
src/ServiceBusExplorer/Controls/HandleQueueControl.cs Updates deadletter menu visibility wiring to use renamed (non-“shared”) menu item fields and context menu strip.
src/ServiceBusExplorer/Controls/HandleQueueControl.Designer.cs Renames deadletter context menu strip + menu item fields; updates designer wiring accordingly.
Files not reviewed (2)
  • src/ServiceBusExplorer/Controls/HandleQueueControl.Designer.cs: Language not supported
  • src/ServiceBusExplorer/Controls/HandleSubscriptionControl.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2950 to +2957
foreach (var body in bodies)
{
count++;
var fileNameParts = saveFileDialog.FileName.Split('.').ToList();
var fileExtension = fileNameParts.Last();
fileNameParts.RemoveAt(fileNameParts.IndexOf(fileExtension));
fileNameParts.Add($"({count}).{fileExtension}");
var fileName = string.Join(".", fileNameParts);
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The numbered-output filename generation is fragile: splitting the full path on '.' and removing the extension via IndexOf can remove the wrong segment when the filename contains multiple dots (e.g. "foo.txt.v1.txt") or the extension token appears earlier. Use Path.GetFileNameWithoutExtension/Path.GetExtension (or RemoveAt(fileNameParts.Count-1)) to build "{base}({count}){ext}" safely, preserving the directory portion.

Suggested change
foreach (var body in bodies)
{
count++;
var fileNameParts = saveFileDialog.FileName.Split('.').ToList();
var fileExtension = fileNameParts.Last();
fileNameParts.RemoveAt(fileNameParts.IndexOf(fileExtension));
fileNameParts.Add($"({count}).{fileExtension}");
var fileName = string.Join(".", fileNameParts);
var selectedFileName = saveFileDialog.FileName;
var directoryName = Path.GetDirectoryName(selectedFileName);
var baseFileName = Path.GetFileNameWithoutExtension(selectedFileName);
var fileExtension = Path.GetExtension(selectedFileName);
foreach (var body in bodies)
{
count++;
var numberedFileName = $"{baseFileName}({count}){fileExtension}";
var fileName = string.IsNullOrEmpty(directoryName)
? numberedFileName
: Path.Combine(directoryName, numberedFileName);

Copilot uses AI. Check for mistakes.
Comment on lines +3132 to +3136
var fileNameParts = saveFileDialog.FileName.Split('.').ToList();
var fileExtension = fileNameParts.Last();
fileNameParts.RemoveAt(fileNameParts.IndexOf(fileExtension));
fileNameParts.Add($"({count}).{fileExtension}");
var fileName = string.Join(".", fileNameParts);
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here as in the messages bulk-save: generating numbered filenames by splitting on '.' and removing via IndexOf can produce incorrect paths when the chosen filename includes multiple dots. Prefer Path-based filename manipulation (GetDirectoryName + GetFileNameWithoutExtension + GetExtension) or at least remove the last segment rather than the first match.

Suggested change
var fileNameParts = saveFileDialog.FileName.Split('.').ToList();
var fileExtension = fileNameParts.Last();
fileNameParts.RemoveAt(fileNameParts.IndexOf(fileExtension));
fileNameParts.Add($"({count}).{fileExtension}");
var fileName = string.Join(".", fileNameParts);
var directoryName = Path.GetDirectoryName(saveFileDialog.FileName);
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(saveFileDialog.FileName);
var fileExtension = Path.GetExtension(saveFileDialog.FileName);
var numberedFileName = $"{fileNameWithoutExtension}({count}){fileExtension}";
var fileName = string.IsNullOrEmpty(directoryName)
? numberedFileName
: Path.Combine(directoryName, numberedFileName);

Copilot uses AI. Check for mistakes.
Comment on lines +2226 to +2238
// saveSelectedDeadletteredMessageToolStripMenuItem
//
this.saveSelectedSharedDeadletteredMessageToolStripMenuItem.Name = "saveSelectedSharedDeadletteredMessageToolStripMenuItem";
this.saveSelectedSharedDeadletteredMessageToolStripMenuItem.Size = new System.Drawing.Size(305, 22);
this.saveSelectedSharedDeadletteredMessageToolStripMenuItem.Text = "Save Selected Message";
this.saveSelectedSharedDeadletteredMessageToolStripMenuItem.Click += new System.EventHandler(this.saveSelectedSharedDeadletteredMessageToolStripMenuItem_Click);
this.saveSelectedDeadletteredMessageToolStripMenuItem.Name = "saveSelectedDeadletteredMessageToolStripMenuItem";
this.saveSelectedDeadletteredMessageToolStripMenuItem.Size = new System.Drawing.Size(305, 22);
this.saveSelectedDeadletteredMessageToolStripMenuItem.Text = "Save Selected Message";
this.saveSelectedDeadletteredMessageToolStripMenuItem.Click += new System.EventHandler(this.saveSelectedSharedDeadletteredMessageToolStripMenuItem_Click);
//
// saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem
// saveSelectedDeadletteredMessageBodyAsFileToolStripMenuItem
//
this.saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem.Name = "saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem";
this.saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem.Size = new System.Drawing.Size(305, 22);
this.saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem.Text = "Save Selected Message Text as File";
this.saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem.Click += new System.EventHandler(this.saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem_Click);
this.saveSelectedDeadletteredMessageBodyAsFileToolStripMenuItem.Name = "saveSelectedDeadletteredMessageBodyAsFileToolStripMenuItem";
this.saveSelectedDeadletteredMessageBodyAsFileToolStripMenuItem.Size = new System.Drawing.Size(305, 22);
this.saveSelectedDeadletteredMessageBodyAsFileToolStripMenuItem.Text = "Save Selected Message Text as File";
this.saveSelectedDeadletteredMessageBodyAsFileToolStripMenuItem.Click += new System.EventHandler(this.saveSelectedSharedDeadletteredMessageBodyAsFileToolStripMenuItem_Click);
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These menu items were renamed to remove the "Shared" wording, but their Click handlers still point to methods named SharedDeadletter (e.g. saveSelectedSharedDeadletteredMessageToolStripMenuItem_Click). This mismatch makes it harder to understand/refactor the UI wiring; consider renaming the handler methods (and updating references) to match the new menu item names.

Copilot uses AI. Check for mistakes.
@ErikMogensen
Copy link
Copy Markdown
Collaborator Author

Looks like good feedback. I'll take a deeper look at it and update this PR later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants