diff --git a/Source/NETworkManager.Profiles/IProfileHostViewModel.cs b/Source/NETworkManager.Profiles/IProfileHostViewModel.cs new file mode 100644 index 0000000000..dd79ff52ff --- /dev/null +++ b/Source/NETworkManager.Profiles/IProfileHostViewModel.cs @@ -0,0 +1,83 @@ +using NETworkManager.Controls; +using System.ComponentModel; +using System.Windows.Input; + +namespace NETworkManager.Profiles; + +/// +/// Interface for a view model that hosts the shared profile panel (search, tag filter, group list) next to a +/// tool-specific view, extending with the additional members the reusable +/// profile panel control binds to. +/// +public interface IProfileHostViewModel : IProfileManager +{ + /// + /// Gets or sets the selected profile. + /// + ProfileInfo SelectedProfile { get; set; } + + /// + /// Gets or sets the search text. + /// + string Search { get; set; } + + /// + /// Gets or sets a value indicating whether a search is in progress. + /// + bool IsSearching { get; set; } + + /// + /// Gets or sets a value indicating whether the profile filter popup is open. + /// + bool ProfileFilterIsOpen { get; set; } + + /// + /// Gets the view for the profile filter tags. + /// + ICollectionView ProfileFilterTagsView { get; } + + /// + /// Gets or sets a value indicating whether to match any profile filter tag. + /// + bool ProfileFilterTagsMatchAny { get; set; } + + /// + /// Gets or sets a value indicating whether to match all profile filter tags. + /// + bool ProfileFilterTagsMatchAll { get; set; } + + /// + /// Gets or sets a value indicating whether a profile filter is currently applied. + /// + bool IsProfileFilterSet { get; set; } + + /// + /// Gets the group expander state store for the profile list. + /// + GroupExpanderStateStore GroupExpanderStateStore { get; } + + /// + /// Gets the command to open the profile filter popup. + /// + ICommand OpenProfileFilterCommand { get; } + + /// + /// Gets the command to apply the profile filter. + /// + ICommand ApplyProfileFilterCommand { get; } + + /// + /// Gets the command to clear the profile filter. + /// + ICommand ClearProfileFilterCommand { get; } + + /// + /// Gets the command to expand all profile groups. + /// + ICommand ExpandAllProfileGroupsCommand { get; } + + /// + /// Gets the command to collapse all profile groups. + /// + ICommand CollapseAllProfileGroupsCommand { get; } +} diff --git a/Source/NETworkManager.Profiles/NETworkManager.Profiles.csproj b/Source/NETworkManager.Profiles/NETworkManager.Profiles.csproj index 5af1160527..d102667fd3 100644 --- a/Source/NETworkManager.Profiles/NETworkManager.Profiles.csproj +++ b/Source/NETworkManager.Profiles/NETworkManager.Profiles.csproj @@ -25,6 +25,7 @@ + diff --git a/Source/NETworkManager.Settings/ProfileViewState.cs b/Source/NETworkManager.Settings/ProfileViewState.cs new file mode 100644 index 0000000000..9f4c0e8103 --- /dev/null +++ b/Source/NETworkManager.Settings/ProfileViewState.cs @@ -0,0 +1,123 @@ +using System; +using System.Windows; +using NETworkManager.Utilities; + +namespace NETworkManager.Settings; + +/// +/// Shared expanded/width state of the profile panel, used by every tool that hosts a profile list. Because all +/// tools observe the same instance, resizing or collapsing the profile panel in one tool +/// is reflected in every other tool automatically through data binding - no manual synchronization needed. +/// +/// +/// reads the first time it is accessed. This is +/// only safe once has completed (done early in App.xaml.cs, before the main +/// window - and therefore any tool view - is created). Do not reference from eagerly +/// loaded resources (e.g. App.xaml or globally merged style dictionaries). +/// +public sealed class ProfileViewState : PropertyChangedBase +{ + public static ProfileViewState Current { get; } + + static ProfileViewState() + { + Current = new ProfileViewState(); + } + + private readonly bool _isLoading; + private bool _canProfileWidthChange = true; + private double _tempProfileWidth; + + private ProfileViewState() + { + _isLoading = true; + + // Must be set before ExpandProfileView below, since assigning it can synchronously trigger + // ResizeProfile(false), which reads _tempProfileWidth. + _tempProfileWidth = SettingsManager.Current.Profile_Width; + + ExpandProfileView = SettingsManager.Current.Profile_ExpandView; + + ProfileWidth = ExpandProfileView + ? new GridLength(SettingsManager.Current.Profile_Width) + : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + + _isLoading = false; + } + + /// + /// Gets or sets a value indicating whether the profile panel is expanded, shared across all tools. + /// + public bool ExpandProfileView + { + get; + set + { + if (value == field) + return; + + if (!_isLoading) + SettingsManager.Current.Profile_ExpandView = value; + + field = value; + + if (_canProfileWidthChange) + ResizeProfile(false); + + OnPropertyChanged(); + } + } + + /// + /// Gets or sets the width of the profile panel, shared across all tools. + /// + public GridLength ProfileWidth + { + get; + set + { + if (value == field) + return; + + if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > + GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed + SettingsManager.Current.Profile_Width = value.Value; + + field = value; + + if (_canProfileWidthChange) + ResizeProfile(true); + + OnPropertyChanged(); + } + } + + private void ResizeProfile(bool dueToChangedSize) + { + _canProfileWidthChange = false; + + if (dueToChangedSize) + { + ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > + GlobalStaticConfiguration.FloatPointFix; + } + else + { + if (ExpandProfileView) + { + ProfileWidth = + Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < + GlobalStaticConfiguration.FloatPointFix + ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) + : new GridLength(_tempProfileWidth); + } + else + { + _tempProfileWidth = ProfileWidth.Value; + ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + } + } + + _canProfileWidthChange = true; + } +} diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs index 5a03e184b8..81931a83df 100644 --- a/Source/NETworkManager.Settings/SettingsInfo.cs +++ b/Source/NETworkManager.Settings/SettingsInfo.cs @@ -178,6 +178,34 @@ public int General_HistoryListEntries } } = GlobalStaticConfiguration.General_HistoryListEntries; + // Profile (shared across all tools with a profile panel) + + public bool Profile_ExpandView + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } = GlobalStaticConfiguration.Profile_ExpandProfileView; + + public double Profile_Width + { + get; + set + { + if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) + return; + + field = value; + OnPropertyChanged(); + } + } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; + // Window public bool Window_ConfirmClose @@ -855,32 +883,6 @@ public string NetworkInterface_InterfaceId } } - public bool NetworkInterface_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double NetworkInterface_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string NetworkInterface_ExportFilePath { get; @@ -1230,32 +1232,6 @@ public int IPScanner_MaxPortThreads } } = GlobalStaticConfiguration.IPScanner_MaxPortThreads; - public bool IPScanner_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double IPScanner_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string IPScanner_ExportFilePath { get; @@ -1390,32 +1366,6 @@ public int PortScanner_MaxPortThreads } } = GlobalStaticConfiguration.PortScanner_MaxPortThreads; - public bool PortScanner_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double PortScanner_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string PortScanner_ExportFilePath { get; @@ -1641,32 +1591,6 @@ public ExportFileType PingMonitor_ExportFileType } } = GlobalStaticConfiguration.PingMonitor_ExportFileType; - public bool PingMonitor_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double PingMonitor_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - #endregion #region Traceroute @@ -1788,32 +1712,6 @@ public double Traceroute_MapHeight } } = GlobalStaticConfiguration.Traceroute_MapHeight; - public bool Traceroute_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double Traceroute_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string Traceroute_ExportFilePath { get; @@ -2028,32 +1926,6 @@ public int DNSLookup_Timeout } } = GlobalStaticConfiguration.DNSLookup_Timeout; - public bool DNSLookup_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double DNSLookup_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string DNSLookup_ExportFilePath { get; @@ -2565,32 +2437,6 @@ public bool RemoteDesktop_VisualStyles } } - public bool RemoteDesktop_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double RemoteDesktop_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - #endregion #region PowerShell @@ -2660,32 +2506,6 @@ public ExecutionPolicy PowerShell_ExecutionPolicy } } = GlobalStaticConfiguration.PowerShell_ExecutionPolicy; - public bool PowerShell_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double PowerShell_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - #endregion #region PuTTY @@ -2898,32 +2718,6 @@ public ObservableCollection PuTTY_ProfileHistory } } = []; - public bool PuTTY_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double PuTTY_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string PuTTY_ApplicationFilePath { get; @@ -3046,32 +2840,6 @@ public ObservableCollection TigerVNC_PortHistory } } = []; - public bool TigerVNC_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double TigerVNC_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string TigerVNC_ApplicationFilePath { get; @@ -3115,32 +2883,6 @@ public ObservableCollection WebConsole_UrlHistory } } = []; - public bool WebConsole_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double WebConsole_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public bool WebConsole_ShowAddressBar { get; @@ -3327,32 +3069,6 @@ public SNMPV3PrivacyProvider SNMP_PrivacyProvider } } = GlobalStaticConfiguration.SNMP_PrivacyProvider; - public bool SNMP_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double SNMP_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string SNMP_ExportFilePath { @@ -3633,32 +3349,6 @@ public ObservableCollection WakeOnLan_BroadcastHistory } } = []; - public bool WakeOnLAN_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double WakeOnLAN_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - #endregion #region Whois @@ -3676,32 +3366,6 @@ public ObservableCollection Whois_DomainHistory } } = []; - public bool Whois_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double Whois_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string Whois_ExportFilePath { get; @@ -3745,32 +3409,6 @@ public ObservableCollection IPGeolocation_HostHistory } } = []; - public bool IPGeolocation_ExpandProfileView - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_ExpandProfileView; - - public double IPGeolocation_ProfileWidth - { - get; - set - { - if (Math.Abs(value - field) < GlobalStaticConfiguration.FloatPointFix) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_DefaultWidthExpanded; - public string IPGeolocation_ExportFilePath { get; diff --git a/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs index 6c3e8441f1..14e9865f44 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs @@ -1,33 +1,24 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Profiles; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; /// /// View model for the DNS lookup host view. /// -public class DNSLookupHostViewModel : ViewModelBase, IProfileManager +public class DNSLookupHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - /// /// Gets the client for inter-tab operations. /// @@ -54,9 +45,6 @@ public string InterTabPartition /// public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - /// /// Gets or sets the index of the selected tab. /// @@ -73,220 +61,15 @@ public int SelectedTabIndex } } - #region Profiles - - /// - /// Gets the collection view of profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the selected profile. - /// - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - /// - /// Gets or sets the search text. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search is in progress. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the collection view for profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; } - - /// - /// Gets the collection of profile filter tags. - /// - private ObservableCollection ProfileFilterTags { get; } = []; - - /// - /// Gets or sets a value indicating whether any tag match is sufficient for filtering. - /// - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - /// - /// Gets or sets a value indicating whether all tags must match for filtering. - /// - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a profile filter is set. - /// - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - /// - /// Gets or sets a value indicating whether the profile view is expanded. - /// - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.DNSLookup_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the width of the profile view. - /// - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.DNSLookup_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - #endregion - #endregion - - #region Constructor, load settings + #region Constructor /// /// Initializes a new instance of the class. /// public DNSLookupHostViewModel() { - _isLoading = true; - - InterTabClient = new DragablzInterTabClient(ApplicationName.DNSLookup); InterTabPartition = nameof(ApplicationName.DNSLookup); @@ -297,38 +80,18 @@ public DNSLookupHostViewModel() new DragablzTabItem(Strings.NewTab, new DNSLookupView(tabId), tabId) ]; - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); + InitializeProfileHost(); + } - SetProfilesView(new ProfileFilterInfo()); + #endregion - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + #region Profile host - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + protected override ApplicationName ApplicationName => ApplicationName.DNSLookup; - LoadSettings(); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.DNSLookup_Enabled; - _isLoading = false; - } - - /// - /// Loads the settings. - /// - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.DNSLookup_ExpandProfileView; - - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.DNSLookup_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - - _tempProfileWidth = SettingsManager.Current.DNSLookup_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.DNSLookup_Host; #endregion @@ -368,159 +131,6 @@ private void LookupProfileAction() AddTab(SelectedProfile.DNSLookup_Host); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - /// - /// Action to add a new profile. - /// - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.DNSLookup); - } - - /// - /// Checks if the profile modification commands can be executed. - /// - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to edit the selected profile. - /// - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile as a new profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to copy the selected profile as a new profile. - /// - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to delete the selected profile. - /// - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a profile group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - /// - /// Action to edit a profile group. - /// - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - /// - /// Action to open the profile filter. - /// - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - /// - /// Action to apply the profile filter. - /// - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - /// - /// Action to clear the profile filter. - /// - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - /// - /// Action to expand all profile groups. - /// - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - /// - /// Action to collapse all profile groups. - /// - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - /// /// Gets the callback for closing a tab item. /// @@ -538,49 +148,6 @@ private static void CloseItemAction(ItemActionCallbackArgs args) #region Methods - /// - /// Sets the IsExpanded property for all profile groups. - /// - /// The value to set. - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - /// - /// Resizes the profile view. - /// - /// Indicates if the resize is due to a changed size. - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - /// /// Adds a new tab for the specified host. /// @@ -595,129 +162,5 @@ public void AddTab(string host = null) SelectedTabIndex = TabItems.Count - 1; } - /// - /// Called when the view becomes visible. - /// - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - /// - /// Called when the view is hidden. - /// - public void OnViewHide() - { - _isViewActive = false; - } - - /// - /// Creates the profile filter tags. - /// - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.DNSLookup_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - /// - /// Sets the profiles view with the specified filter. - /// - /// The profile filter. - /// The profile to select. - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.DNSLookup_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.DNSLookup_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - /// - /// Refreshes the profiles. - /// - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - /// - /// Handles the OnProfilesUpdated event of the ProfileManager. - /// - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - /// - /// Handles the Tick event of the search dispatcher timer. - /// - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs b/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs index 82765b874d..dc6cac0a84 100644 --- a/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs @@ -1,33 +1,23 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Profiles; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; /// /// View model for the IP geolocation host view. /// -public class IPGeolocationHostViewModel : ViewModelBase, IProfileManager +public class IPGeolocationHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - /// /// Gets the client for inter-tab operations. /// @@ -54,9 +44,6 @@ public string InterTabPartition /// public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - /// /// Gets or sets the index of the selected tab. /// @@ -73,208 +60,6 @@ public int SelectedTabIndex } } - #region Profiles - - /// - /// Gets the collection view of profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the selected profile. - /// - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - /// - /// Gets or sets the search text. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search is in progress. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the collection view for profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; } - - /// - /// Gets the collection of profile filter tags. - /// - private ObservableCollection ProfileFilterTags { get; } = []; - - /// - /// Gets or sets a value indicating whether any tag match is sufficient for filtering. - /// - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - /// - /// Gets or sets a value indicating whether all tags must match for filtering. - /// - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a profile filter is set. - /// - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - /// - /// Gets or sets a value indicating whether the profile view is expanded. - /// - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.IPGeolocation_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the width of the profile view. - /// - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.IPGeolocation_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion #region Constructor @@ -284,8 +69,6 @@ public GridLength ProfileWidth /// public IPGeolocationHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.IPGeolocation); InterTabPartition = nameof(ApplicationName.IPGeolocation); @@ -296,38 +79,18 @@ public IPGeolocationHostViewModel() new DragablzTabItem(Strings.NewTab, new IPGeolocationView(tabId), tabId) ]; - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); + } - LoadSettings(); + #endregion - _isLoading = false; - } + #region Profile host - /// - /// Loads the settings. - /// - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.IPGeolocation_ExpandProfileView; + protected override ApplicationName ApplicationName => ApplicationName.IPGeolocation; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.IPGeolocation_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.IPGeolocation_Enabled; - _tempProfileWidth = SettingsManager.Current.IPGeolocation_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.IPGeolocation_Host; #endregion @@ -367,159 +130,6 @@ private void QueryProfileAction() AddTab(SelectedProfile.IPGeolocation_Host); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - /// - /// Action to add a new profile. - /// - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.IPGeolocation); - } - - /// - /// Checks if the profile modification commands can be executed. - /// - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to edit the selected profile. - /// - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile as a new profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to copy the selected profile as a new profile. - /// - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to delete the selected profile. - /// - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a profile group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - /// - /// Action to edit a profile group. - /// - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - /// - /// Action to open the profile filter. - /// - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - /// - /// Action to apply the profile filter. - /// - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - /// - /// Action to clear the profile filter. - /// - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - /// - /// Action to expand all profile groups. - /// - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - /// - /// Action to collapse all profile groups. - /// - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - /// /// Gets the callback for closing a tab item. /// @@ -537,49 +147,6 @@ private static void CloseItemAction(ItemActionCallbackArgs args) #region Methods - /// - /// Sets the IsExpanded property for all profile groups. - /// - /// The value to set. - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - /// - /// Resizes the profile view. - /// - /// Indicates whether the resize is due to a size change. - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - /// /// Adds a new tab for the specified domain. /// @@ -594,129 +161,5 @@ private void AddTab(string domain = null) SelectedTabIndex = TabItems.Count - 1; } - /// - /// Called when the view becomes visible. - /// - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - /// - /// Called when the view is hidden. - /// - public void OnViewHide() - { - _isViewActive = false; - } - - /// - /// Creates the profile filter tags. - /// - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.IPGeolocation_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - /// - /// Sets the profiles view with the specified filter. - /// - /// The profile filter. - /// The profile to select. - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.IPGeolocation_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.IPGeolocation_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - /// - /// Refreshes the profiles. - /// - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - /// - /// Handles the OnProfilesUpdated event of the ProfileManager. - /// - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - /// - /// Handles the Tick event of the search dispatcher timer. - /// - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs b/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs index 63924cec12..9ca9e59493 100644 --- a/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs @@ -1,33 +1,23 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Profiles; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; /// /// View model for the IP scanner host view. /// -public class IPScannerHostViewModel : ViewModelBase, IProfileManager +public class IPScannerHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - /// /// Gets the client for inter-tab operations. /// @@ -54,9 +44,6 @@ public string InterTabPartition /// public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - /// /// Gets or sets the index of the selected tab. /// @@ -73,219 +60,15 @@ public int SelectedTabIndex } } - #region Profiles - - /// - /// Gets the collection view of profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the selected profile. - /// - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - /// - /// Gets or sets the search text. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search is in progress. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the collection view for profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; } - - /// - /// Gets the collection of profile filter tags. - /// - private ObservableCollection ProfileFilterTags { get; } = []; - - /// - /// Gets or sets a value indicating whether any tag match is sufficient for filtering. - /// - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - /// - /// Gets or sets a value indicating whether all tags must match for filtering. - /// - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a profile filter is set. - /// - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - /// - /// Gets or sets a value indicating whether the profile view is expanded. - /// - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.IPScanner_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the width of the profile view. - /// - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.IPScanner_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion - #region Constructor, load settings + #region Constructor /// /// Initializes a new instance of the class. /// public IPScannerHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.IPScanner); InterTabPartition = nameof(ApplicationName.IPScanner); @@ -296,38 +79,18 @@ public IPScannerHostViewModel() new DragablzTabItem(Strings.NewTab, new IPScannerView(tabId), tabId) ]; - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); + } - LoadSettings(); + #endregion - _isLoading = false; - } + #region Profile host - /// - /// Loads the settings. - /// - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.IPScanner_ExpandProfileView; + protected override ApplicationName ApplicationName => ApplicationName.IPScanner; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.IPScanner_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.IPScanner_Enabled; - _tempProfileWidth = SettingsManager.Current.IPScanner_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.IPScanner_HostOrIPRange; #endregion @@ -367,159 +130,6 @@ private void ScanProfileAction() AddTab(SelectedProfile); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - /// - /// Action to add a new profile. - /// - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.IPScanner); - } - - /// - /// Checks if the profile modification commands can be executed. - /// - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to edit the selected profile. - /// - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile as a new profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to copy the selected profile as a new profile. - /// - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - /// - /// Action to delete the selected profile. - /// - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a profile group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - /// - /// Action to edit a profile group. - /// - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - /// - /// Action to open the profile filter. - /// - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - /// - /// Action to apply the profile filter. - /// - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - /// - /// Action to clear the profile filter. - /// - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - /// - /// Action to expand all profile groups. - /// - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - /// - /// Action to collapse all profile groups. - /// - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - /// /// Gets the callback for closing a tab item. /// @@ -537,49 +147,6 @@ private static void CloseItemAction(ItemActionCallbackArgs args) #region Methods - /// - /// Sets the IsExpanded property for all profile groups. - /// - /// The value to set. - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - /// - /// Resizes the profile view. - /// - /// Indicates whether the resize is due to a size change. - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - /// /// Adds a new tab for the specified host or IP range. /// @@ -603,129 +170,5 @@ private void AddTab(ProfileInfo profile) AddTab(profile.IPScanner_HostOrIPRange); } - /// - /// Called when the view becomes visible. - /// - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - /// - /// Called when the view is hidden. - /// - public void OnViewHide() - { - _isViewActive = false; - } - - /// - /// Creates the profile filter tags. - /// - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.IPScanner_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - /// - /// Sets the profiles view with the specified filter. - /// - /// The profile filter. - /// The profile to select. - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.IPScanner_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.IPScanner_HostOrIPRange.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - /// - /// Refreshes the profiles. - /// - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - /// - /// Handles the OnProfilesUpdated event of the ProfileManager. - /// - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - /// - /// Handles the Tick event of the search dispatcher timer. - /// - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/NetworkInterfaceViewModel.cs b/Source/NETworkManager/ViewModels/NetworkInterfaceViewModel.cs index 8126e4992c..c3e1f1532c 100644 --- a/Source/NETworkManager/ViewModels/NetworkInterfaceViewModel.cs +++ b/Source/NETworkManager/ViewModels/NetworkInterfaceViewModel.cs @@ -1,4 +1,4 @@ -using LiveChartsCore; +using LiveChartsCore; using LiveChartsCore.Drawing; using LiveChartsCore.Kernel; using LiveChartsCore.SkiaSharpView; @@ -7,7 +7,6 @@ using log4net; using MahApps.Metro.Controls; using MahApps.Metro.SimpleChildWindow; -using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Models.EventSystem; @@ -27,9 +26,7 @@ using System.Net.Sockets; using System.Threading.Tasks; using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; using NetworkInterface = NETworkManager.Models.Network.NetworkInterface; namespace NETworkManager.ViewModels; @@ -37,18 +34,15 @@ namespace NETworkManager.ViewModels; /// /// ViewModel for the Network Interface feature, allowing management of network adapters. /// -public class NetworkInterfaceViewModel : ViewModelBase, IProfileManager +public class NetworkInterfaceViewModel : ProfileHostViewModelBase { #region Variables private static readonly ILog Log = LogManager.GetLogger(typeof(NetworkInterfaceViewModel)); - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; private BandwidthMeter _bandwidthMeter; private readonly bool _isLoading; - private bool _isViewActive = true; /// /// Gets or sets a value indicating whether network interfaces are currently loading. @@ -467,33 +461,15 @@ public string ConfigSecondaryDNSServer #endregion - #region Profiles - - /// - /// Gets the collection of profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - /// - /// Gets or sets the selected profile. + /// Gets or sets the selected profile. Pre-fills the Config* fields from the profile. /// - public ProfileInfo SelectedProfile + public override ProfileInfo SelectedProfile { - get; + get => base.SelectedProfile; set { - if (value == field) + if (value == base.SelectedProfile) return; if (value != null) @@ -509,184 +485,12 @@ public ProfileInfo SelectedProfile ConfigSecondaryDNSServer = value.NetworkInterface_SecondaryDNSServer; } - field = value; - OnPropertyChanged(); - } - } = new(); - - /// - /// Gets or sets the search text. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search is in progress. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the view for profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; set; } - - /// - /// Gets or sets the collection of profile filter tags. - /// - public ObservableCollection ProfileFilterTags { get; set; } = []; - - /// - /// Gets or sets a value indicating whether to match any profile filter tag. - /// - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - /// - /// Gets or sets a value indicating whether to match all profile filter tags. - /// - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a profile filter is set. - /// - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the store for group expander states. - /// - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - /// - /// Gets or sets a value indicating whether to expand the profile view. - /// - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.NetworkInterface_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the width of the profile view. - /// - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.NetworkInterface_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); + base.SelectedProfile = value; } } #endregion - #endregion - #region Constructor, LoadSettings, OnShutdown /// @@ -703,19 +507,7 @@ public NetworkInterfaceViewModel() _ = LoadNetworkInterfaces(); - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); // Detect if network address or status changed... NetworkChange.NetworkAvailabilityChanged += (_, _) => ReloadNetworkInterfaces(); @@ -724,8 +516,6 @@ public NetworkInterfaceViewModel() // React to settings changes (e.g. the configurable bandwidth chart time window) SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; - LoadSettings(); - _isLoading = false; } @@ -931,19 +721,16 @@ private async Task LoadNetworkInterfaces() IsNetworkInterfaceLoading = false; } - /// - /// Loads the settings. - /// - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.NetworkInterface_ExpandProfileView; + #endregion - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.NetworkInterface_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + #region Profile host - _tempProfileWidth = SettingsManager.Current.NetworkInterface_ProfileWidth; - } + protected override ApplicationName ApplicationName => ApplicationName.NetworkInterface; + + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.NetworkInterface_Enabled; + + // Unlike other tools, profile search here only matches the profile name (no secondary field). + protected override string GetSearchableField(ProfileInfo profile) => string.Empty; #endregion @@ -1050,126 +837,6 @@ private void ApplyProfileAction() _ = ApplyConfigurationFromProfile(); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.NetworkInterface); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile as a new profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a profile group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - #region Additional commands private bool AdditionalCommands_CanExecute(object parameter) @@ -1178,7 +845,7 @@ private bool AdditionalCommands_CanExecute(object parameter) !((MetroWindow)Application.Current.MainWindow).IsAnyDialogOpen && !ConfigurationManager.Current.IsChildWindowOpen; } - + private bool ConfigureCommands_CanExecute(object parameter) => ConfigurationManager.Current.IsAdmin && Application.Current.MainWindow != null && !((MetroWindow)Application.Current.MainWindow).IsAnyDialogOpen && @@ -1391,7 +1058,7 @@ private async void ReloadNetworkInterfaces() // Invoke on UI thread synchronously Application.Current.Dispatcher.Invoke(() => { - // Clear the list + // Clear the list NetworkInterfaces.Clear(); // Add all network interfaces to the list @@ -1693,41 +1360,6 @@ private async Task RemoveIPv4Address(string ipAddress) } } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - private void ResetBandwidthChart() { if (_bandwidthReceivedValues == null) @@ -1849,11 +1481,9 @@ private void StopBandwidthMeter() /// /// Called when the view becomes visible. /// - public void OnViewVisible() + public override void OnViewVisible() { - _isViewActive = true; - - RefreshProfiles(); + base.OnViewVisible(); ResumeBandwidthMeter(); } @@ -1861,101 +1491,17 @@ public void OnViewVisible() /// /// Called when the view is hidden. /// - public void OnViewHide() + public override void OnViewHide() { StopBandwidthMeter(); - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.NetworkInterface_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.NetworkInterface_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); + base.OnViewHide(); } #endregion #region Events - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - private void SettingsManager_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -2034,4 +1580,4 @@ private void BandwidthMeter_UpdateSpeed(object sender, BandwidthMeterSpeedArgs e } #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs index 11d3d3a83d..5244f1e1b3 100644 --- a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs @@ -1,5 +1,4 @@ -using MahApps.Metro.Controls; -using NETworkManager.Controls; +using MahApps.Metro.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Models.Network; @@ -18,25 +17,18 @@ using System.Windows; using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; /// /// ViewModel for the Ping Monitor Host view. /// -public class PingMonitorHostViewModel : ViewModelBase, IProfileManager +public class PingMonitorHostViewModel : ProfileHostViewModelBase { #region Variables private CancellationTokenSource _cancellationTokenSource; - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - - private readonly bool _isLoading; - private bool _isViewActive = true; - private string _group = Strings.Hosts; // Default group name /// @@ -144,208 +136,6 @@ public ObservableCollection Hosts /// public ICollectionView HostsView { get; } - #region Profiles - - /// - /// Gets the view for the profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the selected profile. - /// - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the search text. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search is in progress. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the view for the profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - /// - /// Gets or sets a value indicating whether to match any profile filter tag. - /// - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - /// - /// Gets or sets a value indicating whether to match all profile filter tags. - /// - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter is set. - /// - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the group expander state store. - /// - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - /// - /// Gets or sets a value indicating whether the profile view is expanded. - /// - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.PingMonitor_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the width of the profile view. - /// - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.PingMonitor_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion #region Constructor, load settings @@ -355,8 +145,6 @@ public GridLength ProfileWidth /// public PingMonitorHostViewModel() { - _isLoading = true; - // Host history HostHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.PingMonitor_HostHistory); @@ -365,35 +153,18 @@ public PingMonitorHostViewModel() HostsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(PingMonitorView.Group))); HostsView.SortDescriptions.Add(new SortDescription(nameof(PingMonitorView.Group), ListSortDirection.Ascending)); - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); + } - LoadSettings(); + #endregion - _isLoading = false; - } + #region Profile host - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.PingMonitor_ExpandProfileView; + protected override ApplicationName ApplicationName => ApplicationName.PingMonitor; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.PingMonitor_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.PingMonitor_Enabled; - _tempProfileWidth = SettingsManager.Current.PingMonitor_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.PingMonitor_Host; #endregion @@ -445,125 +216,6 @@ private void CloseGroupAction(object group) RemoveGroup(group.ToString()); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.PingMonitor); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile as a new profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } #endregion #region Methods @@ -707,148 +359,10 @@ private void AddHostToHistory(string host) list.ForEach(SettingsManager.Current.PingMonitor_HostHistory.Add); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - - /// - /// Called when the view becomes visible. - /// - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - /// - /// Called when the view is hidden. - /// - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PingMonitor_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PingMonitor_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.PingMonitor_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - #endregion #region Event - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - private void UserHasCanceled() { StatusMessage = Strings.CanceledByUserMessage; @@ -859,4 +373,4 @@ private void UserHasCanceled() } #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs b/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs index 60414aae6a..d2b982af47 100644 --- a/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs @@ -1,33 +1,23 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Profiles; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; /// /// The view model for the Port Scanner host, managing tabs and profiles. /// -public class PortScannerHostViewModel : ViewModelBase, IProfileManager +public class PortScannerHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - /// /// Gets the InterTabClient for Dragablz. /// @@ -54,9 +44,6 @@ public string InterTabPartition /// public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - /// /// Gets or sets the index of the selected tab. /// @@ -73,219 +60,15 @@ public int SelectedTabIndex } } - #region Profiles - - /// - /// Gets the collection of filtered profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the selected profile. - /// - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - /// - /// Gets or sets the search query for filtering profiles. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search is currently active. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter flyout is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the collection view for profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - /// - /// Gets or sets a value indicating whether to match any selected tag in the filter. - /// - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - /// - /// Gets or sets a value indicating whether to match all selected tags in the filter. - /// - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a profile filter is currently applied. - /// - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the store for group expander states. - /// - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - /// - /// Gets or sets a value indicating whether the profile view is expanded. - /// - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.PortScanner_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the width of the profile view. - /// - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.PortScanner_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - #endregion - #endregion - - #region Constructor, load settings + #region Constructor /// /// Initializes a new instance of the class. /// public PortScannerHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.PortScanner); InterTabPartition = nameof(ApplicationName.PortScanner); @@ -296,35 +79,18 @@ public PortScannerHostViewModel() new DragablzTabItem(Strings.NewTab, new PortScannerView(tabId), tabId) ]; - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + InitializeProfileHost(); + } - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + #endregion - LoadSettings(); + #region Profile host - _isLoading = false; - } + protected override ApplicationName ApplicationName => ApplicationName.PortScanner; - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.PortScanner_ExpandProfileView; + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.PortScanner_Enabled; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.PortScanner_ProfileWidth) - : new GridLength(40); - - _tempProfileWidth = SettingsManager.Current.PortScanner_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.PortScanner_Host; #endregion @@ -355,126 +121,6 @@ private void ScanProfileAction() AddTab(SelectedProfile); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.PortScanner); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a profile group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - /// /// Gets the callback for closing a tab item. /// @@ -489,41 +135,6 @@ private static void CloseItemAction(ItemActionCallbackArgs args) #region Methods - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - /// /// Adds a new tab with the specified host and ports. /// @@ -544,113 +155,5 @@ private void AddTab(ProfileInfo profile) AddTab(profile.PortScanner_Host, profile.PortScanner_Ports); } - /// - /// Called when the view becomes visible. - /// - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - /// - /// Called when the view is hidden. - /// - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PortScanner_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PortScanner_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.PortScanner_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs b/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs index d5bc743ac2..2f3607b342 100644 --- a/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs @@ -1,4 +1,4 @@ -using Dragablz; +using Dragablz; using log4net; using MahApps.Metro.SimpleChildWindow; using NETworkManager.Controls; @@ -11,7 +11,6 @@ using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; @@ -19,20 +18,16 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; using PowerShellProfile = NETworkManager.Profiles.Application.PowerShell; namespace NETworkManager.ViewModels; -public class PowerShellHostViewModel : ViewModelBase, IProfileManager +public class PowerShellHostViewModel : ProfileHostViewModelBase { #region Variables private static readonly ILog Log = LogManager.GetLogger(typeof(PowerShellHostViewModel)); - private readonly DispatcherTimer _searchDispatcherTimer = new(); - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -50,9 +45,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public bool IsExecutableConfigured { get; @@ -92,169 +84,13 @@ public bool HeaderContextMenuIsOpen } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - IsSearching = true; - _searchDispatcherTimer.Start(); - - OnPropertyChanged(); - } - } - private bool _textBoxSearchIsFocused; - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.PowerShell_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.PowerShell_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - + /// + /// Gets or sets a value indicating whether the profile context menu is open. Bound to + /// so + /// can avoid stealing focus while it is open. + /// public bool ProfileContextMenuIsOpen { get; @@ -270,14 +106,10 @@ public bool ProfileContextMenuIsOpen #endregion - #endregion - #region Constructor, load settings public PowerShellHostViewModel() { - _isLoading = true; - // Check if PowerShell executable is configured CheckExecutable(); @@ -293,35 +125,53 @@ public PowerShellHostViewModel() TabItems = []; - // Profiles - CreateTags(); + InitializeProfileHost(); - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), ListSortDirection.Ascending)); + SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; + } - SetProfilesView(new ProfileFilterInfo()); + #endregion - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + #region Profile host - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + protected override ApplicationName ApplicationName => ApplicationName.PowerShell; - LoadSettings(); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.PowerShell_Enabled; - SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; + protected override string GetSearchableField(ProfileInfo profile) => profile.PowerShell_Host; + + /// + /// Also mirrors the popup state into so the main window can suppress + /// global focus-stealing behavior (e.g. re-focusing the embedded terminal) while the filter popup is open. + /// + public new ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); + + private void OpenProfileFilterAction() + { + ConfigurationManager.Current.IsProfileFilterPopupOpen = true; - _isLoading = false; + ProfileFilterIsOpen = true; } - private void LoadSettings() + /// + /// Called when the tag-filter popup closes (including an implicit close, e.g. clicking away), to keep + /// in sync. + /// + public ICommand ProfileFilterPopupClosedCommand => new RelayCommand(_ => OnProfileFilterClosed()); + + public void OnProfileFilterClosed() { - ExpandProfileView = SettingsManager.Current.PowerShell_ExpandProfileView; + ConfigurationManager.Current.IsProfileFilterPopupOpen = false; + } - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.PowerShell_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + public override void OnProfileManagerDialogOpen() + { + ConfigurationManager.OnDialogOpen(); + } - _tempProfileWidth = SettingsManager.Current.PowerShell_ProfileWidth; + public override void OnProfileManagerDialogClose() + { + ConfigurationManager.OnDialogClose(); } #endregion @@ -393,48 +243,6 @@ private void ConnectProfileExternalAction() ConnectProfileExternal(); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.PowerShell); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager.ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - public ICommand TextBoxSearchGotFocusCommand { get { return new RelayCommand(_ => _textBoxSearchIsFocused = true); } @@ -452,52 +260,6 @@ private void ClearSearchAction() Search = string.Empty; } - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ConfigurationManager.Current.IsProfileFilterPopupOpen = true; - - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - IsProfileFilterSet = true; - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ICommand OpenSettingsCommand => new RelayCommand(_ => OpenSettingsAction()); private static void OpenSettingsAction() @@ -562,14 +324,14 @@ private void TryFindExecutable() /// /// Resolves the actual installation path of a PowerShell executable that was installed via the /// Microsoft Store / WindowsApps and therefore appears as a proxy stub in the user's AppData. - /// + /// /// Typical input is a path like: /// C:\Users\{USERNAME}\AppData\Local\Microsoft\WindowsApps\pwsh.exe - /// + /// /// This helper attempts to locate the corresponding real executable under the Program Files /// WindowsApps package layout, e.g.: /// C:\Program Files\WindowsApps\Microsoft.PowerShell_7.*_8wekyb3d8bbwe\pwsh.exe. - /// + /// /// Workaround for: https://github.com/BornToBeRoot/NETworkManager/issues/3223 /// /// Path to the pwsh proxy stub, typically located under the current user's %LocalAppData%\Microsoft\WindowsApps\pwsh.exe. @@ -716,41 +478,6 @@ private static void AddHostToHistory(string host) SettingsManager.Current.General_HistoryListEntries)); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - public void FocusEmbeddedWindow() { /* Don't continue if @@ -780,97 +507,13 @@ public void FocusEmbeddedWindow() } } - public void OnViewVisible() + public override void OnViewVisible() { - _isViewActive = true; - - RefreshProfiles(); + base.OnViewVisible(); FocusEmbeddedWindow(); } - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PowerShell_Enabled).SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PowerShell_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.PowerShell_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - SetProfilesView(new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }, SelectedProfile); - } - - public void OnProfileFilterClosed() - { - ConfigurationManager.Current.IsProfileFilterPopupOpen = false; - } - - public void OnProfileManagerDialogOpen() - { - ConfigurationManager.OnDialogOpen(); - } - - public void OnProfileManagerDialogClose() - { - ConfigurationManager.OnDialogClose(); - } - private void WriteDefaultProfileToRegistry() { if (!SettingsManager.Current.Appearance_PowerShellModifyGlobalProfile) @@ -890,22 +533,6 @@ private void WriteDefaultProfileToRegistry() #region Event - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - private void SettingsManager_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -922,4 +549,4 @@ private void SettingsManager_PropertyChanged(object sender, PropertyChangedEvent } #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/ProfileHostViewModelBase.cs b/Source/NETworkManager/ViewModels/ProfileHostViewModelBase.cs new file mode 100644 index 0000000000..1a55ab14df --- /dev/null +++ b/Source/NETworkManager/ViewModels/ProfileHostViewModelBase.cs @@ -0,0 +1,479 @@ +using NETworkManager.Controls; +using NETworkManager.Models; +using NETworkManager.Profiles; +using NETworkManager.Settings; +using NETworkManager.Utilities; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Windows; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Threading; + +namespace NETworkManager.ViewModels; + +/// +/// Base class for view models that host the shared profile panel (search, tag filter, group list) next to a +/// tool-specific view. Holds all profile-panel state/commands that are identical across tools; derived +/// classes only need to describe how their tool's profiles are identified and searched. +/// +public abstract class ProfileHostViewModelBase : ViewModelBase, IProfileHostViewModel +{ + #region Variables + + private readonly DispatcherTimer _searchDispatcherTimer = new(); + private bool _searchDisabled; + private bool _isViewActive = true; + + /// + /// Gets the application name used for profile dialogs (add/edit/copy-as/delete) of this tool. + /// + protected abstract ApplicationName ApplicationName { get; } + + /// + /// Determines whether the given profile is enabled for this tool. + /// + protected abstract bool IsProfileEnabled(ProfileInfo profile); + + /// + /// Gets the tool-specific field of the given profile that should also be matched against the search text + /// (in addition to the profile name). + /// + protected abstract string GetSearchableField(ProfileInfo profile); + + /// + /// Gets the view for the profiles. + /// + public ICollectionView Profiles + { + get; + private set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } + + /// + /// Gets or sets the selected profile. Virtual so tools that need to react to a profile selection (e.g. + /// pre-filling other fields from the profile) can override it and still participate in the base class's + /// own internal re-selection logic (e.g. in ). + /// + public virtual ProfileInfo SelectedProfile + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } + + /// + /// Gets or sets the search text. + /// + public string Search + { + get; + set + { + if (value == field) + return; + + field = value; + + // Start searching... + if (!_searchDisabled) + { + IsSearching = true; + _searchDispatcherTimer.Start(); + } + + OnPropertyChanged(); + } + } + + /// + /// Gets or sets a value indicating whether a search is in progress. + /// + public bool IsSearching + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } + + /// + /// Gets or sets a value indicating whether the profile filter popup is open. + /// + public bool ProfileFilterIsOpen + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } + + /// + /// Gets the view for the profile filter tags. + /// + public ICollectionView ProfileFilterTagsView { get; private set; } + + private ObservableCollection ProfileFilterTags { get; } = []; + + /// + /// Gets or sets a value indicating whether to match any profile filter tag. + /// + public bool ProfileFilterTagsMatchAny + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } = GlobalStaticConfiguration.Profile_TagsMatchAny; + + /// + /// Gets or sets a value indicating whether to match all profile filter tags. + /// + public bool ProfileFilterTagsMatchAll + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } + + /// + /// Gets or sets a value indicating whether a profile filter is currently applied. + /// + public bool IsProfileFilterSet + { + get; + set + { + if (value == field) + return; + + field = value; + OnPropertyChanged(); + } + } + + /// + /// Gets the group expander state store for the profile list. + /// + public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); + + #endregion + + #region Constructor + + /// + /// Initializes the shared profile-panel state. Must be called by derived constructors after their own + /// tool-specific initialization (and while _isLoading-equivalent guards, if any, are still active). + /// + protected void InitializeProfileHost() + { + CreateTags(); + + ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); + ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), + ListSortDirection.Ascending)); + + SetProfilesView(new ProfileFilterInfo()); + + ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + + _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; + _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + } + + #endregion + + #region ICommands & Actions + + /// + /// Gets the command to add a new profile. + /// + public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); + + private void AddProfileAction() + { + _ = ProfileDialogManager + .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName); + } + + private bool ModifyProfile_CanExecute(object obj) + { + return SelectedProfile is { IsDynamic: false }; + } + + /// + /// Gets the command to edit the selected profile. + /// + public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); + + private void EditProfileAction() + { + _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); + } + + /// + /// Gets the command to copy the selected profile as a new profile. + /// + public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); + + private void CopyAsProfileAction() + { + _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); + } + + /// + /// Gets the command to delete the selected profile. + /// + public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); + + private void DeleteProfileAction() + { + _ = ProfileDialogManager + .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); + } + + /// + /// Gets the command to edit a group. + /// + public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); + + private void EditGroupAction(object group) + { + _ = ProfileDialogManager + .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); + } + + /// + /// Gets the command to open the profile filter. + /// + public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); + + private void OpenProfileFilterAction() + { + ProfileFilterIsOpen = true; + } + + /// + /// Gets the command to apply the profile filter. + /// + public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); + + private void ApplyProfileFilterAction() + { + RefreshProfiles(); + + ProfileFilterIsOpen = false; + } + + /// + /// Gets the command to clear the profile filter. + /// + public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); + + private void ClearProfileFilterAction() + { + _searchDisabled = true; + Search = string.Empty; + _searchDisabled = false; + + foreach (var tag in ProfileFilterTags) + tag.IsSelected = false; + + RefreshProfiles(); + + IsProfileFilterSet = false; + ProfileFilterIsOpen = false; + } + + /// + /// Gets the command to expand all profile groups. + /// + public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); + + private void ExpandAllProfileGroupsAction() + { + SetIsExpandedForAllProfileGroups(true); + } + + /// + /// Gets the command to collapse all profile groups. + /// + public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); + + private void CollapseAllProfileGroupsAction() + { + SetIsExpandedForAllProfileGroups(false); + } + + #endregion + + #region Methods + + private void SetIsExpandedForAllProfileGroups(bool isExpanded) + { + foreach (var group in Profiles.Groups.Cast()) + GroupExpanderStateStore[group.Name.ToString()] = isExpanded; + } + + /// + /// Called when the view becomes visible. + /// + public virtual void OnViewVisible() + { + _isViewActive = true; + + RefreshProfiles(); + } + + /// + /// Called when the view is hidden. + /// + public virtual void OnViewHide() + { + _isViewActive = false; + } + + /// + /// Called when a dialog in the is opened. Virtual so tools with an + /// embedded native window (e.g. PowerShell, PuTTY, RemoteDesktop, TigerVNC, WebConsole) can override it to + /// enable while the dialog is shown. + /// + public virtual void OnProfileManagerDialogOpen() + { + } + + /// + /// Called when a dialog in the is closed. + /// + public virtual void OnProfileManagerDialogClose() + { + } + + private void CreateTags() + { + var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(IsProfileEnabled) + .SelectMany(x => x.TagsCollection).Distinct().ToList(); + + var tagSet = new HashSet(tags); + + for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) + { + if (!tagSet.Contains(ProfileFilterTags[i].Name)) + ProfileFilterTags.RemoveAt(i); + } + + var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); + + foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) + { + ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); + } + } + + private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) + { + Profiles = new CollectionViewSource + { + Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => IsProfileEnabled(x) && ( + string.IsNullOrEmpty(filter.Search) || + x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || + GetSearchableField(x).IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( + // If no tags are selected, show all profiles + (!filter.Tags.Any()) || + // Any tag can match + (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && + filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || + // All tags must match + (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && + filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) + ).OrderBy(x => x.Group).ThenBy(x => x.Name) + }.View; + + Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); + + // Set specific profile or first if null + SelectedProfile = null; + + if (profile != null) + SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? + Profiles.Cast().FirstOrDefault(); + else + SelectedProfile = Profiles.Cast().FirstOrDefault(); + } + + private void RefreshProfiles() + { + if (!_isViewActive) + return; + + var filter = new ProfileFilterInfo + { + Search = Search, + Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], + TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All + }; + + SetProfilesView(filter, SelectedProfile); + + IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); + } + + #endregion + + #region Event + + private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) + { + CreateTags(); + + RefreshProfiles(); + } + + private void SearchDispatcherTimer_Tick(object sender, EventArgs e) + { + _searchDispatcherTimer.Stop(); + + RefreshProfiles(); + + IsSearching = false; + } + + #endregion +} diff --git a/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs b/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs index cd18b020a6..9ffc59c06d 100644 --- a/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PuTTYHostViewModel.cs @@ -1,4 +1,4 @@ -using Dragablz; +using Dragablz; using log4net; using MahApps.Metro.SimpleChildWindow; using NETworkManager.Controls; @@ -11,7 +11,6 @@ using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; @@ -19,22 +18,17 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; using PuTTYProfile = NETworkManager.Settings.Application.PuTTY; namespace NETworkManager.ViewModels; -public class PuTTYHostViewModel : ViewModelBase, IProfileManager +public class PuTTYHostViewModel : ProfileHostViewModelBase { #region Variables private static readonly ILog Log = LogManager.GetLogger(typeof(PuTTYHostViewModel)); - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -52,9 +46,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public bool IsExecutableConfigured { get; @@ -94,172 +85,13 @@ public bool HeaderContextMenuIsOpen } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - private bool _textBoxSearchIsFocused; - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.PuTTY_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.PuTTY_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - + /// + /// Gets or sets a value indicating whether the profile context menu is open. Bound to + /// so + /// can avoid stealing focus while it is open. + /// public bool ProfileContextMenuIsOpen { get; @@ -275,14 +107,10 @@ public bool ProfileContextMenuIsOpen #endregion - #endregion - #region Constructor, load settings public PuTTYHostViewModel() { - _isLoading = true; - // Check if PuTTY executable is configured CheckExecutable(); @@ -297,36 +125,53 @@ public PuTTYHostViewModel() TabItems = []; - // Profiles - CreateTags(); + InitializeProfileHost(); + + SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; + } - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); + #endregion - SetProfilesView(new ProfileFilterInfo()); + #region Profile host - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + protected override ApplicationName ApplicationName => ApplicationName.PuTTY; - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.PuTTY_Enabled; - LoadSettings(); + protected override string GetSearchableField(ProfileInfo profile) => profile.PuTTY_HostOrSerialLine; - SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; + /// + /// Also mirrors the popup state into so the main window can suppress + /// global focus-stealing behavior (e.g. re-focusing the embedded terminal) while the filter popup is open. + /// + public new ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); + + private void OpenProfileFilterAction() + { + ConfigurationManager.Current.IsProfileFilterPopupOpen = true; - _isLoading = false; + ProfileFilterIsOpen = true; } - private void LoadSettings() + /// + /// Called when the tag-filter popup closes (including an implicit close, e.g. clicking away), to keep + /// in sync. + /// + public ICommand ProfileFilterPopupClosedCommand => new RelayCommand(_ => OnProfileFilterClosed()); + + public void OnProfileFilterClosed() { - ExpandProfileView = SettingsManager.Current.PuTTY_ExpandProfileView; + ConfigurationManager.Current.IsProfileFilterPopupOpen = false; + } - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.PuTTY_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + public override void OnProfileManagerDialogOpen() + { + ConfigurationManager.OnDialogOpen(); + } - _tempProfileWidth = SettingsManager.Current.PuTTY_ProfileWidth; + public override void OnProfileManagerDialogClose() + { + ConfigurationManager.OnDialogClose(); } #endregion @@ -406,49 +251,6 @@ private void ConnectProfileExternalAction() ConnectProfileExternal(); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.PuTTY); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - public ICommand TextBoxSearchGotFocusCommand { get { return new RelayCommand(_ => _textBoxSearchIsFocused = true); } @@ -459,55 +261,6 @@ public ICommand TextBoxSearchLostFocusCommand get { return new RelayCommand(_ => _textBoxSearchIsFocused = false); } } - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ConfigurationManager.Current.IsProfileFilterPopupOpen = true; - - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ICommand OpenSettingsCommand => new RelayCommand(_ => OpenSettingsAction()); private static void OpenSettingsAction() @@ -720,41 +473,6 @@ private static void AddProfileToHistory(string profile) SettingsManager.Current.General_HistoryListEntries)); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - public void FocusEmbeddedWindow() { /* Don't continue if @@ -784,104 +502,13 @@ public void FocusEmbeddedWindow() } } - public void OnViewVisible() + public override void OnViewVisible() { - _isViewActive = true; - - RefreshProfiles(); + base.OnViewVisible(); FocusEmbeddedWindow(); } - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PuTTY_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.PuTTY_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.PuTTY_HostOrSerialLine.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - public void OnProfileFilterClosed() - { - ConfigurationManager.Current.IsProfileFilterPopupOpen = false; - } - - public void OnProfileManagerDialogOpen() - { - ConfigurationManager.OnDialogOpen(); - } - - public void OnProfileManagerDialogClose() - { - ConfigurationManager.OnDialogClose(); - } - private void WriteDefaultProfileToRegistry() { if (!IsExecutableConfigured) @@ -909,21 +536,5 @@ private void SettingsManager_PropertyChanged(object sender, PropertyChangedEvent } } - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/RemoteDesktopHostViewModel.cs b/Source/NETworkManager/ViewModels/RemoteDesktopHostViewModel.cs index 22a6b4d159..1de5001b77 100644 --- a/Source/NETworkManager/ViewModels/RemoteDesktopHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/RemoteDesktopHostViewModel.cs @@ -1,4 +1,4 @@ -using Dragablz; +using Dragablz; using MahApps.Metro.SimpleChildWindow; using NETworkManager.Controls; using NETworkManager.Localization.Resources; @@ -11,25 +11,19 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; using RemoteDesktop = NETworkManager.Profiles.Application.RemoteDesktop; namespace NETworkManager.ViewModels; -public class RemoteDesktopHostViewModel : ViewModelBase, IProfileManager +public class RemoteDesktopHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -47,9 +41,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public int SelectedTabIndex { get; @@ -63,213 +54,38 @@ public int SelectedTabIndex } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.RemoteDesktop_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.RemoteDesktop_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - #endregion - #endregion - - #region Constructor, load settings + #region Constructor public RemoteDesktopHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.RemoteDesktop); InterTabPartition = nameof(ApplicationName.RemoteDesktop); TabItems = []; - // Profiles - CreateTags(); + InitializeProfileHost(); + } - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); + #endregion - SetProfilesView(new ProfileFilterInfo()); + #region Profile host - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + protected override ApplicationName ApplicationName => ApplicationName.RemoteDesktop; - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.RemoteDesktop_Enabled; - LoadSettings(); + protected override string GetSearchableField(ProfileInfo profile) => profile.RemoteDesktop_Host; - _isLoading = false; + public override void OnProfileManagerDialogOpen() + { + ConfigurationManager.OnDialogOpen(); } - private void LoadSettings() + public override void OnProfileManagerDialogClose() { - ExpandProfileView = SettingsManager.Current.RemoteDesktop_ExpandProfileView; - - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.RemoteDesktop_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - - _tempProfileWidth = SettingsManager.Current.RemoteDesktop_ProfileWidth; + ConfigurationManager.OnDialogClose(); } #endregion @@ -421,96 +237,6 @@ private void ConnectProfileExternalAction() Process.Start("mstsc.exe", args); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.RemoteDesktop); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ItemActionCallback CloseItemCommand => CloseItemAction; private static void CloseItemAction(ItemActionCallbackArgs args) @@ -678,151 +404,5 @@ private static void AddHostToHistory(string host) SettingsManager.Current.General_HistoryListEntries)); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.RemoteDesktop_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.RemoteDesktop_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.RemoteDesktop_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - public void OnProfileManagerDialogOpen() - { - ConfigurationManager.OnDialogOpen(); - } - - public void OnProfileManagerDialogClose() - { - ConfigurationManager.OnDialogClose(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/SNMPHostViewModel.cs b/Source/NETworkManager/ViewModels/SNMPHostViewModel.cs index d50abcb2d7..f73e4a0601 100644 --- a/Source/NETworkManager/ViewModels/SNMPHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/SNMPHostViewModel.cs @@ -1,32 +1,22 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Models.Network; using NETworkManager.Profiles; using NETworkManager.Profiles.Application; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; -public class SNMPHostViewModel : ViewModelBase, IProfileManager +public class SNMPHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -44,9 +34,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public int SelectedTabIndex { get; @@ -60,215 +47,30 @@ public int SelectedTabIndex } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.SNMP_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.SNMP_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion - #region Constructor, load settings + #region Constructor public SNMPHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.SNMP); InterTabPartition = nameof(ApplicationName.SNMP); TabItems = []; AddTab(); - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); + } - LoadSettings(); + #endregion - _isLoading = false; - } + #region Profile host - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.SNMP_ExpandProfileView; + protected override ApplicationName ApplicationName => ApplicationName.SNMP; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.SNMP_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.SNMP_Enabled; - _tempProfileWidth = SettingsManager.Current.SNMP_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.SNMP_Host; #endregion @@ -293,96 +95,6 @@ private void AddTabProfileAction() AddTab(SelectedProfile); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.SNMP); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ItemActionCallback CloseItemCommand => CloseItemAction; private static void CloseItemAction(ItemActionCallbackArgs args) @@ -393,40 +105,6 @@ private static void CloseItemAction(ItemActionCallbackArgs args) #endregion #region Methods - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } private void AddTab(SNMPSessionInfo sessionInfo, string header = null) { @@ -461,106 +139,5 @@ private void AddTab(ProfileInfo profile) AddTab(sessionInfo, profile.Name); } - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.SNMP_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.SNMP_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.SNMP_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/TigerVNCHostViewModel.cs b/Source/NETworkManager/ViewModels/TigerVNCHostViewModel.cs index f06888b4cf..e9f47e675d 100644 --- a/Source/NETworkManager/ViewModels/TigerVNCHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/TigerVNCHostViewModel.cs @@ -1,4 +1,4 @@ -using Dragablz; +using Dragablz; using MahApps.Metro.SimpleChildWindow; using NETworkManager.Controls; using NETworkManager.Localization.Resources; @@ -10,7 +10,6 @@ using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; @@ -18,18 +17,14 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; using TigerVNC = NETworkManager.Profiles.Application.TigerVNC; namespace NETworkManager.ViewModels; -public class TigerVNCHostViewModel : ViewModelBase, IProfileManager +public class TigerVNCHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; public IInterTabClient InterTabClient { get; } @@ -48,9 +43,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public bool IsConfigured { get; @@ -77,180 +69,12 @@ public int SelectedTabIndex } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.TigerVNC_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.TigerVNC_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion #region Constructor, load settings public TigerVNCHostViewModel() { - _isLoading = true; - CheckSettings(); InterTabClient = new DragablzInterTabClient(ApplicationName.TigerVNC); @@ -258,36 +82,29 @@ public TigerVNCHostViewModel() TabItems = []; - // Profiles - CreateTags(); + InitializeProfileHost(); - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); + SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; + } - SetProfilesView(new ProfileFilterInfo()); + #endregion - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + #region Profile host - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + protected override ApplicationName ApplicationName => ApplicationName.TigerVNC; - LoadSettings(); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.TigerVNC_Enabled; - SettingsManager.Current.PropertyChanged += SettingsManager_PropertyChanged; + protected override string GetSearchableField(ProfileInfo profile) => profile.TigerVNC_Host; - _isLoading = false; + public override void OnProfileManagerDialogOpen() + { + ConfigurationManager.OnDialogOpen(); } - private void LoadSettings() + public override void OnProfileManagerDialogClose() { - ExpandProfileView = SettingsManager.Current.TigerVNC_ExpandProfileView; - - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.TigerVNC_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - - _tempProfileWidth = SettingsManager.Current.TigerVNC_ProfileWidth; + ConfigurationManager.OnDialogClose(); } #endregion @@ -343,96 +160,6 @@ private void ConnectProfileExternalAction() ConnectProfileExternal(); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.TigerVNC); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ICommand OpenSettingsCommand => new RelayCommand(_ => OpenSettingsAction()); private static void OpenSettingsAction() @@ -549,152 +276,10 @@ private static void AddPortToHistory(int port) SettingsManager.Current.General_HistoryListEntries)); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.TigerVNC_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.TigerVNC_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.TigerVNC_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - public void OnProfileManagerDialogOpen() - { - ConfigurationManager.OnDialogOpen(); - } - - public void OnProfileManagerDialogClose() - { - ConfigurationManager.OnDialogClose(); - } - #endregion #region Event - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - private void SettingsManager_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(SettingsInfo.TigerVNC_ApplicationFilePath)) @@ -702,4 +287,4 @@ private void SettingsManager_PropertyChanged(object sender, PropertyChangedEvent } #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/TracerouteHostViewModel.cs b/Source/NETworkManager/ViewModels/TracerouteHostViewModel.cs index 4f404b8e2a..40407b6d5f 100644 --- a/Source/NETworkManager/ViewModels/TracerouteHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/TracerouteHostViewModel.cs @@ -1,30 +1,20 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Profiles; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; -public class TracerouteHostViewModel : ViewModelBase, IProfileManager +public class TracerouteHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -42,9 +32,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public int SelectedTabIndex { get; @@ -58,180 +45,12 @@ public int SelectedTabIndex } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.Traceroute_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.Traceroute_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion - #region Constructor, load settings + #region Constructor public TracerouteHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.Traceroute); InterTabPartition = nameof(ApplicationName.Traceroute); @@ -242,35 +61,18 @@ public TracerouteHostViewModel() new DragablzTabItem(Strings.NewTab, new TracerouteView(tabId), tabId) ]; - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); + } - LoadSettings(); + #endregion - _isLoading = false; - } + #region Profile host - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.Traceroute_ExpandProfileView; + protected override ApplicationName ApplicationName => ApplicationName.Traceroute; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.Traceroute_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.Traceroute_Enabled; - _tempProfileWidth = SettingsManager.Current.Traceroute_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.Traceroute_Host; #endregion @@ -295,96 +97,6 @@ private void TraceProfileAction() AddTab(SelectedProfile); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.Traceroute); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ItemActionCallback CloseItemCommand => CloseItemAction; private void CloseItemAction(ItemActionCallbackArgs args) @@ -396,41 +108,6 @@ private void CloseItemAction(ItemActionCallbackArgs args) #region Methods - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - public void AddTab(string host = null) { var tabId = Guid.NewGuid(); @@ -446,106 +123,5 @@ public void AddTab(ProfileInfo profile) AddTab(profile.Traceroute_Host); } - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.Traceroute_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.Traceroute_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.Traceroute_Host.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/WakeOnLANViewModel.cs b/Source/NETworkManager/ViewModels/WakeOnLANViewModel.cs index 7b122efc71..335d64d2f8 100644 --- a/Source/NETworkManager/ViewModels/WakeOnLANViewModel.cs +++ b/Source/NETworkManager/ViewModels/WakeOnLANViewModel.cs @@ -1,4 +1,4 @@ -using MahApps.Metro.Controls; +using MahApps.Metro.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Models.Network; @@ -6,7 +6,6 @@ using NETworkManager.Settings; using NETworkManager.Utilities; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; @@ -15,24 +14,16 @@ using System.Windows; using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; -using NETworkManager.Controls; namespace NETworkManager.ViewModels; /// /// ViewModel for the Wake on LAN feature. /// -public class WakeOnLANViewModel : ViewModelBase, IProfileManager +public class WakeOnLANViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - - private readonly bool _isLoading; - private bool _isViewActive = true; - /// /// Gets or sets a value indicating whether the Wake on LAN operation is running. /// @@ -123,33 +114,16 @@ private set } } - #region Profiles - - /// - /// Gets the collection view for the profiles. - /// - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - /// - /// Gets or sets the currently selected profile. + /// Gets or sets the currently selected profile. Pre-fills / + /// from the profile, unless a wake-up is currently running. /// - public ProfileInfo SelectedProfile + public override ProfileInfo SelectedProfile { - get; + get => base.SelectedProfile; set { - if (value == field) + if (value == base.SelectedProfile) return; if (value != null && !IsRunning) @@ -158,205 +132,35 @@ public ProfileInfo SelectedProfile Broadcast = value.WakeOnLAN_Broadcast; } - field = value; - OnPropertyChanged(); - } - } = new(); - - /// - /// Gets or sets the search text for filtering profiles. - /// - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether a search operation is in progress. - /// - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the profile filter flyout is open. - /// - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - /// - /// Gets the collection view for the profile filter tags. - /// - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.WakeOnLAN_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.WakeOnLAN_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); + base.SelectedProfile = value; } } #endregion - #endregion - - #region Constructor, load settings + #region Constructor /// /// Initializes a new instance of the class. /// public WakeOnLANViewModel() { - _isLoading = true; - MACAddressHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.WakeOnLan_MACAddressHistory); BroadcastHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.WakeOnLan_BroadcastHistory); - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); + InitializeProfileHost(); + } - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + #endregion - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + #region Profile host - LoadSettings(); + protected override ApplicationName ApplicationName => ApplicationName.WakeOnLAN; - _isLoading = false; - } + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.WakeOnLAN_Enabled; - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.WakeOnLAN_ExpandProfileView; - - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.WakeOnLAN_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - - _tempProfileWidth = SettingsManager.Current.WakeOnLAN_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.WakeOnLAN_MACAddress; #endregion @@ -399,125 +203,6 @@ private void WakeUpProfileAction() _ = WakeUp(NETworkManager.Profiles.Application.WakeOnLAN.CreateInfo(SelectedProfile)); } - /// - /// Gets the command to add a new profile. - /// - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.WakeOnLAN); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - /// - /// Gets the command to edit the selected profile. - /// - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to copy the selected profile as a new profile. - /// - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - /// - /// Gets the command to delete the selected profile. - /// - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - /// - /// Gets the command to edit a profile group. - /// - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - /// - /// Gets the command to open the profile filter flyout. - /// - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - /// - /// Gets the command to apply the profile filter. - /// - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to clear the profile filter. - /// - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - /// - /// Gets the command to expand all profile groups. - /// - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - /// - /// Gets the command to collapse all profile groups. - /// - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } #endregion #region Methods @@ -574,141 +259,5 @@ private void AddBroadcastToHistory(string broadcast) list.ForEach(x => SettingsManager.Current.WakeOnLan_BroadcastHistory.Add(x)); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.WakeOnLAN_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.WakeOnLAN_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.WakeOnLAN_MACAddress.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/WebConsoleHostViewModel.cs b/Source/NETworkManager/ViewModels/WebConsoleHostViewModel.cs index e2457f8543..951174d9eb 100644 --- a/Source/NETworkManager/ViewModels/WebConsoleHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/WebConsoleHostViewModel.cs @@ -1,4 +1,4 @@ -using Dragablz; +using Dragablz; using MahApps.Metro.SimpleChildWindow; using Microsoft.Web.WebView2.Core; using NETworkManager.Controls; @@ -12,25 +12,18 @@ using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; -public class WebConsoleHostViewModel : ViewModelBase, IProfileManager +public class WebConsoleHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -48,9 +41,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - /// /// Variable indicates if the Edge WebView2 runtime is available. /// @@ -80,180 +70,12 @@ public int SelectedTabIndex } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.WebConsole_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.WebConsole_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion - #region Constructor, load settings + #region Constructor public WebConsoleHostViewModel() { - _isLoading = true; - try { CoreWebView2Environment.GetAvailableBrowserVersionString(); @@ -269,34 +91,27 @@ public WebConsoleHostViewModel() TabItems = []; - // Profiles - CreateTags(); + InitializeProfileHost(); + } - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); + #endregion - SetProfilesView(new ProfileFilterInfo()); + #region Profile host - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; + protected override ApplicationName ApplicationName => ApplicationName.WebConsole; - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.WebConsole_Enabled; - LoadSettings(); + protected override string GetSearchableField(ProfileInfo profile) => profile.WebConsole_Url; - _isLoading = false; + public override void OnProfileManagerDialogOpen() + { + ConfigurationManager.OnDialogOpen(); } - private void LoadSettings() + public override void OnProfileManagerDialogClose() { - ExpandProfileView = SettingsManager.Current.WebConsole_ExpandProfileView; - - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.WebConsole_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - - _tempProfileWidth = SettingsManager.Current.WebConsole_ProfileWidth; + ConfigurationManager.OnDialogClose(); } #endregion @@ -338,96 +153,6 @@ private void ConnectProfileAction() ConnectProfile(); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.WebConsole); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ICommand OpenSettingsCommand => new RelayCommand(_ => OpenSettingsAction()); private static void OpenSettingsAction() @@ -514,151 +239,5 @@ private static void AddUrlToHistory(string url) SettingsManager.Current.General_HistoryListEntries)); } - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } - - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.WebConsole_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.WebConsole_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.WebConsole_Url.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - public void OnProfileManagerDialogOpen() - { - ConfigurationManager.OnDialogOpen(); - } - - public void OnProfileManagerDialogClose() - { - ConfigurationManager.OnDialogClose(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/ViewModels/WhoisHostViewModel.cs b/Source/NETworkManager/ViewModels/WhoisHostViewModel.cs index 86e8074257..6a1ecd6199 100644 --- a/Source/NETworkManager/ViewModels/WhoisHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/WhoisHostViewModel.cs @@ -1,30 +1,20 @@ -using Dragablz; +using Dragablz; using NETworkManager.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Models; using NETworkManager.Profiles; -using NETworkManager.Settings; using NETworkManager.Utilities; using NETworkManager.Views; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Data; using System.Windows.Input; -using System.Windows.Threading; namespace NETworkManager.ViewModels; -public class WhoisHostViewModel : ViewModelBase, IProfileManager +public class WhoisHostViewModel : ProfileHostViewModelBase { #region Variables - private readonly DispatcherTimer _searchDispatcherTimer = new(); - private bool _searchDisabled; - public IInterTabClient InterTabClient { get; } public string InterTabPartition @@ -42,9 +32,6 @@ public string InterTabPartition public ObservableCollection TabItems { get; } - private readonly bool _isLoading; - private bool _isViewActive = true; - public int SelectedTabIndex { get; @@ -58,180 +45,12 @@ public int SelectedTabIndex } } - #region Profiles - - public ICollectionView Profiles - { - get; - private set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ProfileInfo SelectedProfile - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = new(); - - public string Search - { - get; - set - { - if (value == field) - return; - - field = value; - - // Start searching... - if (!_searchDisabled) - { - IsSearching = true; - _searchDispatcherTimer.Start(); - } - - OnPropertyChanged(); - } - } - - public bool IsSearching - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool ProfileFilterIsOpen - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public ICollectionView ProfileFilterTagsView { get; } - - private ObservableCollection ProfileFilterTags { get; } = []; - - public bool ProfileFilterTagsMatchAny - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.Profile_TagsMatchAny; - - public bool ProfileFilterTagsMatchAll - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public bool IsProfileFilterSet - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } - - public GroupExpanderStateStore GroupExpanderStateStore { get; } = new(); - - private bool _canProfileWidthChange = true; - private double _tempProfileWidth; - - public bool ExpandProfileView - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.Whois_ExpandProfileView = value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(false); - - OnPropertyChanged(); - } - } - - public GridLength ProfileWidth - { - get; - set - { - if (value == field) - return; - - if (!_isLoading && Math.Abs(value.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix) // Do not save the size when collapsed - SettingsManager.Current.Whois_ProfileWidth = value.Value; - - field = value; - - if (_canProfileWidthChange) - ResizeProfile(true); - - OnPropertyChanged(); - } - } - - #endregion - #endregion #region Constructor public WhoisHostViewModel() { - _isLoading = true; - InterTabClient = new DragablzInterTabClient(ApplicationName.Whois); InterTabPartition = nameof(ApplicationName.Whois); @@ -242,35 +61,18 @@ public WhoisHostViewModel() new DragablzTabItem(Strings.NewTab, new WhoisView(tabId), tabId) ]; - // Profiles - CreateTags(); - - ProfileFilterTagsView = CollectionViewSource.GetDefaultView(ProfileFilterTags); - ProfileFilterTagsView.SortDescriptions.Add(new SortDescription(nameof(ProfileFilterTagsInfo.Name), - ListSortDirection.Ascending)); - - SetProfilesView(new ProfileFilterInfo()); - - ProfileManager.OnProfilesUpdated += ProfileManager_OnProfilesUpdated; - - _searchDispatcherTimer.Interval = GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan; - _searchDispatcherTimer.Tick += SearchDispatcherTimer_Tick; + InitializeProfileHost(); + } - LoadSettings(); + #endregion - _isLoading = false; - } + #region Profile host - private void LoadSettings() - { - ExpandProfileView = SettingsManager.Current.Whois_ExpandProfileView; + protected override ApplicationName ApplicationName => ApplicationName.Whois; - ProfileWidth = ExpandProfileView - ? new GridLength(SettingsManager.Current.Whois_ProfileWidth) - : new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); + protected override bool IsProfileEnabled(ProfileInfo profile) => profile.Whois_Enabled; - _tempProfileWidth = SettingsManager.Current.Whois_ProfileWidth; - } + protected override string GetSearchableField(ProfileInfo profile) => profile.Whois_Domain; #endregion @@ -295,96 +97,6 @@ private void QueryProfileAction() AddTab(SelectedProfile.Whois_Domain); } - public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction()); - - private void AddProfileAction() - { - _ = ProfileDialogManager - .ShowAddProfileDialog(Application.Current.MainWindow, this, null, null, ApplicationName.Whois); - } - - private bool ModifyProfile_CanExecute(object obj) - { - return SelectedProfile is { IsDynamic: false }; - } - - public ICommand EditProfileCommand => new RelayCommand(_ => EditProfileAction(), ModifyProfile_CanExecute); - - private void EditProfileAction() - { - _ = ProfileDialogManager.ShowEditProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand CopyAsProfileCommand => new RelayCommand(_ => CopyAsProfileAction(), ModifyProfile_CanExecute); - - private void CopyAsProfileAction() - { - _ = ProfileDialogManager.ShowCopyAsProfileDialog(Application.Current.MainWindow, this, SelectedProfile); - } - - public ICommand DeleteProfileCommand => new RelayCommand(_ => DeleteProfileAction(), ModifyProfile_CanExecute); - - private void DeleteProfileAction() - { - _ = ProfileDialogManager - .ShowDeleteProfileDialog(Application.Current.MainWindow, this, new List { SelectedProfile }); - } - - public ICommand EditGroupCommand => new RelayCommand(EditGroupAction); - - private void EditGroupAction(object group) - { - _ = ProfileDialogManager - .ShowEditGroupDialog(Application.Current.MainWindow, this, ProfileManager.GetGroupByName($"{group}")); - } - - public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction()); - - private void OpenProfileFilterAction() - { - ProfileFilterIsOpen = true; - } - - public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction()); - - private void ApplyProfileFilterAction() - { - RefreshProfiles(); - - ProfileFilterIsOpen = false; - } - - public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction()); - - private void ClearProfileFilterAction() - { - _searchDisabled = true; - Search = string.Empty; - _searchDisabled = false; - - foreach (var tag in ProfileFilterTags) - tag.IsSelected = false; - - RefreshProfiles(); - - IsProfileFilterSet = false; - ProfileFilterIsOpen = false; - } - - public ICommand ExpandAllProfileGroupsCommand => new RelayCommand(_ => ExpandAllProfileGroupsAction()); - - private void ExpandAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(true); - } - - public ICommand CollapseAllProfileGroupsCommand => new RelayCommand(_ => CollapseAllProfileGroupsAction()); - - private void CollapseAllProfileGroupsAction() - { - SetIsExpandedForAllProfileGroups(false); - } - public ItemActionCallback CloseItemCommand => CloseItemAction; private static void CloseItemAction(ItemActionCallbackArgs args) @@ -395,40 +107,6 @@ private static void CloseItemAction(ItemActionCallbackArgs args) #endregion #region Methods - private void SetIsExpandedForAllProfileGroups(bool isExpanded) - { - foreach (var group in Profiles.Groups.Cast()) - GroupExpanderStateStore[group.Name.ToString()] = isExpanded; - } - - private void ResizeProfile(bool dueToChangedSize) - { - _canProfileWidthChange = false; - - if (dueToChangedSize) - { - ExpandProfileView = Math.Abs(ProfileWidth.Value - GlobalStaticConfiguration.Profile_WidthCollapsed) > - GlobalStaticConfiguration.FloatPointFix; - } - else - { - if (ExpandProfileView) - { - ProfileWidth = - Math.Abs(_tempProfileWidth - GlobalStaticConfiguration.Profile_WidthCollapsed) < - GlobalStaticConfiguration.FloatPointFix - ? new GridLength(GlobalStaticConfiguration.Profile_DefaultWidthExpanded) - : new GridLength(_tempProfileWidth); - } - else - { - _tempProfileWidth = ProfileWidth.Value; - ProfileWidth = new GridLength(GlobalStaticConfiguration.Profile_WidthCollapsed); - } - } - - _canProfileWidthChange = true; - } private void AddTab(string domain = null) { @@ -440,106 +118,5 @@ private void AddTab(string domain = null) SelectedTabIndex = TabItems.Count - 1; } - public void OnViewVisible() - { - _isViewActive = true; - - RefreshProfiles(); - } - - public void OnViewHide() - { - _isViewActive = false; - } - - private void CreateTags() - { - var tags = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.Whois_Enabled) - .SelectMany(x => x.TagsCollection).Distinct().ToList(); - - var tagSet = new HashSet(tags); - - for (var i = ProfileFilterTags.Count - 1; i >= 0; i--) - { - if (!tagSet.Contains(ProfileFilterTags[i].Name)) - ProfileFilterTags.RemoveAt(i); - } - - var existingTagNames = new HashSet(ProfileFilterTags.Select(ft => ft.Name)); - - foreach (var tag in tags.Where(tag => !existingTagNames.Contains(tag))) - { - ProfileFilterTags.Add(new ProfileFilterTagsInfo(false, tag)); - } - } - - private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = null) - { - Profiles = new CollectionViewSource - { - Source = ProfileManager.LoadedProfileFileData.Groups.SelectMany(x => x.Profiles).Where(x => x.Whois_Enabled && ( - string.IsNullOrEmpty(filter.Search) || - x.Name.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1 || - x.Whois_Domain.IndexOf(filter.Search, StringComparison.OrdinalIgnoreCase) > -1) && ( - // If no tags are selected, show all profiles - (!filter.Tags.Any()) || - // Any tag can match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.Any && - filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) || - // All tags must match - (filter.TagsFilterMatch == ProfileFilterTagsMatch.All && - filter.Tags.All(tag => x.TagsCollection.Contains(tag)))) - ).OrderBy(x => x.Group).ThenBy(x => x.Name) - }.View; - - Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group))); - - // Set specific profile or first if null - SelectedProfile = null; - - if (profile != null) - SelectedProfile = Profiles.Cast().FirstOrDefault(x => x.Equals(profile)) ?? - Profiles.Cast().FirstOrDefault(); - else - SelectedProfile = Profiles.Cast().FirstOrDefault(); - } - - private void RefreshProfiles() - { - if (!_isViewActive) - return; - - var filter = new ProfileFilterInfo - { - Search = Search, - Tags = [.. ProfileFilterTags.Where(x => x.IsSelected).Select(x => x.Name)], - TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch.Any : ProfileFilterTagsMatch.All - }; - - SetProfilesView(filter, SelectedProfile); - - IsProfileFilterSet = !string.IsNullOrEmpty(filter.Search) || filter.Tags.Any(); - } - - #endregion - - #region Event - - private void ProfileManager_OnProfilesUpdated(object sender, EventArgs e) - { - CreateTags(); - - RefreshProfiles(); - } - - private void SearchDispatcherTimer_Tick(object sender, EventArgs e) - { - _searchDispatcherTimer.Stop(); - - RefreshProfiles(); - - IsSearching = false; - } - #endregion -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/Controls/ProfileExpanderPanel.xaml b/Source/NETworkManager/Views/Controls/ProfileExpanderPanel.xaml new file mode 100644 index 0000000000..a5955d852f --- /dev/null +++ b/Source/NETworkManager/Views/Controls/ProfileExpanderPanel.xaml @@ -0,0 +1,625 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/NETworkManager/Views/Controls/ProfileExpanderPanel.xaml.cs b/Source/NETworkManager/Views/Controls/ProfileExpanderPanel.xaml.cs new file mode 100644 index 0000000000..5ea10c8ba2 --- /dev/null +++ b/Source/NETworkManager/Views/Controls/ProfileExpanderPanel.xaml.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace NETworkManager.Views.Controls; + +/// +/// Reusable profile panel (search, tag filter, group list) shared by every tool view that hosts profiles. +/// Tool-specific parts (context menu, item tooltip, "run" action) are supplied by the host view through +/// dependency properties; everything else is identical across tools. +/// +public partial class ProfileExpanderPanel +{ + public static readonly DependencyProperty ProfileItemTemplateProperty = DependencyProperty.Register( + nameof(ProfileItemTemplate), typeof(DataTemplate), typeof(ProfileExpanderPanel)); + + /// + /// Gets or sets the item template used for a profile in the list (defines the tool-specific tooltip). + /// + public DataTemplate ProfileItemTemplate + { + get => (DataTemplate)GetValue(ProfileItemTemplateProperty); + set => SetValue(ProfileItemTemplateProperty, value); + } + + public static readonly DependencyProperty ProfileContextMenuExtraItemsProperty = DependencyProperty.Register( + nameof(ProfileContextMenuExtraItems), typeof(IEnumerable), typeof(ProfileExpanderPanel)); + + /// + /// Gets or sets the tool-specific menu items (commands/icons) shown at the top of the profile context + /// menu, before the always-present "Edit", "Copy as", and "Delete" items. + /// + public IEnumerable ProfileContextMenuExtraItems + { + get => (IEnumerable)GetValue(ProfileContextMenuExtraItemsProperty); + set => SetValue(ProfileContextMenuExtraItemsProperty, value); + } + + private void ContextMenu_Opened(object sender, RoutedEventArgs e) + { + // The context menu is a separate visual root (popup) and does not inherit the DataContext of the + // ListBoxItem it was opened from - fix it up here so its command bindings resolve against the + // tool's view model, same as the panel's own DataContext. + if (sender is ContextMenu menu) + menu.DataContext = DataContext; + } + + public static readonly DependencyProperty RunProfileCommandProperty = DependencyProperty.Register( + nameof(RunProfileCommand), typeof(ICommand), typeof(ProfileExpanderPanel)); + + /// + /// Gets or sets the command executed when a profile is activated (Enter key or double-click). Optional - + /// tools without a "run" action for profiles leave this unset. + /// + public ICommand RunProfileCommand + { + get => (ICommand)GetValue(RunProfileCommandProperty); + set => SetValue(RunProfileCommandProperty, value); + } + + public static readonly DependencyProperty TextBoxSearchGotFocusCommandProperty = DependencyProperty.Register( + nameof(TextBoxSearchGotFocusCommand), typeof(ICommand), typeof(ProfileExpanderPanel)); + + /// + /// Gets or sets the command executed when the search box receives focus. Optional - only used by tools + /// that pause background refreshes while the user is searching. + /// + public ICommand TextBoxSearchGotFocusCommand + { + get => (ICommand)GetValue(TextBoxSearchGotFocusCommandProperty); + set => SetValue(TextBoxSearchGotFocusCommandProperty, value); + } + + public static readonly DependencyProperty TextBoxSearchLostFocusCommandProperty = DependencyProperty.Register( + nameof(TextBoxSearchLostFocusCommand), typeof(ICommand), typeof(ProfileExpanderPanel)); + + /// + /// Gets or sets the command executed when the search box loses focus. Optional - only used by tools + /// that pause background refreshes while the user is searching. + /// + public ICommand TextBoxSearchLostFocusCommand + { + get => (ICommand)GetValue(TextBoxSearchLostFocusCommandProperty); + set => SetValue(TextBoxSearchLostFocusCommandProperty, value); + } + + public static readonly DependencyProperty ProfileContextMenuIsOpenProperty = DependencyProperty.Register( + nameof(ProfileContextMenuIsOpen), typeof(bool), typeof(ProfileExpanderPanel), + new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + /// + /// Gets or sets a value indicating whether the profile context menu is open. Optional - only used by + /// tools that need to suppress other behavior (e.g. auto-focusing an embedded window) while the menu is + /// open. + /// + public bool ProfileContextMenuIsOpen + { + get => (bool)GetValue(ProfileContextMenuIsOpenProperty); + set => SetValue(ProfileContextMenuIsOpenProperty, value); + } + + public static readonly DependencyProperty ProfileFilterPopupClosedCommandProperty = DependencyProperty.Register( + nameof(ProfileFilterPopupClosedCommand), typeof(ICommand), typeof(ProfileExpanderPanel)); + + /// + /// Gets or sets the command executed when the tag-filter popup closes (including an implicit close, e.g. + /// clicking away). Optional - only used by tools that mirror the popup's open state into global state. + /// + public ICommand ProfileFilterPopupClosedCommand + { + get => (ICommand)GetValue(ProfileFilterPopupClosedCommandProperty); + set => SetValue(ProfileFilterPopupClosedCommandProperty, value); + } + + public ProfileExpanderPanel() + { + InitializeComponent(); + } + + private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) + RunProfileCommand?.Execute(null); + } + + private void PopupProfileFilter_Closed(object sender, EventArgs e) + { + ProfileFilterPopupClosedCommand?.Execute(null); + } +} diff --git a/Source/NETworkManager/Views/DNSLookupHostView.xaml b/Source/NETworkManager/Views/DNSLookupHostView.xaml index 1e4810b387..6f9e4776e3 100644 --- a/Source/NETworkManager/Views/DNSLookupHostView.xaml +++ b/Source/NETworkManager/Views/DNSLookupHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:DNSLookupHostViewModel}"> @@ -23,6 +24,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,7 +64,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/DNSLookupHostView.xaml.cs b/Source/NETworkManager/Views/DNSLookupHostView.xaml.cs index 36a497a3a8..fc6e032285 100644 --- a/Source/NETworkManager/Views/DNSLookupHostView.xaml.cs +++ b/Source/NETworkManager/Views/DNSLookupHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public DNSLookupHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.LookupProfileCommand.Execute(null); - } - public void AddTab(string host) { _viewModel.AddTab(host); @@ -41,4 +26,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/IPGeolocationHostView.xaml b/Source/NETworkManager/Views/IPGeolocationHostView.xaml index 27f68ee132..182ce67b35 100644 --- a/Source/NETworkManager/Views/IPGeolocationHostView.xaml +++ b/Source/NETworkManager/Views/IPGeolocationHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:IPGeolocationHostViewModel}"> @@ -22,6 +23,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -31,7 +63,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/IPGeolocationHostView.xaml.cs b/Source/NETworkManager/Views/IPGeolocationHostView.xaml.cs index f24002f50d..4f8813acd3 100644 --- a/Source/NETworkManager/Views/IPGeolocationHostView.xaml.cs +++ b/Source/NETworkManager/Views/IPGeolocationHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public IPGeolocationHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.QueryProfileCommand.Execute(null); - } - public void OnViewHide() { _viewModel.OnViewHide(); @@ -36,4 +21,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/IPScannerHostView.xaml b/Source/NETworkManager/Views/IPScannerHostView.xaml index 7936d245c7..b888d10ef1 100644 --- a/Source/NETworkManager/Views/IPScannerHostView.xaml +++ b/Source/NETworkManager/Views/IPScannerHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:IPScannerHostViewModel}"> @@ -22,6 +23,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -31,7 +62,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/IPScannerHostView.xaml.cs b/Source/NETworkManager/Views/IPScannerHostView.xaml.cs index 3d1b1434a4..25d1d3a18d 100644 --- a/Source/NETworkManager/Views/IPScannerHostView.xaml.cs +++ b/Source/NETworkManager/Views/IPScannerHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public IPScannerHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ScanProfileCommand.Execute(null); - } - public void AddTab(string host) { _viewModel.AddTab(host); @@ -41,4 +26,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/NetworkInterfaceView.xaml b/Source/NETworkManager/Views/NetworkInterfaceView.xaml index 4e8efb5e9b..aa1da2d67a 100644 --- a/Source/NETworkManager/Views/NetworkInterfaceView.xaml +++ b/Source/NETworkManager/Views/NetworkInterfaceView.xaml @@ -18,6 +18,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:NetworkInterfaceViewModel}"> @@ -42,6 +43,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1470,601 +1503,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -24,14 +25,43 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -244,606 +274,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs index 9d89c54450..14ef172187 100644 --- a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs +++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs @@ -1,7 +1,4 @@ -using NETworkManager.ViewModels; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; +using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public PingMonitorHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.PingProfileCommand.Execute(null); - } - public void AddHost(string host) { if (_viewModel.SetHost(host)) @@ -42,4 +27,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/PortScannerHostView.xaml b/Source/NETworkManager/Views/PortScannerHostView.xaml index 8a478d35a5..8a820e112a 100644 --- a/Source/NETworkManager/Views/PortScannerHostView.xaml +++ b/Source/NETworkManager/Views/PortScannerHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:PortScannerHostViewModel}"> @@ -23,6 +24,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,7 +69,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/PortScannerHostView.xaml.cs b/Source/NETworkManager/Views/PortScannerHostView.xaml.cs index 2b115f44cf..30e612a289 100644 --- a/Source/NETworkManager/Views/PortScannerHostView.xaml.cs +++ b/Source/NETworkManager/Views/PortScannerHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public PortScannerHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ScanProfileCommand.Execute(null); - } - public void AddTab(string host) { _viewModel.AddTab(host); @@ -41,4 +26,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/PowerShellHostView.xaml b/Source/NETworkManager/Views/PowerShellHostView.xaml index 542b612821..4b402065b6 100644 --- a/Source/NETworkManager/Views/PowerShellHostView.xaml +++ b/Source/NETworkManager/Views/PowerShellHostView.xaml @@ -17,6 +17,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" Loaded="UserControl_Loaded" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:PowerShellHostViewModel}"> @@ -26,6 +27,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -45,7 +90,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Source/NETworkManager/Views/PowerShellHostView.xaml.cs b/Source/NETworkManager/Views/PowerShellHostView.xaml.cs index 1edb19d556..9c80c79016 100644 --- a/Source/NETworkManager/Views/PowerShellHostView.xaml.cs +++ b/Source/NETworkManager/Views/PowerShellHostView.xaml.cs @@ -1,8 +1,6 @@ -using NETworkManager.ViewModels; +using NETworkManager.ViewModels; using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; namespace NETworkManager.Views; @@ -23,22 +21,10 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) _loaded = true; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ConnectProfileCommand.Execute(null); - } - public async void AddTab(string host) { - // Wait for the interface to load, before displaying the dialog to connect a new profile... - // MahApps will throw an exception... + // Wait for the interface to load, before displaying the dialog to connect a new profile... + // MahApps will throw an exception... while (!_loaded) await Task.Delay(250); @@ -60,9 +46,4 @@ public void FocusEmbeddedWindow() { _viewModel.FocusEmbeddedWindow(); } - - private void PopupProfileFilter_Closed(object sender, System.EventArgs e) - { - _viewModel.OnProfileFilterClosed(); - } } diff --git a/Source/NETworkManager/Views/PuTTYHostView.xaml b/Source/NETworkManager/Views/PuTTYHostView.xaml index 515de19a10..f158446330 100644 --- a/Source/NETworkManager/Views/PuTTYHostView.xaml +++ b/Source/NETworkManager/Views/PuTTYHostView.xaml @@ -17,6 +17,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" Loaded="UserControl_Loaded" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:PuTTYHostViewModel}"> @@ -26,6 +27,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -45,7 +89,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Source/NETworkManager/Views/PuTTYHostView.xaml.cs b/Source/NETworkManager/Views/PuTTYHostView.xaml.cs index e9a52605f4..819d3310cb 100644 --- a/Source/NETworkManager/Views/PuTTYHostView.xaml.cs +++ b/Source/NETworkManager/Views/PuTTYHostView.xaml.cs @@ -1,9 +1,6 @@ -using System; +using NETworkManager.ViewModels; using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -24,22 +21,10 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) _loaded = true; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ConnectProfileCommand.Execute(null); - } - public async void AddTab(string host) { - // Wait for the interface to load, before displaying the dialog to connect a new profile... - // MahApps will throw an exception... + // Wait for the interface to load, before displaying the dialog to connect a new profile... + // MahApps will throw an exception... while (!_loaded) await Task.Delay(250); @@ -61,9 +46,4 @@ public void FocusEmbeddedWindow() { _viewModel.FocusEmbeddedWindow(); } - - private void PopupProfileFilter_Closed(object sender, EventArgs e) - { - _viewModel.OnProfileFilterClosed(); - } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/RemoteDesktopHostView.xaml b/Source/NETworkManager/Views/RemoteDesktopHostView.xaml index ac90f0a81f..a47620e7b8 100644 --- a/Source/NETworkManager/Views/RemoteDesktopHostView.xaml +++ b/Source/NETworkManager/Views/RemoteDesktopHostView.xaml @@ -16,6 +16,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" Loaded="UserControl_Loaded" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:RemoteDesktopHostViewModel}"> @@ -25,6 +26,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -34,7 +87,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/RemoteDesktopHostView.xaml.cs b/Source/NETworkManager/Views/RemoteDesktopHostView.xaml.cs index 3e38844ea4..d33633292b 100644 --- a/Source/NETworkManager/Views/RemoteDesktopHostView.xaml.cs +++ b/Source/NETworkManager/Views/RemoteDesktopHostView.xaml.cs @@ -1,8 +1,6 @@ -using NETworkManager.ViewModels; +using NETworkManager.ViewModels; using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; namespace NETworkManager.Views; @@ -23,21 +21,10 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) _loaded = true; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ConnectProfileCommand.Execute(null); - } - public async void AddTab(string host) { - // Wait for the interface to load, before displaying the dialog to connect a new Profile... - // MahApps will throw an exception... + // Wait for the interface to load, before displaying the dialog to connect a new Profile... + // MahApps will throw an exception... while (!_loaded) await Task.Delay(250); @@ -53,4 +40,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/SNMPHostView.xaml b/Source/NETworkManager/Views/SNMPHostView.xaml index df326f7b1e..003dd4dfd5 100644 --- a/Source/NETworkManager/Views/SNMPHostView.xaml +++ b/Source/NETworkManager/Views/SNMPHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:SNMPHostViewModel}"> @@ -22,6 +23,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -31,7 +62,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/SNMPHostView.xaml.cs b/Source/NETworkManager/Views/SNMPHostView.xaml.cs index afb45a4eb6..fb7a33a8b7 100644 --- a/Source/NETworkManager/Views/SNMPHostView.xaml.cs +++ b/Source/NETworkManager/Views/SNMPHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public SNMPHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.AddTabProfileCommand.Execute(null); - } - public void AddTab(string host) { _viewModel.AddTab(host); @@ -41,4 +26,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/TigerVNCHostView.xaml b/Source/NETworkManager/Views/TigerVNCHostView.xaml index 006ff0358d..228da7c123 100644 --- a/Source/NETworkManager/Views/TigerVNCHostView.xaml +++ b/Source/NETworkManager/Views/TigerVNCHostView.xaml @@ -16,6 +16,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" Loaded="UserControl_Loaded" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:TigerVNCHostViewModel}"> @@ -25,6 +26,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -44,7 +88,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Source/NETworkManager/Views/TigerVNCHostView.xaml.cs b/Source/NETworkManager/Views/TigerVNCHostView.xaml.cs index 409a56b999..d556299387 100644 --- a/Source/NETworkManager/Views/TigerVNCHostView.xaml.cs +++ b/Source/NETworkManager/Views/TigerVNCHostView.xaml.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; +using System.Threading.Tasks; +using System.Windows; namespace NETworkManager.Views; @@ -23,22 +21,10 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e) _loaded = true; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ConnectProfileCommand.Execute(null); - } - public async void AddTab(string host) { - // Wait for the interface to load, before displaying the dialog to connect a new profile... - // MahApps will throw an exception... + // Wait for the interface to load, before displaying the dialog to connect a new profile... + // MahApps will throw an exception... while (!_loaded) await Task.Delay(250); @@ -55,4 +41,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/TracerouteHostView.xaml b/Source/NETworkManager/Views/TracerouteHostView.xaml index acc4550b82..e4818e536d 100644 --- a/Source/NETworkManager/Views/TracerouteHostView.xaml +++ b/Source/NETworkManager/Views/TracerouteHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:TracerouteHostViewModel}"> @@ -23,6 +24,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,7 +63,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/TracerouteHostView.xaml.cs b/Source/NETworkManager/Views/TracerouteHostView.xaml.cs index 4e91e5791b..2805de869b 100644 --- a/Source/NETworkManager/Views/TracerouteHostView.xaml.cs +++ b/Source/NETworkManager/Views/TracerouteHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.Profiles; using NETworkManager.ViewModels; @@ -16,18 +13,6 @@ public TracerouteHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.TraceProfileCommand.Execute(null); - } - public void AddTab(string host) { _viewModel.AddTab(host); @@ -47,4 +32,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/WakeOnLANView.xaml b/Source/NETworkManager/Views/WakeOnLANView.xaml index 6aebbc2b6b..29f5d6f368 100644 --- a/Source/NETworkManager/Views/WakeOnLANView.xaml +++ b/Source/NETworkManager/Views/WakeOnLANView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:WakeOnLANViewModel}"> @@ -23,13 +24,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -151,579 +182,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/WakeOnLANView.xaml.cs b/Source/NETworkManager/Views/WakeOnLANView.xaml.cs index 225e7f6819..07e67ab28d 100644 --- a/Source/NETworkManager/Views/WakeOnLANView.xaml.cs +++ b/Source/NETworkManager/Views/WakeOnLANView.xaml.cs @@ -1,5 +1,3 @@ -using System.Windows; -using System.Windows.Controls; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -14,12 +12,6 @@ public WakeOnLANView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - public void OnViewHide() { _viewModel.OnViewHide(); @@ -29,4 +21,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/WebConsoleHostView.xaml b/Source/NETworkManager/Views/WebConsoleHostView.xaml index 806528792f..3febe5c8b3 100644 --- a/Source/NETworkManager/Views/WebConsoleHostView.xaml +++ b/Source/NETworkManager/Views/WebConsoleHostView.xaml @@ -17,6 +17,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:WebConsoleHostViewModel}"> @@ -24,6 +25,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -43,7 +75,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Source/NETworkManager/Views/WebConsoleHostView.xaml.cs b/Source/NETworkManager/Views/WebConsoleHostView.xaml.cs index 510ed18104..2f5f3c937c 100644 --- a/Source/NETworkManager/Views/WebConsoleHostView.xaml.cs +++ b/Source/NETworkManager/Views/WebConsoleHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public WebConsoleHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.ConnectProfileCommand.Execute(null); - } - public void OnViewHide() { _viewModel.OnViewHide(); @@ -36,4 +21,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Source/NETworkManager/Views/WhoisHostView.xaml b/Source/NETworkManager/Views/WhoisHostView.xaml index 8dc0c38efb..7f46fbba6f 100644 --- a/Source/NETworkManager/Views/WhoisHostView.xaml +++ b/Source/NETworkManager/Views/WhoisHostView.xaml @@ -15,6 +15,7 @@ xmlns:profiles="clr-namespace:NETworkManager.Profiles;assembly=NETworkManager.Profiles" xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF" xmlns:networkManager="clr-namespace:NETworkManager" + xmlns:hostControls="clr-namespace:NETworkManager.Views.Controls" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:WhoisHostViewModel}"> @@ -22,6 +23,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -31,7 +63,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Source/NETworkManager/Views/WhoisHostView.xaml.cs b/Source/NETworkManager/Views/WhoisHostView.xaml.cs index f598e20724..dfda536745 100644 --- a/Source/NETworkManager/Views/WhoisHostView.xaml.cs +++ b/Source/NETworkManager/Views/WhoisHostView.xaml.cs @@ -1,6 +1,3 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; using NETworkManager.ViewModels; namespace NETworkManager.Views; @@ -15,18 +12,6 @@ public WhoisHostView() DataContext = _viewModel; } - private void ContextMenu_Opened(object sender, RoutedEventArgs e) - { - if (sender is ContextMenu menu) - menu.DataContext = _viewModel; - } - - private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - _viewModel.QueryProfileCommand.Execute(null); - } - public void OnViewHide() { _viewModel.OnViewHide(); @@ -36,4 +21,4 @@ public void OnViewVisible() { _viewModel.OnViewVisible(); } -} \ No newline at end of file +} diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md index 4106e6b524..2df41043a1 100644 --- a/Website/docs/changelog/next-release.md +++ b/Website/docs/changelog/next-release.md @@ -32,5 +32,6 @@ Release date: **xx.xx.2026** ## Dependencies, Refactoring & Documentation - Code cleanup & refactoring +- Consolidated the duplicated **Profiles** side panel (search, tag filter, grouped list, context menu, add/edit/copy/delete) used across 15 tool views into a single shared control, reducing code duplication. As part of this, the profile panel's expanded/width state is now shared across all tools instead of being tracked per tool. [#3537](https://github.com/BornToBeRoot/NETworkManager/pull/3537) - Language files updated via [#transifex](https://github.com/BornToBeRoot/NETworkManager/pulls?q=author%3Aapp%2Ftransifex-integration) - Dependencies updated via [#dependabot](https://github.com/BornToBeRoot/NETworkManager/pulls?q=author%3Aapp%2Fdependabot)