Skip to content

Commit 86f0c23

Browse files
committed
Feature: Profile tags & filter
1 parent 74c896c commit 86f0c23

File tree

8 files changed

+122
-43
lines changed

8 files changed

+122
-43
lines changed

Source/NETworkManager.Profiles/ProfileInfo.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
using System.Security;
2-
using System.Xml.Serialization;
1+
using NETworkManager.Controls;
32
using NETworkManager.Models.Network;
43
using NETworkManager.Models.PowerShell;
54
using NETworkManager.Models.PuTTY;
65
using NETworkManager.Models.RemoteDesktop;
76
using NETworkManager.Settings;
7+
using System.Security;
8+
using System.Xml.Serialization;
89

910
// ReSharper disable InconsistentNaming
1011
// ReSharper disable PropertyCanBeMadeInitOnly.Global
@@ -33,7 +34,7 @@ public ProfileInfo(ProfileInfo profile)
3334
Host = profile.Host;
3435
Description = profile.Description;
3536
Group = profile.Group;
36-
Tags = profile.Tags;
37+
TagsCollection = profile.TagsCollection;
3738

3839
IsDynamic = profile.IsDynamic;
3940

@@ -42,7 +43,6 @@ public ProfileInfo(ProfileInfo profile)
4243
NetworkInterface_EnableStaticIPAddress = profile.NetworkInterface_EnableStaticIPAddress;
4344
NetworkInterface_IPAddress = profile.NetworkInterface_IPAddress;
4445
NetworkInterface_Subnetmask = profile.NetworkInterface_Subnetmask;
45-
NetworkInterface_SubnetmaskOrCidr = profile.NetworkInterface_SubnetmaskOrCidr;
4646
NetworkInterface_Gateway = profile.NetworkInterface_Gateway;
4747
NetworkInterface_EnableStaticDNS = profile.NetworkInterface_EnableStaticDNS;
4848
NetworkInterface_PrimaryDNSServer = profile.NetworkInterface_PrimaryDNSServer;
@@ -253,17 +253,23 @@ public ProfileInfo(ProfileInfo profile)
253253
/// Description of the profile.
254254
/// </summary>
255255
public string Description { get; set; }
256-
256+
257257
/// <summary>
258258
/// Name of the group. Profiles are grouped based on the name.
259259
/// </summary>
260260
public string Group { get; set; }
261-
261+
262262
/// <summary>
263263
/// Tags to classify the profiles and to filter by it.
264264
/// </summary>
265+
//[Obsolete("Replaced with TagsList")]
265266
public string Tags { get; set; }
266267

268+
/// <summary>
269+
/// Tags collection to classify the profiles and to filter by it.
270+
/// </summary>
271+
public ObservableSetCollection<string> TagsCollection { get; set; } = [];
272+
267273
/// <summary>
268274
/// Dynamic profiles (e.g. AWS) are not save to file.
269275
/// </summary>
@@ -273,9 +279,6 @@ public ProfileInfo(ProfileInfo profile)
273279
public bool NetworkInterface_Enabled { get; set; }
274280
public bool NetworkInterface_EnableStaticIPAddress { get; set; }
275281
public string NetworkInterface_IPAddress { get; set; }
276-
277-
//[Obsolete("Replaced by NetworkInterface_Subnetmask")]
278-
public string NetworkInterface_SubnetmaskOrCidr { get; set; }
279282
public string NetworkInterface_Subnetmask { get; set; }
280283
public string NetworkInterface_Gateway { get; set; }
281284
public bool NetworkInterface_EnableStaticDNS { get; set; }

Source/NETworkManager.Profiles/ProfileManager.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -657,12 +657,12 @@ private static List<GroupInfo> DeserializeGroup(Stream stream)
657657
return (from groupSerializable in ((List<GroupInfoSerializable>)xmlSerializer.Deserialize(stream))!
658658
let profiles = groupSerializable.Profiles.Select(profileSerializable => new ProfileInfo(profileSerializable)
659659
{
660-
// Migrate old data
661-
NetworkInterface_Subnetmask =
662-
string.IsNullOrEmpty(profileSerializable.NetworkInterface_Subnetmask) &&
663-
!string.IsNullOrEmpty(profileSerializable.NetworkInterface_SubnetmaskOrCidr)
664-
? profileSerializable.NetworkInterface_SubnetmaskOrCidr
665-
: profileSerializable.NetworkInterface_Subnetmask,
660+
// Migrate old tags to new tags list
661+
// if TagsList is null or empty and Tags is not null or empty, split Tags by ';' and create a new ObservableSetCollection
662+
// else use the existing TagsList
663+
TagsCollection = (profileSerializable.TagsCollection == null || profileSerializable.TagsCollection.Count == 0) && !string.IsNullOrEmpty(profileSerializable.Tags) ?
664+
new Controls.ObservableSetCollection<string>(profileSerializable.Tags.Split([';'], StringSplitOptions.RemoveEmptyEntries)) :
665+
profileSerializable.TagsCollection,
666666

667667
// Convert passwords to secure strings
668668
RemoteDesktop_Password = !string.IsNullOrEmpty(profileSerializable.RemoteDesktop_Password)

Source/NETworkManager.Settings/GlobalStaticConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public static class GlobalStaticConfiguration
2222
// Application config
2323
public static int ApplicationUIRefreshInterval => 2500;
2424

25+
public static int ApplicationUIDelayInterval => 1000;
26+
2527
// Type to search (average type speed --> 187 chars/min)
2628
public static TimeSpan SearchDispatcherTimerTimeSpan => new(0, 0, 0, 0, 750);
2729

Source/NETworkManager.Settings/SettingsInfo.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public string Version
113113
#region General
114114

115115
// General
116-
private ObservableSetCollection<ApplicationInfo> _general_ApplicationList = new();
116+
private ObservableSetCollection<ApplicationInfo> _general_ApplicationList = [];
117117

118118
public ObservableSetCollection<ApplicationInfo> General_ApplicationList
119119
{
@@ -3394,9 +3394,9 @@ public bool WebConsole_ShowAddressBar
33943394
OnPropertyChanged();
33953395
}
33963396
}
3397-
3397+
33983398
private bool _webConsole_IsStatusBarEnabled = GlobalStaticConfiguration.WebConsole_IsStatusBarEnabled;
3399-
3399+
34003400
public bool WebConsole_IsStatusBarEnabled
34013401
{
34023402
get => _webConsole_IsStatusBarEnabled;
@@ -3409,9 +3409,9 @@ public bool WebConsole_IsStatusBarEnabled
34093409
OnPropertyChanged();
34103410
}
34113411
}
3412-
3412+
34133413
private bool _webConsole_IsPasswordSaveEnabled = GlobalStaticConfiguration.WebConsole_IsPasswordSaveEnabled;
3414-
3414+
34153415
public bool WebConsole_IsPasswordSaveEnabled
34163416
{
34173417
get => _webConsole_IsPasswordSaveEnabled;
@@ -4277,7 +4277,7 @@ public ExportFileType BitCalculator_ExportFileType
42774277

42784278
#region Lookup
42794279

4280-
private ObservableCollection<string> _lookup_OUI_SearchHistory = new();
4280+
private ObservableCollection<string> _lookup_OUI_SearchHistory = [];
42814281

42824282
public ObservableCollection<string> Lookup_OUI_SearchHistory
42834283
{
@@ -4322,7 +4322,7 @@ public ExportFileType Lookup_OUI_ExportFileType
43224322
}
43234323
}
43244324

4325-
private ObservableCollection<string> _lookup_Port_SearchHistory = new();
4325+
private ObservableCollection<string> _lookup_Port_SearchHistory = [];
43264326

43274327
public ObservableCollection<string> Lookup_Port_SearchHistory
43284328
{

Source/NETworkManager/ProfileDialogManager.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ namespace NETworkManager;
1717

1818
public static class ProfileDialogManager
1919
{
20-
#region Variables
21-
22-
private static string DialogResourceKey => "LargeMetroDialog";
23-
24-
#endregion
25-
2620
#region Methods to add and remove profile
2721

2822
private static ProfileInfo ParseProfileInfo(ProfileViewModel instance)
@@ -33,7 +27,7 @@ private static ProfileInfo ParseProfileInfo(ProfileViewModel instance)
3327
Host = instance.Host.Trim(),
3428
Description = instance.Description?.Trim(),
3529
Group = instance.Group.Trim(),
36-
Tags = instance.Tags?.Trim(),
30+
TagsCollection = instance.TagsCollection,
3731

3832
// Network Interface
3933
NetworkInterface_Enabled = instance.NetworkInterface_Enabled,

Source/NETworkManager/ViewModels/ProfileViewModel.cs

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using NETworkManager.Localization.Resources;
1+
using NETworkManager.Controls;
2+
using NETworkManager.Localization.Resources;
23
using NETworkManager.Models;
34
using NETworkManager.Models.Network;
45
using NETworkManager.Models.PowerShell;
@@ -54,7 +55,9 @@ public ProfileViewModel(Action<ProfileViewModel> saveCommand, Action<ProfileView
5455
Groups = CollectionViewSource.GetDefaultView(groups);
5556
Groups.SortDescriptions.Add(new SortDescription());
5657

57-
Tags = profileInfo.Tags;
58+
TagsCollection = profileInfo.TagsCollection;
59+
Tags = CollectionViewSource.GetDefaultView(TagsCollection);
60+
Tags.SortDescriptions.Add(new SortDescription("", ListSortDirection.Ascending));
5861

5962
// Network Interface
6063
NetworkInterface_Enabled = editMode == ProfileEditMode.Add
@@ -487,21 +490,37 @@ public string Group
487490

488491
public ICollectionView Groups { get; }
489492

490-
private string _tags;
493+
private string _tag;
491494

492-
public string Tags
495+
public string Tag
493496
{
494-
get => _tags;
497+
get => _tag;
495498
set
496499
{
497-
if (value == _tags)
500+
if (value == _tag)
498501
return;
499502

500-
_tags = value;
503+
_tag = value;
501504
OnPropertyChanged();
502505
}
503506
}
504507

508+
public ICollectionView Tags { get; }
509+
510+
private ObservableSetCollection<string> _tagsCollection = [];
511+
512+
public ObservableSetCollection<string> TagsCollection
513+
{
514+
get => _tagsCollection;
515+
set
516+
{
517+
if (Equals(value, _tagsCollection))
518+
return;
519+
520+
_tagsCollection = value;
521+
OnPropertyChanged();
522+
}
523+
}
505524
#endregion
506525

507526
#region Network Interface
@@ -3288,7 +3307,7 @@ private async Task ResolveHostActionAsync()
32883307
{
32893308
IsResolveHostnameRunning = true;
32903309

3291-
await Task.Delay(GlobalStaticConfiguration.ApplicationUIRefreshInterval);
3310+
await Task.Delay(GlobalStaticConfiguration.ApplicationUIDelayInterval);
32923311

32933312
var dnsResult =
32943313
await DNSClientHelper.ResolveAorAaaaAsync(Host, SettingsManager.Current.Network_ResolveHostnamePreferIPv4);
@@ -3301,5 +3320,26 @@ private async Task ResolveHostActionAsync()
33013320
IsResolveHostnameRunning = false;
33023321
}
33033322

3323+
public ICommand AddTagCommand => new RelayCommand(_ => AddTagAction());
3324+
3325+
private void AddTagAction()
3326+
{
3327+
var tagsToAdd = Tag.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
3328+
3329+
foreach (var tag in tagsToAdd)
3330+
TagsCollection.Add(tag);
3331+
3332+
Tag = string.Empty;
3333+
}
3334+
3335+
public ICommand RemoveTagCommand => new RelayCommand(RemoveTagAction);
3336+
3337+
private void RemoveTagAction(object param)
3338+
{
3339+
if (param is not string tag)
3340+
return;
3341+
3342+
TagsCollection.Remove(tag);
3343+
}
33043344
#endregion
3305-
}
3345+
}

Source/NETworkManager/Views/ProfileChildWindow.xaml

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,8 @@
12921292
<RowDefinition Height="Auto" />
12931293
<RowDefinition Height="10" />
12941294
<RowDefinition Height="Auto" />
1295+
<RowDefinition Height="10" />
1296+
<RowDefinition Height="Auto" />
12951297
</Grid.RowDefinitions>
12961298
<Grid.ColumnDefinitions>
12971299
<ColumnDefinition Width="1*" />
@@ -1349,13 +1351,13 @@
13491351
</Style.Triggers>
13501352
</Style>
13511353
</Button.Resources>
1352-
<Rectangle Width="20" Height="20" Fill="{DynamicResource MahApps.Brushes.Gray3}">
1354+
<Rectangle Width="16" Height="16" Fill="{DynamicResource MahApps.Brushes.Gray3}">
13531355
<Rectangle.OpacityMask>
13541356
<VisualBrush Stretch="Uniform" Visual="{iconPacks:Material Kind=SearchWeb}" />
13551357
</Rectangle.OpacityMask>
13561358
</Rectangle>
13571359
</Button>
1358-
</Grid>
1360+
</Grid>
13591361
<mah:ProgressRing Grid.Column="3" Grid.Row="2" Width="24" Height="24" Margin="10,0,0,0"
13601362
Visibility="{Binding IsResolveHostnameRunning, Converter={StaticResource BooleanToVisibilityCollapsedConverter}}" />
13611363
<Rectangle Width="24" Height="24" Grid.Column="3" Grid.Row="2"
@@ -1424,20 +1426,58 @@
14241426
</Style.Triggers>
14251427
</Style>
14261428
</Button.Resources>
1427-
<Rectangle Width="20" Height="20" Fill="{DynamicResource MahApps.Brushes.Gray3}">
1429+
<Rectangle Width="16" Height="16" Fill="{DynamicResource MahApps.Brushes.Gray3}">
14281430
<Rectangle.OpacityMask>
14291431
<VisualBrush Stretch="Uniform" Visual="{iconPacks:Material Kind=Plus}" />
14301432
</Rectangle.OpacityMask>
14311433
</Rectangle>
14321434
</Button>
1433-
</Grid>
1435+
</Grid>
14341436
<Rectangle Width="24" Height="24" Grid.Column="3" Grid.Row="6"
14351437
ToolTip="{x:Static Member=localization:Strings.HelpMessage_Tags}"
14361438
Style="{StaticResource ResourceKey=HelpImageRectangle}" Margin="10,0,0,0">
14371439
<Rectangle.Resources>
14381440
<Style TargetType="{x:Type TypeName=ToolTip}" BasedOn="{StaticResource ResourceKey=HelpToolTip}" />
14391441
</Rectangle.Resources>
14401442
</Rectangle>
1443+
<ItemsControl Grid.Column="2" Grid.Row="10" ItemsSource="{Binding Path=Tags}">
1444+
<ItemsControl.ItemsPanel>
1445+
<ItemsPanelTemplate>
1446+
<WrapPanel/>
1447+
</ItemsPanelTemplate>
1448+
</ItemsControl.ItemsPanel>
1449+
<ItemsControl.ItemTemplate>
1450+
<DataTemplate>
1451+
<Border Margin="0,5,10,5" Padding="5"
1452+
BorderThickness="1" BorderBrush="{DynamicResource MahApps.Brushes.Gray8}"
1453+
CornerRadius="5">
1454+
<StackPanel Orientation="Horizontal">
1455+
<TextBlock Text="{Binding .}" Style="{StaticResource InfoTextBlock}" />
1456+
<Button Style="{StaticResource CleanButton}"
1457+
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type simpleChildWindow:ChildWindow}}, Path=DataContext.RemoveTagCommand}" CommandParameter="{Binding .}"
1458+
Margin="10,0,0,0">
1459+
<Rectangle Width="12" Height="12">
1460+
<Rectangle.OpacityMask>
1461+
<VisualBrush Stretch="Uniform" Visual="{iconPacks:Material Kind=WindowClose}" />
1462+
</Rectangle.OpacityMask>
1463+
<Rectangle.Style>
1464+
<Style TargetType="{x:Type Rectangle}">
1465+
<Setter Property="Fill" Value="{DynamicResource MahApps.Brushes.Gray3}" />
1466+
<Style.Triggers>
1467+
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=IsMouseOver}"
1468+
Value="True">
1469+
<Setter Property="Fill" Value="Red" />
1470+
</DataTrigger>
1471+
</Style.Triggers>
1472+
</Style>
1473+
</Rectangle.Style>
1474+
</Rectangle>
1475+
</Button>
1476+
</StackPanel>
1477+
</Border>
1478+
</DataTemplate>
1479+
</ItemsControl.ItemTemplate>
1480+
</ItemsControl>
14411481
</Grid>
14421482
</StackPanel>
14431483
</ScrollViewer>

Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
</Style.Triggers>
161161
</Style>
162162
</Button.Resources>
163-
<Rectangle Width="20" Height="20" Fill="{DynamicResource MahApps.Brushes.Gray3}">
163+
<Rectangle Width="16" Height="16" Fill="{DynamicResource MahApps.Brushes.Gray3}">
164164
<Rectangle.OpacityMask>
165165
<VisualBrush Stretch="Uniform" Visual="{iconPacks:Material Kind=Plus}" />
166166
</Rectangle.OpacityMask>

0 commit comments

Comments
 (0)