Skip to content

Commit a1abbbc

Browse files
committed
Feature: Profile filter
1 parent 9b2bb36 commit a1abbbc

File tree

8 files changed

+531
-80
lines changed

8 files changed

+531
-80
lines changed

Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs

Lines changed: 139 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ namespace NETworkManager.ViewModels;
2222
public class IPScannerHostViewModel : ViewModelBase, IProfileManager
2323
{
2424
#region Variables
25-
26-
private readonly IDialogCoordinator _dialogCoordinator;
2725
private readonly DispatcherTimer _searchDispatcherTimer = new();
2826

2927
public IInterTabClient InterTabClient { get; }
@@ -129,6 +127,70 @@ public bool IsSearching
129127
OnPropertyChanged();
130128
}
131129
}
130+
131+
private bool _profileFilterIsOpen;
132+
133+
public bool ProfileFilterIsOpen
134+
{
135+
get => _profileFilterIsOpen;
136+
set
137+
{
138+
if (value == _profileFilterIsOpen)
139+
return;
140+
141+
_profileFilterIsOpen = value;
142+
OnPropertyChanged();
143+
}
144+
}
145+
146+
public ICollectionView ProfileFilterTagsView { get; }
147+
148+
private ObservableCollection<ProfileFilterTagsInfo> ProfileFilterTags { get; } = [];
149+
150+
private bool _profileFilterTagsMatchAny = GlobalStaticConfiguration.Profile_TagsMatchAny;
151+
152+
public bool ProfileFilterTagsMatchAny
153+
{
154+
get => _profileFilterTagsMatchAny;
155+
set
156+
{
157+
if (value == _profileFilterTagsMatchAny)
158+
return;
159+
160+
_profileFilterTagsMatchAny = value;
161+
OnPropertyChanged();
162+
}
163+
}
164+
165+
private bool _profileFilterTagsMatchAll;
166+
167+
public bool ProfileFilterTagsMatchAll
168+
{
169+
get => _profileFilterTagsMatchAll;
170+
set
171+
{
172+
if (value == _profileFilterTagsMatchAll)
173+
return;
174+
175+
_profileFilterTagsMatchAll = value;
176+
OnPropertyChanged();
177+
}
178+
}
179+
180+
private bool _isProfileFilterSet;
181+
182+
public bool IsProfileFilterSet
183+
{
184+
get => _isProfileFilterSet;
185+
set
186+
{
187+
if (value == _isProfileFilterSet)
188+
return;
189+
190+
_isProfileFilterSet = value;
191+
OnPropertyChanged();
192+
}
193+
}
132194

133195
private bool _canProfileWidthChange = true;
134196
private double _tempProfileWidth;
@@ -184,14 +246,12 @@ public GridLength ProfileWidth
184246

185247
#region Constructor, load settings
186248

187-
public IPScannerHostViewModel(IDialogCoordinator instance)
249+
public IPScannerHostViewModel()
188250
{
189251
_isLoading = true;
190252

191-
_dialogCoordinator = instance;
192-
193253
InterTabClient = new DragablzInterTabClient(ApplicationName.IPScanner);
194-
InterTabPartition = ApplicationName.IPScanner.ToString();
254+
InterTabPartition = nameof(ApplicationName.IPScanner);
195255

196256
var tabId = Guid.NewGuid();
197257

@@ -201,7 +261,12 @@ public IPScannerHostViewModel(IDialogCoordinator instance)
201261
];
202262

203263
// Profiles
204-
SetProfilesView();
264+
CreateTags();
265+
266+
ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags);
267+
ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), ListSortDirection.Ascending));
268+
269+
SetProfilesView(new ProfileFilterInfo());
205270

206271
ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated;
207272

@@ -297,6 +362,36 @@ private void ClearSearchAction()
297362
{
298363
Search = string.Empty;
299364
}
365+
366+
public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction());
367+
368+
private void OpenProfileFilterAction()
369+
{
370+
ProfileFilterIsOpen = true;
371+
}
372+
373+
public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction());
374+
375+
private void ApplyProfileFilterAction()
376+
{
377+
RefreshProfiles();
378+
379+
IsProfileFilterSet = true;
380+
ProfileFilterIsOpen = false;
381+
}
382+
383+
public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction());
384+
385+
private void ClearProfileFilterAction()
386+
{
387+
foreach (var tag in ProfileFilterTags)
388+
tag.IsSelected = false;
389+
390+
RefreshProfiles();
391+
392+
IsProfileFilterSet = false;
393+
ProfileFilterIsOpen = false;
394+
}
300395

301396
public ItemActionCallback CloseItemCommand => CloseItemAction;
302397

@@ -364,37 +459,43 @@ public void OnViewHide()
364459
{
365460
_isViewActive = false;
366461
}
367-
368-
private void SetProfilesView(ProfileInfo profile = null)
462+
463+
private void CreateTags()
369464
{
370-
Profiles = new CollectionViewSource
371-
{
372-
Source = ProfileManager.Groups.SelectMany(x => x.Profiles).Where(x => x.IPScanner_Enabled)
373-
.OrderBy(x => x.Group).ThenBy(x => x.Name)
374-
}.View;
465+
var tags = ProfileManager.Groups.SelectMany(x => x.Profiles).Where(x => x.IPScanner_Enabled).SelectMany(x => x.TagsCollection).Distinct().ToList();
375466

376-
Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group)));
467+
var tagSet = new HashSet<string>(tags);
377468

378-
Profiles.Filter = o =>
469+
for (var i = ProfileFilterTags.Count - 1; i >= 0; i--)
379470
{
380-
if (string.IsNullOrEmpty(Search))
381-
return true;
471+
if (!tagSet.Contains(ProfileFilterTags[i].Name))
472+
ProfileFilterTags.RemoveAt(i);
473+
}
382474

383-
if (o is not ProfileInfo info)
384-
return false;
475+
var existingTagNames = new HashSet<string>(ProfileFilterTags.Select(ft => ft.Name));
385476

386-
var search = Search.Trim();
477+
foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag)))
478+
{
479+
ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag));
480+
}
481+
}
387482

388-
// Search by: Tag=xxx (exact match, ignore case)
389-
/*
390-
if (search.StartsWith(ProfileManager.TagIdentifier, StringComparison.OrdinalIgnoreCase))
391-
return !string.IsNullOrEmpty(info.Tags) && info.PingMonitor_Enabled && info.Tags.Replace(" ", "").Split(';').Any(str => search.Substring(ProfileManager.TagIdentifier.Length, search.Length - ProfileManager.TagIdentifier.Length).Equals(str, StringComparison.OrdinalIgnoreCase));
392-
*/
483+
private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null)
484+
{
485+
Profiles = new CollectionViewSource
486+
{
487+
Source = ProfileManager.Groups.SelectMany(x => x.Profiles).Where(x => x.IPScanner_Enabled && (
488+
string.IsNullOrEmpty(filter.Search) || x.Name.IndexOf(filter.Search, StringComparison.Ordinal) > -1 || x.IPScanner_HostOrIPRange.IndexOf(filter.Search, StringComparison.Ordinal) > -1) && (
489+
// If no tags are selected, show all profiles
490+
(!filter.Tags.Any()) ||
491+
// Any tag can match
492+
(filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) ||
493+
// All tags must match
494+
(filter.TagsFilterMatch == ProfileFilterTagsMatch.All && filter.Tags.All(tag => x.TagsCollection.Contains(tag))))
495+
).OrderBy(x => x.Group).ThenBy(x => x.Name)
496+
}.View;
393497

394-
// Search by: Name, IPScanner_HostOrIPRange
395-
return info.Name.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1 ||
396-
info.IPScanner_HostOrIPRange.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1;
397-
};
498+
Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group)));
398499

399500
// Set specific profile or first if null
400501
SelectedProfile = null;
@@ -411,7 +512,12 @@ private void RefreshProfiles()
411512
if (!_isViewActive)
412513
return;
413514

414-
SetProfilesView(SelectedProfile);
515+
SetProfilesView(new ProfileFilterInfo
516+
{
517+
Search = Search,
518+
Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)],
519+
TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All
520+
}, SelectedProfile);
415521
}
416522

417523
#endregion
@@ -420,6 +526,8 @@ private void RefreshProfiles()
420526

421527
private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e)
422528
{
529+
CreateTags();
530+
423531
RefreshProfiles();
424532
}
425533

Source/NETworkManager/ViewModels/NetworkInterfaceViewModel.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,10 +1439,9 @@ private void CreateTags()
14391439

14401440
var existingTagNames = new HashSet<string>(ProfileFilterTags.Select(ft => ft.Name));
14411441

1442-
foreach (var tag in tags)
1442+
foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag)))
14431443
{
1444-
if (!existingTagNames.Contains(tag))
1445-
ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag));
1444+
ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag));
14461445
}
14471446
}
14481447

@@ -1451,7 +1450,7 @@ private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = nul
14511450
Profiles = new CollectionViewSource
14521451
{
14531452
Source = ProfileManager.Groups.SelectMany(x => x.Profiles).Where(x => x.NetworkInterface_Enabled && (
1454-
string.IsNullOrEmpty(filter.Search) || x.Name.IndexOf(filter.Search) > -1) && (
1453+
string.IsNullOrEmpty(filter.Search) || x.Name.IndexOf(filter.Search, StringComparison.Ordinal) > -1) && (
14551454
// If no tags are selected, show all profiles
14561455
(!filter.Tags.Any()) ||
14571456
// Any tag can match
@@ -1492,6 +1491,8 @@ private void RefreshProfiles()
14921491

14931492
private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e)
14941493
{
1494+
CreateTags();
1495+
14951496
RefreshProfiles();
14961497
}
14971498

Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ public bool ProfileFilterIsOpen
227227
}
228228
}
229229

230-
public ICollectionView ProfileFilterTagsView { get; set; }
230+
public ICollectionView ProfileFilterTagsView { get; }
231231

232-
public ObservableCollection<ProfileFilterTagsInfo> ProfileFilterTags { get; set; } = [];
232+
private ObservableCollection<ProfileFilterTagsInfo> ProfileFilterTags { get; } = [];
233233

234234
private bool _profileFilterTagsMatchAny = GlobalStaticConfiguration.Profile_TagsMatchAny;
235235

@@ -693,10 +693,9 @@ private void CreateTags()
693693

694694
var existingTagNames = new HashSet<string>(ProfileFilterTags.Select(ft => ft.Name));
695695

696-
foreach (var tag in tags)
696+
foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag)))
697697
{
698-
if (!existingTagNames.Contains(tag))
699-
ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag));
698+
ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag));
700699
}
701700
}
702701

@@ -705,7 +704,7 @@ private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = nul
705704
Profiles = new CollectionViewSource
706705
{
707706
Source = ProfileManager.Groups.SelectMany(x => x.Profiles).Where(x => x.PingMonitor_Enabled && (
708-
string.IsNullOrEmpty(filter.Search) || x.Name.IndexOf(filter.Search) > -1 || x.PingMonitor_Host.IndexOf(filter.Search) > -1) && (
707+
string.IsNullOrEmpty(filter.Search) || x.Name.IndexOf(filter.Search, StringComparison.Ordinal) > -1 || x.PingMonitor_Host.IndexOf(filter.Search, StringComparison.Ordinal) > -1) && (
709708
// If no tags are selected, show all profiles
710709
(!filter.Tags.Any()) ||
711710
// Any tag can match

0 commit comments

Comments
 (0)