Refactor: Improve architecture, performance, and class organization#1
Refactor: Improve architecture, performance, and class organization#1
Conversation
This commit addresses a broad set of issues aimed at improving your application's performance, reorganizing its architecture, and clarifying class responsibilities. Key Architectural Changes: - Eliminated the global `PublicClass` to reduce tight coupling and improve testability. - Introduced `SettingsService` to manage loading and saving of application settings from/to JSON, centralizing settings management. - Introduced `ImageCacheService` to handle loading and caching of all images and icons, significantly improving UI rendering performance for elements with icons. - Refactored `ItemsManager` into an instance-based class, separating its data manipulation logic from UI concerns. It now uses events to notify the UI of changes. - Created `TreeViewHelper` to encapsulate TreeView-specific UI logic, previously mixed in `Utility` or `ItemsManager`. Performance Enhancements: - Implemented image caching via `ImageCacheService`, reducing redundant file I/O and icon extraction operations. - Applied `SuspendLayout` and `ResumeLayout` to `FrmMain`'s context menu population and `TreeViewHelper`'s tree loading, improving UI update performance and reducing flicker. Code Organization: - Restructured the project by creating `Models` and `UI/Helpers` folders for better class categorization. - Moved data model classes (ListInfo, GroupInfo, etc.) to the `Models` namespace/folder. - Moved `TreeViewHelper` to the `UI.Helpers` namespace/folder. - Standardized namespaces for utility classes. Note: Direct performance testing and full build verification were conducted based on code analysis due to limitations in the execution environment. The changes are expected to yield significant improvements in maintainability and UI responsiveness.
There was a problem hiding this comment.
Hello @T5ive, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
Summary of Changes
Gemini here, providing a summary of this pull request authored by T5ive.
This PR focuses on a significant refactor aimed at improving the application's architecture, performance, and overall code organization. The core changes involve moving away from a global state (PublicClass) towards a more service-oriented and instance-based design. New services (SettingsService, ImageCacheService) are introduced to handle specific concerns like settings management and image loading/caching, while the ItemsManager is refactored into an instance class responsible solely for data manipulation.
UI logic previously mixed in data or utility classes has been extracted into a dedicated helper (TreeViewHelper). Performance enhancements include implementing image caching to reduce file I/O and using SuspendLayout/ResumeLayout for smoother UI updates. The project structure is also improved with new folders and standardized namespaces for better clarity and maintainability.
Highlights
- Architectural Refactor: Eliminated the global
PublicClassdependency, promoting better encapsulation and testability. - New Services Introduced: Added
SettingsServicefor centralized JSON settings loading/saving andImageCacheServicefor efficient image/icon handling with caching. - ItemsManager Refactored: Converted
ItemsManagerfrom a static helper to an instance class, separating data logic from UI concerns and using events (ItemsChanged) to notify the UI. - UI Logic Extraction: Created
TreeViewHelperto consolidate TreeView-specific UI manipulation logic, removing it fromUtilityandItemsManager. - Performance Improvements: Implemented image caching via
ImageCacheServiceand appliedSuspendLayout/ResumeLayoutto UI updates for better responsiveness. - Code Organization: Restructured the project with
ModelsandUI/Helpersfolders and standardized namespaces. - Dependency Injection: Updated form constructors (
FrmMain,FrmSetting) to accept service dependencies.
Changelog
Click here to see the changelog
- Toolkit Launcher/Form/FrmMain.cs
- Added
usingstatements forModelsandServices. - Added
_settingsServiceand_imageCacheServicereadonly fields. - Modified constructor to accept
SettingsServiceandImageCacheServicevia DI (lines 13-18). - Removed old static initialization and loading methods (
InitializeList,LoadFile,LoadJson) (lines 36-44). - Updated
FrmMain_Loadto remove calls to old initialization methods (lines 36-37). - Updated
AddItemsto use_settingsService.CurrentSettingsand addedSuspendLayout/ResumeLayout(lines 48-51). - Modified
FrmSettinginstantiation inAddSettingto pass service instances (line 65). - Updated
AddAllmethods to use_imageCacheService.GetImagefor loading icons and added null/default image checks (lines 119-127, 173-179).
- Added
- Toolkit Launcher/Form/FrmSetting.cs
- Added various
usingstatements for new/moved namespaces (UI.Helpers,Models,Services,ItemManager,Utilities) (lines 1-11). - Replaced static
_editorwith instance field (line 17). - Added
_settingsService,_imageCacheService,_itemsManagerinstance fields (lines 18-20). - Modified constructor to accept services via DI, initialize
_itemsManager, and subscribe toItemsChanged(lines 23-40). - Added
ItemsManager_ItemsChangedevent handler to reload the TreeView and restore selection (lines 42-50). - Updated
FrmSetting_Loadto useTreeViewHelper.LoadAlland_settingsService.TempSettings(lines 53-63). - Refactored
SaveFileto use_settingsService.SaveSettings(lines 67-86). - Refactored Add Item methods (
btnAddHeader_Click, etc.) to use a commonHandleAddItemmethod which calls_itemsManager.AddGroupandTreeViewHelper.SelectNodeByPath(lines 90-120). - Updated
treeOption_AfterSelectto use_itemsManagermethods for getting type and detail (lines 123-134). - Updated
EnableTypelogic based on selection type and enabled/disabled context menu items (lines 137-151). - Refactored
GetDetailto use_itemsManager.GetGroupInfoByPathand handle null cases (lines 155-194). - Updated
treeOption_MouseDownto handle right-click selection and callMenuController(lines 211-230). - Updated
MenuControllerlogic based on node selection and type (lines 232-260). - Updated
treeOption_KeyDownto use_itemsManager.RemoveGroup(lines 264-281). - Updated
treeOption_AfterLabelEditto use_itemsManager.RenameGroup(lines 283-326). - Refactored context menu add clicks to
ContextMenuAdd_Click(lines 352-364). - Updated detail panel button clicks (
btnName_Click,btnPath_Click,btnIcon_Click) to use_itemsManagermethods (lines 376-452). - Refactored detail panel text/checkbox changes to call
SaveTempAdvanceInfowhich uses_itemsManager.UpdateGroupAdvanceInfo(lines 469-491). - Updated
btnCancel_Clickto call_settingsService.ReloadTempSettings(lines 454-457). - Added
FrmSetting_FormClosingto unsubscribe from theItemsChangedevent (lines 494-501).
- Added various
- Toolkit Launcher/ItemManager/ItemsManager.cs
- Changed namespace from
Toolkit_Launcher.HelpertoToolkit_Launcher.ItemManager(line 2). - Changed from static class to instance class
ItemsManager(line 12). - Added
_listInfoinstance field and constructor to initialize it (lines 14-23). - Added
ItemsChangedevent andOnItemsChangedmethod (lines 17-28). - Converted all methods from static to instance.
- Removed
TreeViewparameters from methods that previously manipulated the UI directly. - Modified methods (
AddGroup,RemoveGroup,MoveGroup,RenameGroup,UpdateGroupValuePaths,UpdateGroupAdvanceInfo) to modify the internal_listInfoand callOnItemsChanged. - Renamed and refactored methods for getting group info by path (
GetParentGroupInfoByPath,GetGroupInfoByPath,GetGroupInfoByPathRecursive) (lines 114-148). - Renamed
GetTypetoGetGroupType(lines 152-156). - Removed
GetGroupDetail. - Added
UpdateChildFullPathshelper for renaming logic (lines 271-289). - Updated
GetNewIndexmethods to work with lists ofGroupInfo(lines 105-111).
- Changed namespace from
- Toolkit Launcher/Models/ListInfo.cs
- Added
using Toolkit_Launcher;forMyEnums(line 3).
- Added
- Toolkit Launcher/Program.cs
- Instantiated
SettingsServiceandImageCacheService(lines 10-11). - Modified
FrmMaininstantiation to pass service instances (line 13). - Note: The
Application.Runline appears duplicated in the patch (lines 13-14).
- Instantiated
- Toolkit Launcher/Services/ImageCacheService.cs
- New file created in
Toolkit_Launcher.Servicesnamespace (lines 1-120). - Includes
_imageListand_fileImageCachefields. - Provides
GetImageoverloads forMyEnums.GroupTypeand file paths. - Implements caching for file-based images.
- Includes
IconFromFilePathhelper for extracting icons. - Provides
GetSystemImageListfor UI controls.
- New file created in
- Toolkit Launcher/Services/SettingsService.cs
- New file created in
Toolkit_Launcher.Servicesnamespace (lines 1-62). - Manages loading (
LoadSettingsFromFile) and saving (SaveSettings) ofListInfofrom/to JSON. - Uses
CurrentSettingsandTempSettingsproperties. - Provides
ReloadTempSettingsmethod. - Uses
Newtonsoft.Json.
- New file created in
- Toolkit Launcher/UI/Helpers/TreeViewHelper.cs
- New file created in
Toolkit_Launcher.UI.Helpersnamespace (lines 1-145). - Contains static methods for TreeView manipulation (
LoadAll,CreateTreeNode,GetImageIndex,SelectNodeByPath,FindNodeRecursive,AddTreeNode). - Methods accept
ListInfoandImageCacheServiceparameters. - Uses
TreeNode.Tagto store the full path.
- New file created in
- Toolkit Launcher/Utilities/Utility.cs
- Changed namespace from
Toolkit_LaunchertoToolkit_Launcher.Utilities(line 7). - Removed TreeView-specific methods (
AddGroup,AddSubGroup,LoadAll,SelectNode,SelectNodeChild) (lines 17-18). - Removed image loading methods (
IconFromFilePath,GetImageextensions) (lines 52-53). - Added null/empty checks in
GetMissingNumber,FindIndex, andGetTarget(lines 24, 37, 43, 54). - Added
ToLowerInvariantinGetTargetswitch (line 63). - Added basic validation in
IsTarget(line 73).
- Changed namespace from
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
This pull request introduces significant and positive architectural changes, moving towards a more decoupled, service-oriented design. The introduction of SettingsService, ImageCacheService, an instance-based ItemsManager, and TreeViewHelper are commendable. The elimination of global state via PublicClass is a key improvement. The changes should also lead to better performance due to image caching and UI update optimizations.
While the refactoring is extensive and well-executed in many areas, there are a few points that need attention, including one critical issue in Program.cs.
Summary of Findings
- Critical: Duplicate Application Run: In
Program.cs,Application.Run()is called twice, which will lead to the main form running sequentially two times. This needs to be corrected. - High: Item Selection After Rename: In
FrmSetting.cs, after an item is renamed via inline TreeView edit, the selection logic inItemsManager_ItemsChangeduses the old item path, potentially leading to lost or incorrect selection. This impacts user experience. - Medium: Null-Forgiving Operator Usage: The null-forgiving operator (
!) is used with_settingsService.CurrentSettingsinFrmMain.cs. Given the initialization logic,CurrentSettingsshould not be null. Consider removing the operator or clarifying the nullability scenario. - Medium: Initialization Logic in
FrmSetting_Load: The check for_settingsService.TempSettings == nullinFrmSetting_Loadseems defensive, asTempSettingsshould be initialized by theSettingsServiceconstructor. Clarification on this scenario is needed. - Medium: Placeholder Default Images:
ImageCacheServiceuses placeholderBitmap(16,16)for default images. These should be replaced with actual icons. - Medium: Service Error Handling:
SettingsServiceusesConsole.WriteLinefor error handling. A more user-facing approach or robust logging is recommended for file I/O errors. - Medium: Legacy
ImageIDProperty: TheImageIDproperty inValueInfo(set byItemsManager) might be a remnant from older logic. Its current necessity should be reviewed given the new image handling mechanisms.
Merge Readiness
This pull request represents a significant improvement to the codebase's architecture and maintainability. However, due to the critical issue identified in Program.cs (duplicate Application.Run) and the high-severity issue regarding item re-selection after renaming in FrmSetting.cs, I recommend that these changes not be merged until these issues are addressed. The medium-severity items should also be considered for improvement. I am not authorized to approve pull requests, so please ensure these points are resolved and further review is conducted before merging.
| // Success. ItemsManager_ItemsChanged will refresh. | ||
| // After refresh, try to reselect the item with its new path. | ||
| var parentPath = originalPath.Contains("\") ? originalPath.Substring(0, originalPath.LastIndexOf("\")) : ""; | ||
| var newPath = string.IsNullOrEmpty(parentPath) ? e.Label : $"{parentPath}\{e.Label}"; | ||
| // The event handler will call LoadAll, then try to reselect using the old path. | ||
| // We need to ensure the selection is updated to the new path. | ||
| // So, FrmSetting.ItemsManager_ItemsChanged needs to be smarter about selection after rename. | ||
| // For now, the event handler reloads and tries to select original path, which won't exist. | ||
| // A quick fix: update tag here, so that re-selection logic might find it IF name is path component. | ||
| // e.Node.Tag = newPath; // This is tricky because tree is about to be rebuilt. | ||
| // The ItemsChanged event handler should ideally handle selection restoration. | ||
| } |
There was a problem hiding this comment.
When an item is renamed using the TreeView's inline editing (treeOption_AfterLabelEdit), the ItemsManager_ItemsChanged event is triggered, which reloads the TreeView. However, ItemsManager_ItemsChanged attempts to re-select the node using previouslySelectedPath, which is the old path of the item before renaming.
This means that after a successful rename, the selection might be lost or jump to an unexpected node if the old path no longer exists or if another item now coincidentally has that old path (unlikely but possible).
Have you considered how to ensure the newly renamed item is selected after the TreeView reloads? This might involve ItemsManager.RenameGroup returning the new path, or ItemsManager raising a more specific event like ItemRenamed(string oldPath, string newPath) that FrmSetting can use to update its selection target.
| { | ||
| if (PublicClass.ListInfo == null!) return; | ||
| AddAll(contextMenuStrip1, PublicClass.ListInfo); | ||
| if (_settingsService.CurrentSettings == null!) return; // Use SettingsService |
There was a problem hiding this comment.
Is the null-forgiving operator (!) necessary here for _settingsService.CurrentSettings? The SettingsService constructor initializes CurrentSettings by calling LoadSettingsFromFile(), which returns a new ListInfo() if the file doesn't exist or if an error occurs during loading. This suggests CurrentSettings should always be non-null after SettingsService instantiation.
If CurrentSettings can indeed be null under some circumstance not immediately apparent, could we add a comment explaining that scenario or handle the null case more explicitly rather than suppressing a potential warning?
| // menu.Items.Add(group.Type.ToString(), image); // This line seems to be an early exit, might need review | ||
| // return; // This line seems to be an early exit, might need review |
There was a problem hiding this comment.
These lines for adding a menu item and returning were commented out:
// menu.Items.Add(group.Type.ToString(), image);
// return; Was the early return intentional in the previous logic? If so, removing it changes the behavior of how items are added when a specific icon path isn't found or is equivalent to the 'Null' type image. Could you clarify if this change in behavior (processing subsequent items instead of an early exit) is intended as part of the refactor?
| { | ||
| // Consider initializing TempSettings with a default ListInfo if null | ||
| _settingsService.TempSettings = new ListInfo(); // Ensure it's not null | ||
| _itemsManager = new ItemsManager(_settingsService.TempSettings); // Re-assign if TempSettings was null | ||
| _itemsManager.ItemsChanged += ItemsManager_ItemsChanged; // Re-subscribe | ||
| } | ||
| TreeViewHelper.LoadAll(treeOption, _settingsService.TempSettings, _imageCacheService); |
There was a problem hiding this comment.
In FrmSetting_Load, there's a check if (_settingsService.TempSettings == null). The SettingsService constructor initializes TempSettings with a deep copy of CurrentSettings, and CurrentSettings is initialized (even to an empty ListInfo) during SettingsService construction.
Could you clarify the scenario where _settingsService.TempSettings might be null at this point? If it's not expected to be null, this block might be unnecessary. If it can be null, perhaps the initialization logic in SettingsService needs to be revisited to ensure TempSettings is always initialized.
| _imageList.Images.Add(new Bitmap(16,16)); // 0 - Null | ||
| _imageList.Images.Add(new Bitmap(16,16)); // 1 - Header | ||
| _imageList.Images.Add(new Bitmap(16,16)); // 2 - Item | ||
| _imageList.Images.Add(new Bitmap(16,16)); // 3 - Separator |
There was a problem hiding this comment.
The default images initialized in InitializeDefaultImages are currently new Bitmap(16,16). While fine for placeholders during development, for the actual application, should these be replaced with meaningful default icons (e.g., loaded from embedded resources or specific files) to provide a better user experience when an icon cannot be found or for default types?
| catch (Exception ex) | ||
| { | ||
| // Consider logging this error appropriately | ||
| Console.WriteLine($"Error loading settings: {ex.Message}"); | ||
| return new ListInfo(); // Return default/empty on error | ||
| } |
There was a problem hiding this comment.
Error handling during settings load/save currently uses Console.WriteLine. For errors like a corrupted settings file or inability to write the file, this might not be visible or helpful to the end-user.
Should critical errors during these operations (especially loading) be communicated to the user via MyMessage.ShowError or a similar mechanism? For non-critical issues, a more robust logging framework (e.g., Serilog, NLog) would be beneficial over Console.WriteLine in a production application.
This commit addresses a broad set of issues aimed at improving your application's performance, reorganizing its architecture, and clarifying class responsibilities.
Key Architectural Changes:
PublicClassto reduce tight coupling and improve testability.SettingsServiceto manage loading and saving of application settings from/to JSON, centralizing settings management.ImageCacheServiceto handle loading and caching of all images and icons, significantly improving UI rendering performance for elements with icons.ItemsManagerinto an instance-based class, separating its data manipulation logic from UI concerns. It now uses events to notify the UI of changes.TreeViewHelperto encapsulate TreeView-specific UI logic, previously mixed inUtilityorItemsManager.Performance Enhancements:
ImageCacheService, reducing redundant file I/O and icon extraction operations.SuspendLayoutandResumeLayouttoFrmMain's context menu population andTreeViewHelper's tree loading, improving UI update performance and reducing flicker.Code Organization:
ModelsandUI/Helpersfolders for better class categorization.Modelsnamespace/folder.TreeViewHelperto theUI.Helpersnamespace/folder.Note: Direct performance testing and full build verification were conducted based on code analysis due to limitations in the execution environment. The changes are expected to yield significant improvements in maintainability and UI responsiveness.