diff --git a/Lite/Controls/CorrelatedTimelineLanesControl.xaml.cs b/Lite/Controls/CorrelatedTimelineLanesControl.xaml.cs index d39775a..beb4c57 100644 --- a/Lite/Controls/CorrelatedTimelineLanesControl.xaml.cs +++ b/Lite/Controls/CorrelatedTimelineLanesControl.xaml.cs @@ -287,7 +287,7 @@ private void UpdateBlockingLane(List<(double Time, double Value)> blockingData, meanLine.LineWidth = 1; } - BlockingChart.Plot.Axes.DateTimeTicksBottom(); + BlockingChart.Plot.Axes.DateTimeTicksBottomDateChange(); BlockingChart.Plot.Axes.Bottom.TickLabelStyle.IsVisible = false; ReapplyAxisColors(BlockingChart); @@ -363,7 +363,7 @@ private void UpdateLane(ScottPlot.WPF.WpfPlot chart, string title, _crosshairManager?.SetLaneData(chart, times, values); - chart.Plot.Axes.DateTimeTicksBottom(); + chart.Plot.Axes.DateTimeTicksBottomDateChange(); // Hide bottom tick labels on all lanes except the last (File I/O) if (chart != FileIoChart) chart.Plot.Axes.Bottom.TickLabelStyle.IsVisible = false; diff --git a/Lite/Controls/ServerTab.xaml b/Lite/Controls/ServerTab.xaml index c04d23d..e85bc38 100644 --- a/Lite/Controls/ServerTab.xaml +++ b/Lite/Controls/ServerTab.xaml @@ -202,7 +202,7 @@ - + @@ -1046,7 +1046,7 @@ - + @@ -1182,7 +1182,7 @@ - + @@ -1239,7 +1239,7 @@ - + @@ -1578,7 +1578,7 @@ - + - + data) otherPlot.Color = ScottPlot.Color.FromHex("#E57373"); _cpuHover?.Add(otherPlot, "Other"); - CpuChart.Plot.Axes.DateTimeTicksBottom(); + CpuChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(CpuChart); CpuChart.Plot.YLabel("CPU %"); CpuChart.Plot.Axes.SetLimitsY(0, 105); @@ -1932,7 +1932,7 @@ private void UpdateMemoryChart(List data, List data) } } - MemoryGrantSizingChart.Plot.Axes.DateTimeTicksBottom(); + MemoryGrantSizingChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(MemoryGrantSizingChart); MemoryGrantSizingChart.Plot.YLabel("Memory (MB)"); SetChartYLimitsWithLegendPadding(MemoryGrantSizingChart, 0, sizingMax > 0 ? sizingMax : 100); @@ -2025,7 +2025,7 @@ private void UpdateMemoryGrantCharts(List data) } } - MemoryGrantActivityChart.Plot.Axes.DateTimeTicksBottom(); + MemoryGrantActivityChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(MemoryGrantActivityChart); MemoryGrantActivityChart.Plot.YLabel("Count"); SetChartYLimitsWithLegendPadding(MemoryGrantActivityChart, 0, activityMax > 0 ? activityMax : 10); @@ -2061,7 +2061,7 @@ private void UpdateTempDbChart(List data) vsPlot.Color = ScottPlot.Color.FromHex("#81C784"); _tempDbHover?.Add(vsPlot, "Version Store"); - TempDbChart.Plot.Axes.DateTimeTicksBottom(); + TempDbChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(TempDbChart); TempDbChart.Plot.YLabel("MB"); @@ -2107,7 +2107,7 @@ private void UpdateTempDbFileIoChart(List data) } } - TempDbFileIoChart.Plot.Axes.DateTimeTicksBottom(); + TempDbFileIoChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(TempDbFileIoChart); TempDbFileIoChart.Plot.YLabel("TempDB File I/O Latency (ms)"); SetChartYLimitsWithLegendPadding(TempDbFileIoChart, 0, maxLatency > 0 ? maxLatency : 10); @@ -2191,14 +2191,14 @@ private void UpdateFileIoCharts(List data) } } - FileIoReadChart.Plot.Axes.DateTimeTicksBottom(); + FileIoReadChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(FileIoReadChart); FileIoReadChart.Plot.YLabel("Read Latency (ms)"); SetChartYLimitsWithLegendPadding(FileIoReadChart, 0, readMax > 0 ? readMax : 10); ShowChartLegend(FileIoReadChart); FileIoReadChart.Refresh(); - FileIoWriteChart.Plot.Axes.DateTimeTicksBottom(); + FileIoWriteChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(FileIoWriteChart); FileIoWriteChart.Plot.YLabel("Write Latency (ms)"); SetChartYLimitsWithLegendPadding(FileIoWriteChart, 0, writeMax > 0 ? writeMax : 10); @@ -2255,14 +2255,14 @@ private void UpdateFileIoThroughputCharts(List data) } } - FileIoReadThroughputChart.Plot.Axes.DateTimeTicksBottom(); + FileIoReadThroughputChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(FileIoReadThroughputChart); FileIoReadThroughputChart.Plot.YLabel("Read Throughput (MB/s)"); SetChartYLimitsWithLegendPadding(FileIoReadThroughputChart, 0, readMax > 0 ? readMax : 1); ShowChartLegend(FileIoReadThroughputChart); FileIoReadThroughputChart.Refresh(); - FileIoWriteThroughputChart.Plot.Axes.DateTimeTicksBottom(); + FileIoWriteThroughputChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(FileIoWriteThroughputChart); FileIoWriteThroughputChart.Plot.YLabel("Write Throughput (MB/s)"); SetChartYLimitsWithLegendPadding(FileIoWriteThroughputChart, 0, writeMax > 0 ? writeMax : 1); @@ -2298,7 +2298,7 @@ private void UpdateLockWaitTrendChart(List data, int hoursBa zeroLine.LegendText = "Lock Waits"; zeroLine.Color = ScottPlot.Color.FromHex("#4FC3F7"); zeroLine.MarkerSize = 0; - LockWaitTrendChart.Plot.Axes.DateTimeTicksBottom(); + LockWaitTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); LockWaitTrendChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(LockWaitTrendChart); LockWaitTrendChart.Plot.YLabel("Lock Wait Time (ms/sec)"); @@ -2325,7 +2325,7 @@ private void UpdateLockWaitTrendChart(List data, int hoursBa if (values.Length > 0) globalMax = Math.Max(globalMax, values.Max()); } - LockWaitTrendChart.Plot.Axes.DateTimeTicksBottom(); + LockWaitTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); LockWaitTrendChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(LockWaitTrendChart); LockWaitTrendChart.Plot.YLabel("Lock Wait Time (ms/sec)"); @@ -2362,7 +2362,7 @@ private void UpdateBlockingTrendChart(List data, int hoursBack, Date zeroLine.LegendText = "Blocking Incidents"; zeroLine.Color = ScottPlot.Color.FromHex("#E57373"); zeroLine.MarkerSize = 0; - BlockingTrendChart.Plot.Axes.DateTimeTicksBottom(); + BlockingTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); BlockingTrendChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(BlockingTrendChart); BlockingTrendChart.Plot.YLabel("Blocking Incidents"); @@ -2404,7 +2404,7 @@ private void UpdateBlockingTrendChart(List data, int hoursBack, Date plot.MarkerSize = 0; /* No markers, just lines */ _blockingTrendHover?.Add(plot, "Blocking Incidents"); - BlockingTrendChart.Plot.Axes.DateTimeTicksBottom(); + BlockingTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); BlockingTrendChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(BlockingTrendChart); BlockingTrendChart.Plot.YLabel("Blocking Incidents"); @@ -2441,7 +2441,7 @@ private void UpdateDeadlockTrendChart(List data, int hoursBack, Date zeroLine.LegendText = "Deadlocks"; zeroLine.Color = ScottPlot.Color.FromHex("#FFB74D"); zeroLine.MarkerSize = 0; - DeadlockTrendChart.Plot.Axes.DateTimeTicksBottom(); + DeadlockTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); DeadlockTrendChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(DeadlockTrendChart); DeadlockTrendChart.Plot.YLabel("Deadlocks"); @@ -2483,7 +2483,7 @@ private void UpdateDeadlockTrendChart(List data, int hoursBack, Date plot.MarkerSize = 0; /* No markers, just lines */ _deadlockTrendHover?.Add(plot, "Deadlocks"); - DeadlockTrendChart.Plot.Axes.DateTimeTicksBottom(); + DeadlockTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); DeadlockTrendChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(DeadlockTrendChart); DeadlockTrendChart.Plot.YLabel("Deadlocks"); @@ -2520,7 +2520,7 @@ private void UpdateCurrentWaitsDurationChart(List data, i zeroLine.LegendText = "Current Waits"; zeroLine.Color = ScottPlot.Color.FromHex("#4FC3F7"); zeroLine.MarkerSize = 0; - CurrentWaitsDurationChart.Plot.Axes.DateTimeTicksBottom(); + CurrentWaitsDurationChart.Plot.Axes.DateTimeTicksBottomDateChange(); CurrentWaitsDurationChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(CurrentWaitsDurationChart); CurrentWaitsDurationChart.Plot.YLabel("Total Wait Duration (ms)"); @@ -2550,7 +2550,7 @@ private void UpdateCurrentWaitsDurationChart(List data, i if (values.Length > 0) globalMax = Math.Max(globalMax, values.Max()); } - CurrentWaitsDurationChart.Plot.Axes.DateTimeTicksBottom(); + CurrentWaitsDurationChart.Plot.Axes.DateTimeTicksBottomDateChange(); CurrentWaitsDurationChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(CurrentWaitsDurationChart); CurrentWaitsDurationChart.Plot.YLabel("Total Wait Duration (ms)"); @@ -2585,7 +2585,7 @@ private void UpdateCurrentWaitsBlockedChart(List data, zeroLine.LegendText = "Blocked Sessions"; zeroLine.Color = ScottPlot.Color.FromHex("#E57373"); zeroLine.MarkerSize = 0; - CurrentWaitsBlockedChart.Plot.Axes.DateTimeTicksBottom(); + CurrentWaitsBlockedChart.Plot.Axes.DateTimeTicksBottomDateChange(); CurrentWaitsBlockedChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(CurrentWaitsBlockedChart); CurrentWaitsBlockedChart.Plot.YLabel("Blocked Sessions"); @@ -2615,7 +2615,7 @@ private void UpdateCurrentWaitsBlockedChart(List data, if (values.Length > 0) globalMax = Math.Max(globalMax, values.Max()); } - CurrentWaitsBlockedChart.Plot.Axes.DateTimeTicksBottom(); + CurrentWaitsBlockedChart.Plot.Axes.DateTimeTicksBottomDateChange(); CurrentWaitsBlockedChart.Plot.Axes.SetLimitsX(rangeStart.ToOADate(), rangeEnd.ToOADate()); ReapplyAxisColors(CurrentWaitsBlockedChart); CurrentWaitsBlockedChart.Plot.YLabel("Blocked Sessions"); @@ -2642,7 +2642,7 @@ private void UpdateQueryDurationTrendChart(List data) plot.Color = ScottPlot.Color.FromHex("#4FC3F7"); _queryDurationTrendHover?.Add(plot, "Query Duration"); - QueryDurationTrendChart.Plot.Axes.DateTimeTicksBottom(); + QueryDurationTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(QueryDurationTrendChart); QueryDurationTrendChart.Plot.YLabel("Duration (ms/sec)"); SetChartYLimitsWithLegendPadding(QueryDurationTrendChart, 0, values.Max()); @@ -2666,7 +2666,7 @@ private void UpdateProcDurationTrendChart(List data) plot.Color = ScottPlot.Color.FromHex("#81C784"); _procDurationTrendHover?.Add(plot, "Procedure Duration"); - ProcDurationTrendChart.Plot.Axes.DateTimeTicksBottom(); + ProcDurationTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(ProcDurationTrendChart); ProcDurationTrendChart.Plot.YLabel("Duration (ms/sec)"); SetChartYLimitsWithLegendPadding(ProcDurationTrendChart, 0, values.Max()); @@ -2690,7 +2690,7 @@ private void UpdateQueryStoreDurationTrendChart(List data) plot.Color = ScottPlot.Color.FromHex("#FFB74D"); _queryStoreDurationTrendHover?.Add(plot, "Query Store Duration"); - QueryStoreDurationTrendChart.Plot.Axes.DateTimeTicksBottom(); + QueryStoreDurationTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(QueryStoreDurationTrendChart); QueryStoreDurationTrendChart.Plot.YLabel("Duration (ms/sec)"); SetChartYLimitsWithLegendPadding(QueryStoreDurationTrendChart, 0, values.Max()); @@ -2855,7 +2855,7 @@ private void UpdateExecutionCountTrendChart(List data) plot.Color = ScottPlot.Color.FromHex("#BA68C8"); _executionCountTrendHover?.Add(plot, "Executions"); - ExecutionCountTrendChart.Plot.Axes.DateTimeTicksBottom(); + ExecutionCountTrendChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(ExecutionCountTrendChart); ExecutionCountTrendChart.Plot.YLabel("Executions/sec"); SetChartYLimitsWithLegendPadding(ExecutionCountTrendChart, 0, values.Max()); @@ -3415,7 +3415,7 @@ private async System.Threading.Tasks.Task UpdateWaitStatsChartFromPickerAsync() if (values.Length > 0) globalMax = Math.Max(globalMax, values.Max()); } - WaitStatsChart.Plot.Axes.DateTimeTicksBottom(); + WaitStatsChart.Plot.Axes.DateTimeTicksBottomDateChange(); DateTime rangeStart, rangeEnd; if (IsCustomRange && fromDate.HasValue && toDate.HasValue) { @@ -3579,7 +3579,7 @@ private async System.Threading.Tasks.Task UpdateMemoryClerksChartFromPickerAsync } } - MemoryClerksChart.Plot.Axes.DateTimeTicksBottom(); + MemoryClerksChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(MemoryClerksChart); MemoryClerksChart.Plot.YLabel("Memory (MB)"); SetChartYLimitsWithLegendPadding(MemoryClerksChart, 0, globalMax > 0 ? globalMax : 100); @@ -3773,7 +3773,7 @@ private async System.Threading.Tasks.Task UpdatePerfmonChartFromPickerAsync() if (values.Length > 0) globalMax = Math.Max(globalMax, values.Max()); } - PerfmonChart.Plot.Axes.DateTimeTicksBottom(); + PerfmonChart.Plot.Axes.DateTimeTicksBottomDateChange(); DateTime rangeStart, rangeEnd; if (IsCustomRange && fromDate.HasValue && toDate.HasValue) { @@ -3913,6 +3913,8 @@ private static void ApplyTheme(ScottPlot.WPF.WpfPlot chart) chart.Plot.Axes.Left.TickLabelStyle.ForeColor = textColor; chart.Plot.Axes.Bottom.Label.ForeColor = textColor; chart.Plot.Axes.Left.Label.ForeColor = textColor; + chart.Plot.Axes.Bottom.TickLabelStyle.FontSize = 13; + chart.Plot.Axes.Left.TickLabelStyle.FontSize = 13; // Set the WPF control Background to match so no white flash appears before ScottPlot's render loop fires chart.Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(figureBackground.R, figureBackground.G, figureBackground.B)); @@ -3970,6 +3972,8 @@ private static void ReapplyAxisColors(ScottPlot.WPF.WpfPlot chart) chart.Plot.Axes.Left.TickLabelStyle.ForeColor = textColor; chart.Plot.Axes.Bottom.Label.ForeColor = textColor; chart.Plot.Axes.Left.Label.ForeColor = textColor; + chart.Plot.Axes.Bottom.TickLabelStyle.FontSize = 13; + chart.Plot.Axes.Left.TickLabelStyle.FontSize = 13; } /// @@ -5309,7 +5313,7 @@ private void UpdateCollectorDurationChart(List data) colorIdx++; } - CollectorDurationChart.Plot.Axes.DateTimeTicksBottom(); + CollectorDurationChart.Plot.Axes.DateTimeTicksBottomDateChange(); ReapplyAxisColors(CollectorDurationChart); CollectorDurationChart.Plot.YLabel("Duration (ms)"); CollectorDurationChart.Plot.Axes.AutoScale(); diff --git a/Lite/Helpers/AxesExtensions.cs b/Lite/Helpers/AxesExtensions.cs new file mode 100644 index 0000000..fb19a60 --- /dev/null +++ b/Lite/Helpers/AxesExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace PerformanceMonitorLite.Helpers; + +internal static class AxesExtensions +{ + /// Culture's short-date pattern with the year component removed (e.g. "M/d" en-US, "dd/MM" en-GB, "dd.MM" de-DE). + private static readonly string MonthDayPattern = BuildMonthDayPattern(); + + private static string BuildMonthDayPattern() + { + var p = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; + p = Regex.Replace(p, @"y+", ""); + p = Regex.Replace(p, @"^[\s/.\-]+|[\s/.\-]+$", ""); + p = Regex.Replace(p, @"([/.\-\s])\1+", "$1"); + return string.IsNullOrWhiteSpace(p) ? "M/d" : p; + } + + /// + /// Like DateTimeTicksBottom(), but prints the date line on only the first tick + /// and on ticks where the date component changes. All other ticks show time-only. + /// Date and time formats follow the current culture. + /// + public static void DateTimeTicksBottomDateChange(this ScottPlot.AxisManager axes) + { + axes.DateTimeTicksBottom(); + if (axes.Bottom.TickGenerator is ScottPlot.TickGenerators.DateTimeAutomatic gen) + { + DateTime? lastDate = null; + var culture = CultureInfo.CurrentCulture; + gen.LabelFormatter = dt => + { + var time = dt.ToString("t", culture); + if (lastDate is null || dt.Date != lastDate.Value) + { + lastDate = dt.Date; + return $"{dt.ToString(MonthDayPattern, culture)}\n{time}"; + } + return time; + }; + } + } +} diff --git a/Lite/Themes/CoolBreezeTheme.xaml b/Lite/Themes/CoolBreezeTheme.xaml index d22edec..010d8a8 100644 --- a/Lite/Themes/CoolBreezeTheme.xaml +++ b/Lite/Themes/CoolBreezeTheme.xaml @@ -595,6 +595,40 @@ + + + diff --git a/Lite/Themes/DarkTheme.xaml b/Lite/Themes/DarkTheme.xaml index 3d64f59..869af3d 100644 --- a/Lite/Themes/DarkTheme.xaml +++ b/Lite/Themes/DarkTheme.xaml @@ -595,6 +595,40 @@ + + + diff --git a/Lite/Themes/LightTheme.xaml b/Lite/Themes/LightTheme.xaml index e47e241..e990830 100644 --- a/Lite/Themes/LightTheme.xaml +++ b/Lite/Themes/LightTheme.xaml @@ -595,6 +595,40 @@ + + +