Skip to content

Commit 9ad9721

Browse files
authored
Feature: Profile expander state (#3171)
* Feature: Profile expander state * Feature: Expander state * Chore: Refactoring * Feature: Expander state * Feature: Expander state * Feature: Expander state * Docs: #3162
1 parent fd3e864 commit 9ad9721

40 files changed

+1249
-114
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System.ComponentModel;
2+
using System.Windows;
3+
using System.Windows.Controls;
4+
5+
namespace NETworkManager.Controls
6+
{
7+
public class GroupExpander : Expander
8+
{
9+
public static readonly DependencyProperty StateStoreProperty =
10+
DependencyProperty.Register(
11+
"StateStore",
12+
typeof(GroupExpanderStateStore),
13+
typeof(GroupExpander),
14+
new PropertyMetadata(null, OnStateStoreChanged));
15+
16+
public GroupExpanderStateStore StateStore
17+
{
18+
get => (GroupExpanderStateStore)GetValue(StateStoreProperty);
19+
set => SetValue(StateStoreProperty, value);
20+
}
21+
22+
public static readonly DependencyProperty GroupNameProperty =
23+
DependencyProperty.Register(
24+
nameof(GroupName),
25+
typeof(string),
26+
typeof(GroupExpander),
27+
new PropertyMetadata(null, OnGroupNameChanged));
28+
29+
public string GroupName
30+
{
31+
get => (string)GetValue(GroupNameProperty);
32+
set => SetValue(GroupNameProperty, value);
33+
}
34+
35+
static GroupExpander()
36+
{
37+
IsExpandedProperty.OverrideMetadata(
38+
typeof(GroupExpander),
39+
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsExpandedChanged));
40+
}
41+
42+
private static void OnIsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
43+
{
44+
var expander = (GroupExpander)d;
45+
46+
if (expander.StateStore != null && expander.GroupName != null)
47+
expander.StateStore[expander.GroupName] = (bool)e.NewValue;
48+
}
49+
50+
private static void OnStateStoreChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
51+
{
52+
var expander = (GroupExpander)d;
53+
54+
if (e.OldValue is GroupExpanderStateStore oldStore)
55+
oldStore.PropertyChanged -= expander.StateStore_PropertyChanged;
56+
57+
if (e.NewValue is GroupExpanderStateStore newStore)
58+
newStore.PropertyChanged += expander.StateStore_PropertyChanged;
59+
60+
expander.UpdateIsExpanded();
61+
}
62+
63+
private void StateStore_PropertyChanged(object sender, PropertyChangedEventArgs e)
64+
{
65+
if (e.PropertyName == $"Item[{GroupName}]")
66+
UpdateIsExpanded();
67+
}
68+
69+
private static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
70+
{
71+
var expander = (GroupExpander)d;
72+
73+
expander.UpdateIsExpanded();
74+
}
75+
76+
private void UpdateIsExpanded()
77+
{
78+
if (StateStore == null || GroupName == null)
79+
return;
80+
81+
// Prevent recursive updates
82+
if (IsExpanded == StateStore[GroupName])
83+
return;
84+
85+
IsExpanded = StateStore[GroupName];
86+
}
87+
}
88+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Collections.Generic;
2+
using System.ComponentModel;
3+
using System.Diagnostics;
4+
5+
namespace NETworkManager.Controls
6+
{
7+
public class GroupExpanderStateStore : INotifyPropertyChanged
8+
{
9+
public event PropertyChangedEventHandler PropertyChanged;
10+
11+
protected void OnPropertyChanged(string propertyName) =>
12+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
13+
14+
/// <summary>
15+
/// Stores the expansion state of each group by its name.
16+
/// </summary>
17+
private readonly Dictionary<string, bool> _states = [];
18+
19+
/// <summary>
20+
/// The indexer to get or set the expansion state of a group by its name.
21+
/// </summary>
22+
/// <param name="groupName">Name of the group.</param>
23+
/// <returns>True if expanded, false if collapsed.</returns>
24+
public bool this[string groupName]
25+
{
26+
get
27+
{
28+
// Default to expanded if not set
29+
if (!_states.TryGetValue(groupName, out var val))
30+
_states[groupName] = val = true;
31+
32+
return val;
33+
}
34+
set
35+
{
36+
if (_states.TryGetValue(groupName, out var existing) && existing == value)
37+
return;
38+
39+
Debug.WriteLine("GroupExpanderStateStore: Setting state of '{0}' to {1}", groupName, value);
40+
41+
_states[groupName] = value;
42+
OnPropertyChanged($"Item[{groupName}]");
43+
}
44+
}
45+
}
46+
}

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3987,4 +3987,10 @@ Right-click for more options.</value>
39873987
<data name="AddGroupDots" xml:space="preserve">
39883988
<value>Add group...</value>
39893989
</data>
3990+
<data name="ExpandAll" xml:space="preserve">
3991+
<value>Expand all</value>
3992+
</data>
3993+
<data name="CollapseAll" xml:space="preserve">
3994+
<value>Collapse all</value>
3995+
</data>
39903996
</root>

Source/NETworkManager/ViewModels/AWSSessionManagerHostViewModel.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ public bool IsProfileFilterSet
308308
OnPropertyChanged();
309309
}
310310
}
311+
312+
private readonly GroupExpanderStateStore _groupExpanderStateStore = new();
313+
public GroupExpanderStateStore GroupExpanderStateStore => _groupExpanderStateStore;
311314

312315
private bool _canProfileWidthChange = true;
313316
private double _tempProfileWidth;
@@ -625,6 +628,20 @@ private void ClearProfileFilterAction()
625628
ProfileFilterIsOpen = false;
626629
}
627630

631+
public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction());
632+
633+
private void ExpandAllProfileGroupsAction()
634+
{
635+
SetIsExpandedForAllProfileGroups(true);
636+
}
637+
638+
public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction());
639+
640+
private void CollapseAllProfileGroupsAction()
641+
{
642+
SetIsExpandedForAllProfileGroups(false);
643+
}
644+
628645
public ICommand OpenDocumentationCommand
629646
{
630647
get { return new RelayCommand(_ => OpenDocumentationAction()); }
@@ -1011,6 +1028,12 @@ private static void AddRegionToHistory(string region)
10111028
SettingsManager.Current.General_HistoryListEntries));
10121029
}
10131030

1031+
private void SetIsExpandedForAllProfileGroups(bool isExpanded)
1032+
{
1033+
foreach (var group in Profiles.Groups.Cast<CollectionViewGroup>())
1034+
GroupExpanderStateStore[group.Name.ToString()] = isExpanded;
1035+
}
1036+
10141037
private void ResizeProfile(bool dueToChangedSize)
10151038
{
10161039
_canProfileWidthChange = false;

Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ public bool IsProfileFilterSet
196196
}
197197
}
198198

199+
private readonly GroupExpanderStateStore _groupExpanderStateStore = new();
200+
public GroupExpanderStateStore GroupExpanderStateStore => _groupExpanderStateStore;
201+
199202
private bool _canProfileWidthChange = true;
200203
private double _tempProfileWidth;
201204

@@ -399,6 +402,20 @@ private void ClearProfileFilterAction()
399402
ProfileFilterIsOpen = false;
400403
}
401404

405+
public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction());
406+
407+
private void ExpandAllProfileGroupsAction()
408+
{
409+
SetIsExpandedForAllProfileGroups(true);
410+
}
411+
412+
public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction());
413+
414+
private void CollapseAllProfileGroupsAction()
415+
{
416+
SetIsExpandedForAllProfileGroups(false);
417+
}
418+
402419
public ItemActionCallback CloseItemCommand => CloseItemAction;
403420

404421
private static void CloseItemAction(ItemActionCallbackArgs<TabablzControl> args)
@@ -410,6 +427,12 @@ private static void CloseItemAction(ItemActionCallbackArgs<TabablzControl> args)
410427

411428
#region Methods
412429

430+
private void SetIsExpandedForAllProfileGroups(bool isExpanded)
431+
{
432+
foreach (var group in Profiles.Groups.Cast<CollectionViewGroup>())
433+
GroupExpanderStateStore[group.Name.ToString()] = isExpanded;
434+
}
435+
413436
private void ResizeProfile(bool dueToChangedSize)
414437
{
415438
_canProfileWidthChange = false;

Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ public bool IsProfileFilterSet
197197
}
198198
}
199199

200+
private readonly GroupExpanderStateStore _groupExpanderStateStore = new();
201+
public GroupExpanderStateStore GroupExpanderStateStore => _groupExpanderStateStore;
202+
200203
private bool _canProfileWidthChange = true;
201204
private double _tempProfileWidth;
202205

@@ -399,6 +402,20 @@ private void ClearProfileFilterAction()
399402
ProfileFilterIsOpen = false;
400403
}
401404

405+
public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction());
406+
407+
private void ExpandAllProfileGroupsAction()
408+
{
409+
SetIsExpandedForAllProfileGroups(true);
410+
}
411+
412+
public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction());
413+
414+
private void CollapseAllProfileGroupsAction()
415+
{
416+
SetIsExpandedForAllProfileGroups(false);
417+
}
418+
402419
public ItemActionCallback CloseItemCommand => CloseItemAction;
403420

404421
private static void CloseItemAction(ItemActionCallbackArgs<TabablzControl> args)
@@ -410,6 +427,12 @@ private static void CloseItemAction(ItemActionCallbackArgs<TabablzControl> args)
410427

411428
#region Methods
412429

430+
private void SetIsExpandedForAllProfileGroups(bool isExpanded)
431+
{
432+
foreach (var group in Profiles.Groups.Cast<CollectionViewGroup>())
433+
GroupExpanderStateStore[group.Name.ToString()] = isExpanded;
434+
}
435+
413436
private void ResizeProfile(bool dueToChangedSize)
414437
{
415438
_canProfileWidthChange = false;

Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ public bool IsProfileFilterSet
196196
OnPropertyChanged();
197197
}
198198
}
199+
200+
private readonly GroupExpanderStateStore _groupExpanderStateStore = new();
201+
public GroupExpanderStateStore GroupExpanderStateStore => _groupExpanderStateStore;
199202

200203
private bool _canProfileWidthChange = true;
201204
private double _tempProfileWidth;
@@ -398,6 +401,20 @@ private void ClearProfileFilterAction()
398401
IsProfileFilterSet = false;
399402
ProfileFilterIsOpen = false;
400403
}
404+
405+
public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction());
406+
407+
private void ExpandAllProfileGroupsAction()
408+
{
409+
SetIsExpandedForAllProfileGroups(true);
410+
}
411+
412+
public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction());
413+
414+
private void CollapseAllProfileGroupsAction()
415+
{
416+
SetIsExpandedForAllProfileGroups(false);
417+
}
401418

402419
public ItemActionCallback CloseItemCommand => CloseItemAction;
403420

@@ -410,6 +427,12 @@ private static void CloseItemAction(ItemActionCallbackArgs<TabablzControl> args)
410427

411428
#region Methods
412429

430+
private void SetIsExpandedForAllProfileGroups(bool isExpanded)
431+
{
432+
foreach (var group in Profiles.Groups.Cast<CollectionViewGroup>())
433+
GroupExpanderStateStore[group.Name.ToString()] = isExpanded;
434+
}
435+
413436
private void ResizeProfile(bool dueToChangedSize)
414437
{
415438
_canProfileWidthChange = false;

0 commit comments

Comments
 (0)