Repository navigation
Dashboard tab with live message counts for all entities - #866
Conversation
- Replace stale allChildNodes cache with restore-then-snapshot approach (always restore full node set before re-filtering) - Add 250ms debounce timer on TextChanged to prevent UI stutter - filterSnapshot cleared on tree refresh via InvalidateTreeViewFilter
…tainer Timer was lazily created but never disposed, causing a resource leak. Now created in constructor and registered with components for automatic disposal.
TabControl in Panel2 with Dashboard (default) and Explorer tabs. DashboardControl shows DataGridView with Active/DLQ/Scheduled counts, async loading, refresh button, auto-refresh timer, DLQ color coding.
…disposal - Add isLoading guard to prevent overlapping LoadDataAsync calls - Move writeToLog calls to UI thread (collect errors in Task.Run, log after await) - Extract Font objects to fields, dispose in Dispose(bool) - Revert AssemblyVersion to 1.0.0.1 (upstream test convention)
Replace topic rows (always 0 counts) with subscription rows that show actual Active/DLQ message counts from SubscriptionDescription.
Filter now also filters child nodes (subscriptions) under topics. When a topic is kept because a child matches, non-matching children are hidden. Child snapshots are restored when filter is cleared.
…ANGELOG/TESTING update
Clicking a row in the Dashboard DataGridView now selects the matching node in the TreeView, scrolls to it, and switches to the Explorer tab. Supports both queue rows and subscription rows (TopicName / SubName).
Ctrl+C copies selected row as TSV, context menu offers Copy row + Copy name.
Root cause: FilterChildNodes only checked direct children text match. Topic structure is Topic → "Subscriptions" container → actual subs. The container node text never matched, so all subscriptions were hidden. Fix: use recursive NodeMatchesFilter + recurse FilterChildNodes into container nodes. RestoreChildNodes also recurses for nested snapshots.
…RowSelected - ContextMenuStrip promoted from local var to field with explicit Dispose() - Added rootNode null check in DashboardRowSelected to prevent NRE before connection
…e sync) Dashboard refresh button and auto-refresh timer now trigger ShowEntities(EntityType.All) via OnRefreshRequested callback instead of calling Azure SDK directly. This ensures log panel, TreeView, and dashboard stay in sync through one unified refresh flow.
DashboardControl.UpdateRow() finds and updates one row by entity name. RefreshSelectedEntity() calls it after queue and subscription refresh.
- DashboardControl.RemoveRow: removes row by name (case-insensitive) - DashboardControl.RemoveRowsWithPrefix: removes all subscription rows for a topic - DashboardControl.AddRow: idempotent sorted insert with 0 counts - MainForm OnDelete: queue->RemoveRow, topic->RemoveRowsWithPrefix, sub->RemoveRow - MainForm OnCreate: queue->AddRow, subscription->AddRow
ErikMogensen
left a comment
There was a problem hiding this comment.
Got this error:
C:\GitHub\paolosalvatori\ServiceBusExplorer\src\ServiceBusExplorer\Forms\MainForm.cs(250,53,250,73): error CS0234: The type or namespace name 'TreeViewFilterHelper' does not exist in the namespace 'ServiceBusExplorer.Helpers' (are you missing an assembly reference?)
I assume we have to wait until the treeview PR is merged.
…ilterDebounceTimer conflict (keep readonly + constructor init)
173d035 to
673e730
Compare
|
Hi @ErikMogensen, #865 has been merged — rebased this PR on the updated main. The compile error should be gone now. Ready for re-review! |
|
Hi @ErikMogensen , @SeanFeldman |
Yes, as mentioned in this comment #616 (comment) from 2022 the message count operation puts a heavy burden on the Service Bus service. Therefore I just sent an email to the Service Bus Program Management to check if what they said in 2022 still applies. |
|
Thanks @RaoulJacobs for the additions, @ErikMogensen sent an email to the guy who left #616 comment in 2022. If there are no meaningful drawbacks regarding the auto-refresh feature, we'll merge your PR right away. Thanks for the contribution! |
|
Thanks Paolo! The auto-refresh is disabled by default — it's a gadget feature, so no meaningful drawbacks and zero impact on existing behavior. In the meantime I've made a few additional improvements that further align the overall UX. Would you prefer I include them in this PR, or open a separate one? |
Are they closely related to this one? In that case yes please, otherwise no thanks. The product team hasn't responded about the auto-refresh. I suggest we go for what they wanted in 2022 which was:
If they reply with a different answer this time we can always fix it. @RaoulJacobs, please remove the possibility to set it to 30 seconds and add a text stating something like: "Auto refresh affects the performance of the Service Bus resource, especially when set to a high frequency". I hope that text will fit... |
ErikMogensen
left a comment
There was a problem hiding this comment.
Some picky changes to the code and some questions about some files.
There was a problem hiding this comment.
It was decided to remove this file.
There was a problem hiding this comment.
Please remove this file since we are not using such a file.
There was a problem hiding this comment.
Seems that this file is for manual testing of the dashboard. Is that so?
There was a problem hiding this comment.
Yes, it was an internal manual test checklist for our own verification during development — not intended for upstream. Removed.
There was a problem hiding this comment.
I don't see this file used anywhere. Is the file referencing this missing?
There was a problem hiding this comment.
I don't see this file used anywhere. Is the file referencing this missing?
There was a problem hiding this comment.
dashboard-tab.png and dashboard-refresh.png are referenced in docs/documentation.md — the Dashboard section uses both images to illustrate the tab and the right-click refresh. dashboard-copy.png was indeed not referenced and has been removed.
There was a problem hiding this comment.
I don't see this file used anywhere. Is the file referencing this missing?
There was a problem hiding this comment.
dashboard-tab.png and dashboard-refresh.png are referenced in docs/documentation.md — the Dashboard section uses both images to illustrate the tab and the right-click refresh. dashboard-copy.png was indeed not referenced and has been removed.
There was a problem hiding this comment.
I don't see docs/documentation.md in this PR, but I guess it was in the other PR.
| } | ||
| } | ||
|
|
||
| public async void LoadDataAsync() |
There was a problem hiding this comment.
Change this to async task LoadDataAsync() for better error handling.
| if (getQueues == null || getTopics == null || getSubscriptions == null || isLoading) return; | ||
|
|
||
| isLoading = true; |
There was a problem hiding this comment.
This could be a race condition since there is a gap between the read and the write. Currently that does not occur since this method will be only called from the UI thread, but to be prepared for future code changes please secure it. One way of doing it is:
- Change
isloadingto an int sinceinterlockeddoes not work withbool. - Change the above code to:
if (getQueues == null || getTopics == null || getSubscriptions == null) return;
if (System.Threading.Interlocked.CompareExchange(ref isLoading, 1, 0) != 0) return;
| Dock = DockStyle.Bottom, | ||
| AutoSize = false, | ||
| Height = 20, | ||
| Font = new Font("Segoe UI", 8F), |
There was a problem hiding this comment.
Please handle this font in the same way you handle the other fonts, such as headerFont. Assigning it to a field and disposing it.
| dataGridView.Rows.Clear(); | ||
| foreach (var row in rows.OrderBy(r => r.Type).ThenBy(r => r.Name)) | ||
| { | ||
| var total = row.Active + row.DeadLetter + row.Scheduled; |
There was a problem hiding this comment.
total is calculated in two places, here and in UpdateRow. If you add a Total property to DashboardRow it is only done in one place.
…ements Auto-refresh UX: - Remove 30s option; minimum is 1min (1/5/15/30/60min options) - Add warning banner when auto-refresh is enabled Dashboard: - Sync with treeview checkbox: when off, dashboard shows all entities ignoring active filter - Sort preservation: PopulateGrid retains sorted column and direction after refresh - Pending selection fix: clicking dashboard row while filter active + sync off clears filter first, then selects node Code quality (Erik's review): - async void LoadDataAsync -> async Task for proper error handling - isLoading bool -> int with Interlocked.CompareExchange (race condition fix) - DashboardRow.Total as computed property via static ComputeTotal (single source of truth) - hintFont and warningFont as fields, properly disposed - ComboBox width 80 -> 90px, syncWithTreeViewCheckBox position corrected TreeView filter: - Clear button: hidden when empty, visible on input, clears filter on click - Clear button forecolor fix (black text on hover) - ApplyFilter wired to filter text changes and sync checkbox toggle Removed: - docs/img/dashboard-copy.png (unreferenced) - CHANGELOG.md (not used in this project) - tools/version-bump.js (decided to remove)
|
Hi @ErikMogensen, addressed all your review comments: Removed files:
Note: Auto-refresh:
Code quality:
Additional improvements included in this push:
|
- Auto-refresh: remove 30s option, add 1/5/15/30/60min intervals - Add warning banner when auto-refresh is enabled - async void LoadDataAsync -> async Task for proper error handling - isLoading bool -> int with Interlocked.CompareExchange (race condition fix) - DashboardRow.Total as computed property via static ComputeTotal helper - hintFont and warningFont as fields, properly disposed - Fix syncWithTreeViewCheckBox position (gap after wider combobox) - Remove tools/version-bump.js, CHANGELOG.md, docs/img/dashboard-copy.png - MainForm.RefreshDashboard: await LoadDataAsync
Summary
Dependencies
Screenshots
Right-click context menu:
Test plan