Skip to content

Commit 9c7680a

Browse files
Tidy up duplicate code
1 parent 6b35145 commit 9c7680a

2 files changed

Lines changed: 149 additions & 67 deletions

File tree

Dashboard/Controls/FinOpsContent.xaml.cs

Lines changed: 13 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ public partial class FinOpsContent : UserControl
3333
private DateTime _serverInventoryCacheTime;
3434
private decimal _currentServerMonthlyCost;
3535

36-
private List<FinOpsDatabaseSizeStats> _allDatabaseSizes = new();
37-
private readonly Dictionary<string, ColumnFilterState> _dbSizeColumnFilters = new();
36+
private DataGridFilterManager<FinOpsDatabaseSizeStats>? _dbSizesFilterMgr;
3837
private Popup? _dbSizeFilterPopup;
3938
private ColumnFilterPopup? _dbSizeFilterPopupContent;
4039

@@ -75,6 +74,8 @@ private void OnLoaded(object sender, RoutedEventArgs e)
7574
TabHelpers.FreezeColumns(ExpensiveQueriesDataGrid, 1);
7675
TabHelpers.FreezeColumns(IndexAnalysisDetailGrid, 1);
7776
TabHelpers.FreezeColumns(HighImpactDataGrid, 1);
77+
78+
_dbSizesFilterMgr = new DataGridFilterManager<FinOpsDatabaseSizeStats>(DatabaseSizesDataGrid);
7879
}
7980

8081
/// <summary>
@@ -629,54 +630,32 @@ private async Task LoadDatabaseSizesAsync()
629630
}
630631
}
631632

632-
_allDatabaseSizes = data;
633-
_dbSizeColumnFilters.Clear();
634-
ApplyDbSizeFilters();
635-
UpdateDbSizeFilterButtonStyles();
633+
_dbSizesFilterMgr!.UpdateData(data);
634+
UpdateDbSizeCountUI();
636635
}
637636
catch (Exception ex)
638637
{
639638
Logger.Error($"Error loading database sizes: {ex.Message}", ex);
640639
}
641640
}
642641

643-
private void ApplyDbSizeFilters()
642+
private void UpdateDbSizeCountUI()
644643
{
645-
var filtered = _allDatabaseSizes.AsEnumerable();
646-
647-
if (_dbSizeColumnFilters.Count > 0)
648-
{
649-
filtered = filtered.Where(item =>
650-
{
651-
foreach (var filter in _dbSizeColumnFilters.Values)
652-
{
653-
if (filter.IsActive && !DataGridFilterService.MatchesFilter(item, filter))
654-
return false;
655-
}
656-
return true;
657-
});
658-
}
659-
660-
var list = filtered.ToList();
661-
DatabaseSizesDataGrid.ItemsSource = list;
662-
DatabaseSizesNoDataMessage.Visibility = list.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
663-
DbSizeCountIndicator.Text = list.Count > 0 ? $"{list.Count} file(s)" : "";
644+
var list = DatabaseSizesDataGrid.ItemsSource as System.Collections.IList;
645+
int count = list?.Count ?? 0;
646+
DatabaseSizesNoDataMessage.Visibility = count == 0 ? Visibility.Visible : Visibility.Collapsed;
647+
DbSizeCountIndicator.Text = count > 0 ? $"{count} file(s)" : "";
664648
}
665649

666650
private void DatabaseSizesFilter_Click(object sender, RoutedEventArgs e)
667651
{
668652
if (sender is not Button button || button.Tag is not string columnName) return;
669-
ShowDbSizeFilterPopup(button, columnName);
670-
}
671653

672-
private void ShowDbSizeFilterPopup(Button button, string columnName)
673-
{
674654
if (_dbSizeFilterPopup == null)
675655
{
676656
_dbSizeFilterPopupContent = new ColumnFilterPopup();
677657
_dbSizeFilterPopupContent.FilterApplied += FilterPopup_DbSizeFilterApplied;
678658
_dbSizeFilterPopupContent.FilterCleared += FilterPopup_DbSizeFilterCleared;
679-
680659
_dbSizeFilterPopup = new Popup
681660
{
682661
Child = _dbSizeFilterPopupContent,
@@ -686,7 +665,7 @@ private void ShowDbSizeFilterPopup(Button button, string columnName)
686665
};
687666
}
688667

689-
_dbSizeColumnFilters.TryGetValue(columnName, out var existingFilter);
668+
_dbSizesFilterMgr!.Filters.TryGetValue(columnName, out var existingFilter);
690669
_dbSizeFilterPopupContent!.Initialize(columnName, existingFilter);
691670
_dbSizeFilterPopup.PlacementTarget = button;
692671
_dbSizeFilterPopup.IsOpen = true;
@@ -697,13 +676,8 @@ private void FilterPopup_DbSizeFilterApplied(object? sender, FilterAppliedEventA
697676
if (_dbSizeFilterPopup != null)
698677
_dbSizeFilterPopup.IsOpen = false;
699678

700-
if (e.FilterState.IsActive)
701-
_dbSizeColumnFilters[e.FilterState.ColumnName] = e.FilterState;
702-
else
703-
_dbSizeColumnFilters.Remove(e.FilterState.ColumnName);
704-
705-
ApplyDbSizeFilters();
706-
UpdateDbSizeFilterButtonStyles();
679+
_dbSizesFilterMgr!.SetFilter(e.FilterState);
680+
UpdateDbSizeCountUI();
707681
}
708682

709683
private void FilterPopup_DbSizeFilterCleared(object? sender, EventArgs e)
@@ -712,34 +686,6 @@ private void FilterPopup_DbSizeFilterCleared(object? sender, EventArgs e)
712686
_dbSizeFilterPopup.IsOpen = false;
713687
}
714688

715-
private void UpdateDbSizeFilterButtonStyles()
716-
{
717-
foreach (var column in DatabaseSizesDataGrid.Columns)
718-
{
719-
if (column.Header is StackPanel stackPanel)
720-
{
721-
var filterButton = stackPanel.Children.OfType<Button>().FirstOrDefault();
722-
if (filterButton != null && filterButton.Tag is string columnName)
723-
{
724-
bool hasActiveFilter = _dbSizeColumnFilters.TryGetValue(columnName, out var filter) && filter.IsActive;
725-
726-
var textBlock = new TextBlock
727-
{
728-
Text = "\uE71C",
729-
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
730-
Foreground = hasActiveFilter
731-
? new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(0xFF, 0xD7, 0x00))
732-
: new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(0xFF, 0xFF, 0xFF))
733-
};
734-
filterButton.Content = textBlock;
735-
filterButton.ToolTip = hasActiveFilter && filter != null
736-
? $"Filter: {filter.DisplayText}\n(Click to modify)"
737-
: "Click to filter";
738-
}
739-
}
740-
}
741-
}
742-
743689
// ============================================
744690
// Application Connections Tab
745691
// ============================================
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2026 Erik Darling, Darling Data LLC
3+
*
4+
* This file is part of the SQL Server Performance Monitor.
5+
*
6+
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
7+
*/
8+
9+
using System;
10+
using System.Collections.Generic;
11+
using System.Linq;
12+
using System.Windows;
13+
using System.Windows.Controls;
14+
using System.Windows.Media;
15+
using PerformanceMonitorDashboard.Models;
16+
17+
namespace PerformanceMonitorDashboard.Services;
18+
19+
/// <summary>
20+
/// Non-generic interface for looking up filter state from a shared dictionary.
21+
/// </summary>
22+
public interface IDataGridFilterManager
23+
{
24+
Dictionary<string, ColumnFilterState> Filters { get; }
25+
void SetFilter(ColumnFilterState filterState);
26+
void UpdateFilterButtonStyles();
27+
}
28+
29+
/// <summary>
30+
/// Manages column filter state, unfiltered data capture, and filter application
31+
/// for a single DataGrid. Eliminates per-grid boilerplate code.
32+
/// </summary>
33+
public class DataGridFilterManager<T> : IDataGridFilterManager
34+
{
35+
private readonly DataGrid _dataGrid;
36+
private readonly Dictionary<string, ColumnFilterState> _filters = new();
37+
private List<T>? _unfilteredData;
38+
39+
public DataGridFilterManager(DataGrid dataGrid)
40+
{
41+
_dataGrid = dataGrid;
42+
}
43+
44+
public Dictionary<string, ColumnFilterState> Filters => _filters;
45+
46+
/// <summary>
47+
/// Called when new data arrives (refresh cycle). Captures unfiltered data,
48+
/// then re-applies any active filters.
49+
/// </summary>
50+
public void UpdateData(List<T> newData)
51+
{
52+
_unfilteredData = newData;
53+
54+
if (!HasActiveFilters())
55+
{
56+
_dataGrid.ItemsSource = newData;
57+
return;
58+
}
59+
60+
ApplyFilters();
61+
}
62+
63+
/// <summary>
64+
/// Applies or removes a filter and re-filters the data.
65+
/// </summary>
66+
public void SetFilter(ColumnFilterState filterState)
67+
{
68+
if (filterState.IsActive)
69+
_filters[filterState.ColumnName] = filterState;
70+
else
71+
_filters.Remove(filterState.ColumnName);
72+
73+
ApplyFilters();
74+
UpdateFilterButtonStyles();
75+
}
76+
77+
private bool HasActiveFilters()
78+
{
79+
return _filters.Count > 0 && _filters.Values.Any(f => f.IsActive);
80+
}
81+
82+
private void ApplyFilters()
83+
{
84+
if (_unfilteredData == null) return;
85+
86+
if (!HasActiveFilters())
87+
{
88+
_dataGrid.ItemsSource = _unfilteredData;
89+
return;
90+
}
91+
92+
var filteredData = _unfilteredData.Where(item =>
93+
{
94+
foreach (var filter in _filters.Values)
95+
{
96+
if (filter.IsActive && !DataGridFilterService.MatchesFilter(item!, filter))
97+
return false;
98+
}
99+
return true;
100+
}).ToList();
101+
102+
_dataGrid.ItemsSource = filteredData;
103+
}
104+
105+
/// <summary>
106+
/// Updates filter icon colors (gold when active, dim when inactive).
107+
/// </summary>
108+
public void UpdateFilterButtonStyles()
109+
{
110+
foreach (var column in _dataGrid.Columns)
111+
{
112+
if (column.Header is StackPanel headerPanel)
113+
{
114+
var filterButton = headerPanel.Children.OfType<Button>().FirstOrDefault();
115+
if (filterButton != null && filterButton.Tag is string columnName)
116+
{
117+
bool hasActive = _filters.TryGetValue(columnName, out var filter) && filter.IsActive;
118+
119+
var textBlock = new TextBlock
120+
{
121+
Text = hasActive ? "\uE16E" : "\uE71C",
122+
FontFamily = new FontFamily("Segoe MDL2 Assets"),
123+
Foreground = hasActive
124+
? new SolidColorBrush(Color.FromRgb(0xFF, 0xD7, 0x00))
125+
: (Brush)Application.Current.FindResource("ForegroundDimBrush")
126+
};
127+
filterButton.Content = textBlock;
128+
129+
filterButton.ToolTip = hasActive && filter != null
130+
? $"Filter: {filter.DisplayText}\n(Click to modify)"
131+
: "Click to filter";
132+
}
133+
}
134+
}
135+
}
136+
}

0 commit comments

Comments
 (0)