Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
61f5a3b
Implements #843 in Lite
ClaudioESSilva Apr 15, 2026
d6b49a5
Implements #843 for Full Dashboard
ClaudioESSilva Apr 15, 2026
3fcbc61
Merge pull request #844 from ClaudioESSilva/feature/auto-scrolling
erikdarlingdata Apr 15, 2026
214b64e
Add trailing newlines to ScrollPanBehavior files
erikdarlingdata Apr 15, 2026
3c98ccb
Merge pull request #845 from erikdarlingdata/fix/trailing-newlines
erikdarlingdata Apr 15, 2026
ccf82aa
Harden DuckDB queries: parameterize values, escape paths, fix IsArchi…
erikdarlingdata Apr 15, 2026
f9be94c
Merge pull request #850 from erikdarlingdata/fix/duckdb-security-hard…
erikdarlingdata Apr 15, 2026
c11608d
Encrypt webhook URLs with DPAPI via Windows Credential Manager
erikdarlingdata Apr 15, 2026
32f0154
Merge pull request #851 from erikdarlingdata/fix/webhook-url-encryption
erikdarlingdata Apr 15, 2026
523a91c
Lazy-load server tabs: only load visible tab on open, full-load on fi…
erikdarlingdata Apr 16, 2026
3db895c
Cap query/procedure/query store grid results to TOP 500
erikdarlingdata Apr 16, 2026
a88572c
Merge pull request #852 from erikdarlingdata/fix/lazy-load-server-tabs
erikdarlingdata Apr 16, 2026
50d10f5
Remove pointless WAITFOR DECOMPRESS filters from stats/store queries
erikdarlingdata Apr 16, 2026
160921f
Refactor query/procedure/query store stats to phased DECOMPRESS approach
erikdarlingdata Apr 16, 2026
29e561c
Merge pull request #853 from erikdarlingdata/fix/remove-waitfor-decom…
erikdarlingdata Apr 16, 2026
4235f50
Fix FinOps TDE recommendation on SQL Server 2019+ (#854)
erikdarlingdata Apr 16, 2026
2b67f09
Merge pull request #855 from erikdarlingdata/fix/854-finops-tde-versi…
erikdarlingdata Apr 16, 2026
e668b76
Sync PlanAnalyzer and BenefitScorer from PerformanceStudio (Apr 9-16)
erikdarlingdata Apr 16, 2026
41f182f
Merge pull request #856 from erikdarlingdata/sync/planalyzer-from-ps-…
erikdarlingdata Apr 16, 2026
3e950a5
Fall back to single-database mode when Azure master is inaccessible (…
erikdarlingdata Apr 17, 2026
d5f586c
Merge pull request #858 from erikdarlingdata/fix/857-azure-master-fal…
erikdarlingdata Apr 17, 2026
694ead6
Add nonclustered indexes for query/procedure/query store lookups
erikdarlingdata Apr 17, 2026
a484e6e
Merge pull request #859 from erikdarlingdata/fix/835-query-lookup-ind…
erikdarlingdata Apr 17, 2026
a5829ba
Scope query snapshots to current database on Azure SQL DB (#857)
erikdarlingdata Apr 18, 2026
e41c960
Merge pull request #861 from erikdarlingdata/fix/857-live-snapshot-az…
erikdarlingdata Apr 18, 2026
b815472
Polish Lite chart axes and sub-tab styling
erikdarlingdata Apr 18, 2026
333a8f7
Merge pull request #862 from erikdarlingdata/ui/lite-chart-and-tab-po…
erikdarlingdata Apr 18, 2026
64ddd01
Port Lite chart/tab polish to Dashboard + LSP diagnostics cleanup
erikdarlingdata Apr 19, 2026
02db6b5
Merge pull request #863 from erikdarlingdata/chore/lsp-diagnostics-cl…
erikdarlingdata Apr 19, 2026
f1c8160
Fix Overview crosshair disappearing after tab switches / layout passes
erikdarlingdata Apr 19, 2026
d319039
Merge pull request #864 from erikdarlingdata/fix/overview-crosshair-s…
erikdarlingdata Apr 19, 2026
f014bf8
Fix Memory Pressure Events chart filter; add MCP interpretation (#865)
erikdarlingdata Apr 19, 2026
b86250f
Port Memory Pressure Events feature to Lite (#865)
erikdarlingdata Apr 19, 2026
04d2e24
Bump schema table count test to 30 for memory_pressure_events
erikdarlingdata Apr 19, 2026
641e59f
Merge pull request #866 from erikdarlingdata/fix/865-memory-pressure-…
erikdarlingdata Apr 19, 2026
985da4a
Fix blocked process report plan lookup (#867) (#868)
erikdarlingdata Apr 20, 2026
2958c0c
Pre-filter query snapshot requests into #temp on Azure SQL DB (#857) …
erikdarlingdata Apr 20, 2026
4cebae2
Stop retrying collectors after non-transient permission denial (#857)…
erikdarlingdata Apr 20, 2026
3327c91
Skip live query plans on Azure SQL DB (#857) (#871)
erikdarlingdata Apr 21, 2026
2c1ccbe
Fix FinOps recommendation severity sort order (#872)
erikdarlingdata Apr 21, 2026
08c68c7
Merge pull request #874 from erikdarlingdata/fix/872-finops-severity-…
erikdarlingdata Apr 21, 2026
eb5d326
Fix FinOps severity sort order in Dashboard (#872)
erikdarlingdata Apr 21, 2026
12d4e34
Merge pull request #875 from erikdarlingdata/fix/872-dashboard-finops…
erikdarlingdata Apr 21, 2026
95bca55
Drop sys.dm_os_schedulers from memory_stats on Azure SQL DB (#857) (#…
erikdarlingdata Apr 21, 2026
791f107
Release v2.8.0: version bumps and changelog (#877)
erikdarlingdata Apr 22, 2026
46a1285
Scope v2.8.0 webhook DPAPI note to Dashboard (#879)
erikdarlingdata Apr 22, 2026
41ced86
Add Open Log Folder sidebar button in Lite (#873) (#881)
erikdarlingdata Apr 22, 2026
cfd7fab
Add right-click View Plan on Lite Deadlocks grid (#880) (#882)
erikdarlingdata Apr 22, 2026
cd2bc05
Add right-click View Plan on Dashboard Blocked Process Reports and De…
erikdarlingdata Apr 22, 2026
5c988a0
Fixes #889
ClaudioESSilva Apr 24, 2026
6ded45b
fixes #889 on Lite
ClaudioESSilva Apr 24, 2026
059180d
Merge pull request #890 from ClaudioESSilva/fix/improve-themes
erikdarlingdata Apr 24, 2026
6c01da9
Add Off collection preset and re-enable-all behavior for named preset…
erikdarlingdata Apr 24, 2026
fdd824a
Bump DuckDB.NET to 1.5.2
erikdarlingdata Apr 25, 2026
4bdd81e
Bump Microsoft.Extensions.*, System.Text.Json, ScottPlot.WPF
erikdarlingdata Apr 25, 2026
c182721
Merge pull request #893 from erikdarlingdata/feature/bump-duckdb-1.5.2
erikdarlingdata Apr 25, 2026
f63096c
Merge pull request #894 from erikdarlingdata/feature/safe-nuget-bumps
erikdarlingdata Apr 25, 2026
90907b4
Bump Microsoft.Data.SqlClient to 7.0.1
erikdarlingdata Apr 25, 2026
7c642d6
Merge pull request #895 from erikdarlingdata/feature/sqlclient-7.0.1
erikdarlingdata Apr 25, 2026
987317c
Bump ModelContextProtocol to 1.2.0
erikdarlingdata Apr 25, 2026
623fbd9
Merge pull request #896 from erikdarlingdata/feature/mcp-1.2.0
erikdarlingdata Apr 25, 2026
2366f57
Bump Microsoft.NET.Test.Sdk to 18.4.0
erikdarlingdata Apr 25, 2026
97efd3d
Merge pull request #897 from erikdarlingdata/feature/bump-test-sdk-18…
erikdarlingdata Apr 25, 2026
b23d64b
Raise install loop timeout from 5 minutes to 1 hour (#884) (#886)
erikdarlingdata Apr 25, 2026
69fe460
Add Purge Now action to Manage Servers (#900)
erikdarlingdata Apr 28, 2026
8d3ca32
Merge pull request #901 from erikdarlingdata/feature/900-purge-now-an…
erikdarlingdata Apr 28, 2026
e3d2d5b
Surface total non-idle CPU and let alerts target it (#899)
erikdarlingdata Apr 28, 2026
5f0248e
Merge pull request #902 from erikdarlingdata/feature/899-total-non-id…
erikdarlingdata Apr 28, 2026
0c9303e
Sync columnstore icon routing + Parallelism subtype icons from Perfor…
erikdarlingdata Apr 28, 2026
62cc4f2
Skip historical sweep when collectors resume after a gap (#892)
erikdarlingdata Apr 28, 2026
96b708f
Merge pull request #904 from erikdarlingdata/feature/892-resume-gap-d…
erikdarlingdata Apr 28, 2026
2ad8c67
Merge pull request #903 from erikdarlingdata/sync/parallelism-columns…
erikdarlingdata Apr 28, 2026
c1dcfc2
Add per-database exclusions for collectors (Dashboard, #887)
erikdarlingdata Apr 28, 2026
6c55dc6
Merge pull request #905 from erikdarlingdata/feature/887-per-database…
erikdarlingdata Apr 28, 2026
f79c14c
Add per-database exclusions for Lite collectors (#887)
erikdarlingdata Apr 28, 2026
add515e
Merge pull request #906 from erikdarlingdata/feature/887-lite-per-dat…
erikdarlingdata Apr 28, 2026
c8e36a1
Add docs/how-collection-works.md
erikdarlingdata Apr 28, 2026
dbaf19c
Merge pull request #907 from erikdarlingdata/docs/how-collection-works
erikdarlingdata Apr 28, 2026
ebd458e
Release v2.9.0: Installed Version column, Lite-style indicators in Fu…
erikdarlingdata Apr 28, 2026
38a72df
Merge pull request #908 from erikdarlingdata/feature/manage-servers-v…
erikdarlingdata Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,92 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.9.0] - TBD

### Important

- **Breaking change to `config.data_retention`** — the `@truncate_all` parameter has been removed. Pass `@retention_days = 0` for the same behavior. `@retention_days = NULL` (default) respects per-collector retention from `config.collection_schedule` with a 30-day fallback for unscheduled tables; `@retention_days = N > 0` overrides every table to N days. Any existing Agent jobs or scripts calling `data_retention @truncate_all = 1` need to be updated ([#900])
- **New `config.collector_database_exclusions` table** for per-database collector exclusions. Eight per-database collectors filter against this table; system databases remain hard-skipped by the collectors themselves. Existing installs get the table on the next upgrade — `install/01_install_database.sql` and `config.ensure_config_tables` both create it under an `IF OBJECT_ID … IS NULL` guard ([#887])

### Added

- **Per-database collector exclusions** — exclude noisy or unimportant databases from per-database collectors. Dashboard side adds `config.collector_database_exclusions` and filters 8 collectors (`query_stats`, `query_store`, `procedure_stats`, `file_io_stats`, `waiting_tasks`, `database_configuration`, `database_size_stats`, `server_properties`). Lite side adds an `ExcludedDatabases` list per server in `servers.json` and filters 9 collectors ([#887])
- **`Off` collection preset** — `EXECUTE config.apply_collection_preset @preset_name = N'Off'` disables every collector in one call. Pair with a second Agent job that applies a non-`Off` preset at the start of your active window for overnight / quiet-hours scoping. Non-`Off` presets now also set `enabled = 1` across the board so the switch from `Off → Balanced` reliably resumes collection ([#888])
- **Purge Now action** in Manage Servers — confirm dialog with a mode picker (Use configured / 1 / 3 / 7 / Custom / All) drives `config.data_retention`; right-click menu on the Manage Servers grid mirrors every per-row action (Edit, Toggle Favorite, Check Server Version, Purge Now, Remove) ([#900])
- **Total non-idle CPU on Lite Overview** — headline value shows total CPU with the SQL-only value alongside (e.g. `64% (SQL 60%)`); new `CpuAlertMode` dropdown in Settings → Alerts (Total / SqlOnly) drives both the alert evaluator and headline color; tray notifications and email alerts label the value as "Total CPU" or "SQL CPU" ([#899])
- **Resume gap detection** — `query_stats`, `procedure_stats`, and `query_store` collectors skip the historical sweep on first run after an Off preset, Agent stoppage, or server reboot. When the last successful run is older than 5× the configured `frequency_minutes` (floored at 30 minutes), the cutoff clamps to `SYSDATETIME()` so only forward-going data is collected on resume — preventing the tempdb blowout that hit the original reporter ([#892])
- **Right-click View Plan** on Dashboard Blocked Process Reports (View Blocked Plan + View Blocking Plan), Dashboard Deadlocks, and Lite Deadlocks grids. Plan lookup hits `sys.dm_exec_query_stats` + `sys.dm_exec_text_query_plan` on the monitored server, falling back to `executionStack/frame` entries when the process-level `sql_handle` is empty or evicted ([#880])
- **Open Log Folder** sidebar button in Lite — opens `%LocalAppData%\PerformanceMonitorLite\logs\` in Explorer for grabbing historical logs to attach to bug reports. Sits below View Log, which retains its existing behavior of opening today's log file ([#873])
- **Installed Version column** in the Manage Servers grid for both Dashboard and Lite. Dashboard shows the PerformanceMonitor database version on each server (probed in parallel via `GetInstalledVersionAsync`, with `Not installed` / `Unavailable` fallbacks). Lite shows the running app's own version on every row, mirroring Full's column header for consistency.
- **Lite-style server card indicators in Full** — back-ported the Ellipse-with-DataTriggers status dot (Online/Offline/Warning/Unknown) and the right-aligned favorite star from Lite to the Full Dashboard's server list, matching Lite's visual treatment.
- **Architecture overview** at `docs/how-collection-works.md` covering the minute loop, dispatcher, collector shape, `config.collection_schedule`, retention, and the Dashboard read path

### Changed

- **PlanIconMapper synced** with PerformanceStudio v1.9.0 improvements — columnstore storage type on scan/delete/insert/update/merge operators routes to `columnstore_index_*` icons (covers CCI and NCCI); `Parallelism` operator subtypes (Repartition Streams, Distribute Streams, Gather Streams) get their own icons
- **`Microsoft.Data.SqlClient` 6.1.4 → 7.0.1** — major-version bump. Azure/Entra dependencies were split out of the core package in 7.0; `Microsoft.Data.SqlClient.Extensions.Azure 1.0.0` added to Dashboard, Lite, and Installer.Core for `ActiveDirectoryInteractive` connections
- **`ModelContextProtocol` 0.7.0-preview.1 → 1.2.0** — off the preview tag and onto stable 1.x in Dashboard and Lite
- **`DuckDB.NET` 1.5.0 → 1.5.2** in Lite — fixes unbounded row group growth on indexed tables under repeated load+insert cycles, memory leaks and race conditions in prepared statements, WAL checkpoint marking, and Windows UTF-8/UTF-16 handling
- **`Microsoft.Extensions.*` 10.0.5 → 10.0.7**, **`System.Text.Json` 10.0.5 → 10.0.7**, **`ScottPlot.WPF` 5.1.57 → 5.1.58** — patch-level bumps with no expected behavioral change
- **Theme polish** on grids and plan viewer in Dashboard and Lite — thanks [@ClaudioESSilva](https://github.com/ClaudioESSilva) ([#889])

### Fixed

- **Install loop timeout** raised from 5 minutes to 1 hour. `install/98_validate_installation.sql` runs every enabled collector with `@debug = 1` in a single batch; on large databases (reporter had 7.2M rows in `collect.query_stats`, 4.4M in `collect.query_store_data`) this took ~9 minutes and was blowing the 5-minute timeout, failing the install or upgrade ([#884])

[#873]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/873
[#880]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/880
[#884]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/884
[#887]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/887
[#888]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/888
[#889]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/889
[#892]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/892
[#899]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/899
[#900]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/900

## [2.8.0] - 2026-04-22

### Important

- **New nonclustered indexes** on `collect.query_stats`, `collect.procedure_stats`, and `collect.query_store_data` to eliminate Eager Index Spools in Dashboard grid queries. On large installations these indexes may take several minutes to build; the upgrade script uses `ONLINE = ON` on Enterprise/Developer/Azure editions and falls back to offline on Standard/Web ([#835])

### Added

- **Memory Pressure Events in Lite** — the collector, chart, and `get_memory_pressure_events` MCP tool previously only in the Full Edition are now available in Lite ([#865])
- **Grid auto-scrolling** in Lite and Dashboard ([#843]) — thanks [@ClaudioESSilva](https://github.com/ClaudioESSilva)

### Changed

- **PlanAnalyzer and BenefitScorer** synced with PerformanceStudio's Apr 9–16 improvements
- **Query/Procedure/Query Store stats** refactored to a phased DECOMPRESS approach; removed unhelpful `WAITFOR DECOMPRESS` filters
- **Query/Procedure/Query Store grids** capped to TOP 500 to prevent UI freezes on large datasets
- **Server tabs lazy-load** — only the visible server tab loads on startup; remaining tabs load on first visit
- **Webhook URLs (Dashboard)** encrypted with DPAPI via Windows Credential Manager — Lite webhook URLs remain in plaintext settings for now
- **DuckDB queries hardened** — parameterized values, escaped paths, fixed `IsArchiving` race
- **Lite chart axes and sub-tab styling** polished, then ported to Dashboard

### Fixed

- **Memory Pressure Events chart filter** was dropping valid rows; added MCP interpretation guidance ([#865])
- **FinOps recommendation severity sort order** in Lite and Dashboard ([#872])
- **Overview crosshair** disappearing after tab switches or layout passes
- **Blocked process report plan lookup** returning the wrong plan ([#867])
- **FinOps TDE recommendation** flagging Standard edition on SQL Server 2019+ where TDE is free ([#854])
- **Azure SQL DB collector** falls back to single-database mode when `master` is inaccessible ([#857])
- **Azure SQL DB query snapshots** scoped to the current database ([#857])
- **Azure SQL DB query snapshot prefilter** — request set is narrowed into `#temp` before joining DMVs to avoid Azure-specific execution plan issues ([#857])
- **Azure SQL DB live query plans** — now skipped gracefully instead of erroring ([#857])
- **Azure SQL DB memory_stats collector** — dropped `sys.dm_os_schedulers` which is blocked on elastic-pool contained users regardless of DB-scoped grants ([#857])
- **Non-transient permission denials** now stop collector retries instead of looping forever ([#857])

[#835]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/835
[#843]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/843
[#854]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/854
[#857]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/857
[#865]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/865
[#867]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/867
[#872]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/872

## [2.7.0] - 2026-04-13

### Added
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Analysis/SqlServerBaselineProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ private static double PoolVariance(List<BaselineBucket> buckets, double grandMea
return totalSumSq / (totalSamples - 1);
}

private class CachedBaseline
private sealed class CachedBaseline
{
public DateTime ComputedAt { get; init; }
public DateTime RealTime { get; init; }
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/ConfigChangesContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<MenuItem Header="Export to CSV..." Click="ExportToCsv_Click"/>
</ContextMenu>
</UserControl.Resources>
<TabControl>
<TabControl ItemContainerStyle="{DynamicResource SubTabItemStyle}">
<!-- Server Configuration Changes Sub-Tab -->
<TabItem Header="Server Config Changes">
<Grid>
Expand Down
24 changes: 21 additions & 3 deletions Dashboard/Controls/CorrelatedTimelineLanesControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ public partial class CorrelatedTimelineLanesControl : UserControl
public CorrelatedTimelineLanesControl()
{
InitializeComponent();
Unloaded += (_, _) => _crosshairManager?.Dispose();
/* No Unloaded → Dispose() handler: WPF fires Unloaded for transient
reasons (tab virtualization, layout rebuilds) and Dispose() clears
the crosshair manager's lane list, permanently breaking the crosshair
until the ServerTab is rebuilt. The manager holds only managed state
(a Popup + lane references) — letting GC clean it up with the control
is fine. */
}

/// <summary>
Expand Down Expand Up @@ -69,6 +74,9 @@ public async Task RefreshAsync(int hoursBack, DateTime? fromDate, DateTime? toDa

_crosshairManager?.PrepareForRefresh();

try
{

var cpuTask = _dataService.GetCpuUtilizationAsync(hoursBack, fromDate, toDate);
var waitTask = _dataService.GetTotalWaitStatsTrendAsync(hoursBack, fromDate, toDate);
var blockingTask = _dataService.GetBlockedSessionTrendAsync(hoursBack, fromDate, toDate);
Expand Down Expand Up @@ -225,8 +233,18 @@ public async Task RefreshAsync(int hoursBack, DateTime? fromDate, DateTime? toDa
_crosshairManager?.SetComparisonLabel(ComparisonLabel(comparisonRange.Value, fromDate, hoursBack));
}

/* VLines must be re-attached before SyncXAxes so they're part of
the render set when the chart refreshes. */
_crosshairManager?.ReattachVLines();
SyncXAxes(hoursBack, fromDate, toDate);
}
finally
{
/* Safety net: if something threw between PrepareForRefresh() and the
ReattachVLines() call above, VLines are still null. EnsureVLinesAttached
creates them only for lanes where VLine is null, so it's idempotent. */
_crosshairManager?.EnsureVLinesAttached();
}
}

/// <summary>
Expand Down Expand Up @@ -320,7 +338,7 @@ private void UpdateBlockingLane(List<(double Time, double Value)> blockingData,
}
}

BlockingChart.Plot.Axes.DateTimeTicksBottom();
BlockingChart.Plot.Axes.DateTimeTicksBottomDateChange();
BlockingChart.Plot.Axes.Bottom.TickLabelStyle.IsVisible = false;
TabHelpers.ReapplyAxisColors(BlockingChart);

Expand Down Expand Up @@ -394,7 +412,7 @@ private void UpdateLane(ScottPlot.WPF.WpfPlot chart, string title,

_crosshairManager?.SetLaneData(chart, times, values);

chart.Plot.Axes.DateTimeTicksBottom();
chart.Plot.Axes.DateTimeTicksBottomDateChange();
if (chart != FileIoChart)
chart.Plot.Axes.Bottom.TickLabelStyle.IsVisible = false;

Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/CurrentConfigContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<MenuItem Header="Export to CSV..." Click="ExportToCsv_Click"/>
</ContextMenu>
</UserControl.Resources>
<TabControl>
<TabControl ItemContainerStyle="{DynamicResource SubTabItemStyle}">
<!-- Current Server Configuration Sub-Tab -->
<TabItem Header="Server Configuration">
<Grid>
Expand Down
10 changes: 8 additions & 2 deletions Dashboard/Controls/FinOpsContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
SelectionChanged="ServerSelector_SelectionChanged"/>
</StackPanel>

<TabControl Grid.Row="1" Background="Transparent" BorderThickness="0">
<TabControl Grid.Row="1" Background="Transparent" BorderThickness="0" ItemContainerStyle="{DynamicResource SubTabItemStyle}">

<!-- Recommendations Sub-Tab -->
<TabItem Header="Recommendations">
Expand Down Expand Up @@ -90,7 +90,7 @@
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Severity}" MinWidth="80" Width="Auto">
<DataGridTextColumn Binding="{Binding Severity}" SortMemberPath="SeveritySort" MinWidth="80" Width="Auto">
<DataGridTextColumn.Header>
<StackPanel Orientation="Horizontal">
<Button Style="{DynamicResource ColumnFilterButtonStyle}" Tag="Severity" Click="FinOpsFilter_Click" Margin="0,0,4,0"/>
Expand All @@ -111,6 +111,9 @@
<DataTrigger Binding="{Binding Severity}" Value="Low">
<Setter Property="Foreground" Value="#27AE60"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow, AncestorLevel=1}, FallbackValue=False}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource GridSelectionForegroundBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
Expand Down Expand Up @@ -416,6 +419,9 @@
<Setter Property="Foreground" Value="#E74C3C"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow, AncestorLevel=1}, FallbackValue=False}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource GridSelectionForegroundBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/MemoryContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</ResourceDictionary>
</UserControl.Resources>

<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0">
<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0" ItemContainerStyle="{DynamicResource SubTabItemStyle}">
<!-- Memory Overview Sub-Tab -->
<TabItem Header="Memory Overview">
<!-- Chart + Summary Panel (grid removed) -->
Expand Down
Loading
Loading