A powerful Windows Forms desktop application for managing, organizing, and exporting icon and image libraries with database-driven tagging, searching, and project integration capabilities.
- Overview
- Key Features
- Installation
- Getting Started
- Usage Guide
- Configuration
- Database Schema
- Architecture
- Development
- Troubleshooting
- Contributing
IconCommander is a comprehensive icon library management system that helps developers organize thousands of icons across multiple collections, tag them for easy discovery, and export them directly to their development projects. The application uses SQLite for efficient storage and retrieval, supports bulk imports with automatic tagging, and provides advanced features like icon merging and project integration.
- Collections: Top-level groupings for icon sets (e.g., "Font Awesome", "Material Design")
- Veins: Folder-based sources of icons within collections (linked to filesystem directories)
- Icons: Logical icon entities that may have multiple file formats and sizes
- IconFiles: Individual icon files with binary data, dimensions, and metadata
- Tags: Searchable keywords automatically extracted from filenames or JSON mappings
- Projects: Development projects (Windows or Web) where icons can be exported
- Database-Driven Organization: All icons stored in SQLite with rich metadata
- Hierarchical Structure: Collections → Veins → Icons → IconFiles
- Binary Storage: Icons stored as BLOBs with SHA256 deduplication
- Multi-Format Support: .ico, .png, .jpg, .bmp, .gif, .svg, .tiff, .wmf, .emf
- Automatic Tagging: Extract tags from filenames or JSON mapping files
- Recursive Directory Scanning: Import entire folder hierarchies
- Progress Tracking: Real-time import progress with BackgroundWorker
- JSON Keyword Mapping: Support for Iconex format keyword definitions
- Duplicate Detection: SHA256 hash-based deduplication
- SVG Support: Parse SVG XML for accurate dimension extraction
- Cancellable Operations: Cancel long-running imports
- Windows Projects: Export to resource files (.resx) and project folders
- Web Projects: Export to web resource directories
- Auto-Update Project Files: Automatically update .csproj with new icons
- Export Tracking: Record all exports in ProjectIcons table
- Icon Merging: Combine icons with configurable overlay positions
- Multiple Sizes: Support for icons in various dimensions
- Format Conversion: Convert between different image formats
- Preview System: View icons before export
- Theme Support: Multiple UI themes with persistence (ZidThemes)
- Dynamic Forms: Auto-generated CRUD forms with UIGenerator
- Enhanced Grids: Sortable, filterable data grids (ZidGrid)
- Breadcrumb Navigation: Folder navigation with AddressBar control
- Operating System: Windows 7 or later
- .NET Framework: 4.8 or higher
- Visual Studio: 2019 or later (for development)
- NuGet: Package manager (integrated with Visual Studio)
-
Clone the Repository
git clone https://github.com/yourusername/IconCommander.git cd IconCommander -
Restore NuGet Packages
nuget restore IconCommander.sln
-
Build the Solution
msbuild IconCommander.sln /p:Configuration=Release /v:minimal
-
Run the Application
.\IconCommander\bin\Release\IconCommander.exe
On first launch, IconCommander will:
- Create a default SQLite database (
Icons.db) - Initialize the database schema with all required tables
- Apply the default UI theme
- Create configuration entries in
App.config
-
Create a Collection
- Navigate to
Icons → Collections → Manage Collections - Click the Insert button
- Enter collection name and description
- Click Save
- Navigate to
-
Import Icons from a Folder
- Navigate to
Icons → Veins → Import Vein - Select an existing collection or create a new one
- Enter a name for the vein
- Browse to the folder containing your icons
- (Optional) Provide a JSON tags file for advanced keyword mapping
- Click Import and wait for completion
- Navigate to
-
Create a Project
- Navigate to
Project → Create Project - Enter project details (name, path, type)
- Configure export settings
- Click Save
- Navigate to
-
Browse and Export Icons
- Browse icons from the main window
- Select an icon and size
- Click Export to send to your active project
Collections are the top-level organizational units for your icon libraries.
Creating a Collection:
Menu: Icons → Collections → Manage Collections
Button: Insert
Fields:
- Name (required): Display name for the collection
- Description: Detailed description
- Comments: Internal notes
- License: Associated license (optional)
Editing a Collection:
- Select a collection from the grid
- Click Update
- Modify fields and click Save
Deleting a Collection:
- Select a collection from the grid
- Click Delete
- Confirm deletion (warning: cascades to veins and icons)
Veins represent folder-based icon sources linked to your filesystem.
Creating a Vein:
Menu: Icons → Veins → Manage Veins
Button: Insert
Fields:
- Collection (required): Parent collection (dropdown)
- Name (required): Vein display name
- Description: Detailed description
- Path (required): Folder path on disk
- IsIcon: Contains .ico files
- IsImage: Contains image files (.png, .jpg, etc.)
- IsSvg: Contains .svg files
Best Practices:
- Use descriptive vein names that reflect the icon style or source
- Ensure the Path points to a valid directory
- Set the correct file type flags (IsIcon, IsImage, IsSvg)
The import system supports bulk importing with automatic tagging.
Basic Import (Filename-Based Tags):
- Navigate to
Icons → Veins → Import Vein - Select a vein from the dropdown
- The vein path will be loaded automatically
- Review the folder tree to verify contents
- Click Import
- Monitor progress in the log window
Advanced Import (JSON Keyword Mapping):
- Prepare a JSON tags file in Iconex format:
{
"From": "iconex_keywords",
"To": "IconCommander",
"Mappings": [
{
"Text": "account",
"References": [123, 456, 789]
},
{
"Text": "user",
"References": [123, 456]
}
]
}- In the Import Vein dialog, click Browse next to Tags File
- Select your JSON file
- The system will load keywords into temporary tables
- Click Import
- Icons will be tagged using the JSON mappings
Import Process Details:
- All files are scanned recursively
- Binary data is read and stored in the database
- SHA256 hashes are calculated for deduplication
- Duplicate files (same hash) are skipped
- For SVG files, dimensions are extracted from XML attributes
- For images, dimensions are extracted using System.Drawing.Image
- Tags are extracted from JSON or filename (split by space, underscore, hyphen)
- Progress is reported every 100 files or 1 second
- Operations can be cancelled at any time
Projects represent your development projects where icons will be exported.
Creating a Windows Project:
Menu: Project → Create Project
Fields:
- Name: Project display name
- Path: Root project directory
- Type: "Windows"
- ResourceFile: Path to .resx file (e.g., Properties\Resources.resx)
- ResourceFolder: Folder for icon files (e.g., Resources\Icons)
- SaveIconsTo: "Folder", "File", or "Both"
- ProjectFile: Path to .csproj file
- UpdateProjectFile: 1 (auto-update) or 0 (manual)
Creating a Web Project:
Fields:
- Name: Project display name
- Path: Root project directory
- Type: "Web"
- ResourceFolder: Web resource path (e.g., wwwroot/images/icons)
- ProjectFile: Path to .csproj file
- UpdateProjectFile: 1 (auto-update) or 0 (manual)
Export Behavior:
For Windows Projects:
- Icon file is copied to
Path + ResourceFolder - If
UpdateProjectFile = 1, the .csproj is updated with a<Content Include="...">entry - If
ResourceFileis defined, the icon is added to the .resx file as an embedded resource - Export is recorded in ProjectIcons table
For Web Projects:
- Icon file is copied to
Path + ResourceFolder - If
UpdateProjectFile = 1, the .csproj is updated with a<Content Include="...">entry - Export is recorded in ProjectIcons table
The merge feature allows you to combine two icons (e.g., adding a badge overlay).
Merge Process:
- Select a base icon (BigIcon)
- Select an overlay icon (SmallIcon)
- Choose overlay position:
- TopLeft, Top, TopRight
- Left, Center, Right
- BottomLeft, Bottom, BottomRight
- Preview the merged result
- Save the merged icon to the database
- The merge recipe is recorded in MergeRecipes table
Position Calculation:
TopLeft: (0, 0)
Top: (centerX - smallWidth/2, 0)
TopRight: (bigWidth - smallWidth, 0)
Left: (0, centerY - smallHeight/2)
Center: (centerX - smallWidth/2, centerY - smallHeight/2)
Right: (bigWidth - smallWidth, centerY - smallHeight/2)
BottomLeft: (0, bigHeight - smallHeight)
Bottom: (centerX - smallWidth/2, bigHeight - smallHeight)
BottomRight: (bigWidth - smallWidth, bigHeight - smallHeight)
Search Capabilities (Planned Feature):
- Text search by icon name
- Filter by tags (multi-select)
- Filter by collection
- Filter by vein
- Filter by file extension
- Filter by size range
- Combine multiple filters
Search Performance: The database includes optimized indexes for fast searching:
idx_IconTagsonIconTags(Tag)- Fast tag lookupidx_IconTags2onIconTags(Icon)- Fast icon lookupidx_IconTags3onIconTags(Icon, Tag)- Composite indexidx_IconFilesonIconFiles(Icon)- Fast file lookup
Location: IconCommander\App.config
<configuration>
<appSettings>
<!-- UI theme selection -->
<add key="SelectedTheme" value="Default" />
<!-- Database file path -->
<add key="DatabaseFile" value="C:\Path\To\Icons.db" />
</appSettings>
<connectionStrings>
<add name="IconDatabase"
connectionString="Data Source=Icons.db;Version=3;DefaultTimeout=5000;SyncMode=Off;JournalMode=Memory" />
</connectionStrings>
</configuration>| Parameter | Value | Purpose |
|---|---|---|
Data Source |
Path to .db file | SQLite database location |
Version |
3 | SQLite version |
DefaultTimeout |
5000 | Command timeout in milliseconds |
SyncMode |
Off | Skip fsync for faster writes (less durability) |
JournalMode |
Memory | Keep journal in RAM (faster, less safe) |
ReadOnly |
false | Allow write operations |
FailIfMissing |
false | Create database if it doesn't exist |
For Import Operations:
SyncMode=Off: Disables fsync calls (2-3x faster writes)JournalMode=Memory: Keeps rollback journal in RAM- Trade-off: Lower durability in case of power failure
For Production Use:
SyncMode=Normal: Standard durabilityJournalMode=Delete: Standard journaling- Trade-off: Slower writes, safer data
Available themes (from ZidThemes):
- Default
- Blue
- Dark
- Light
- Green
- Purple
Change theme via:
Menu: View → Theme → [Select Theme]
Theme preference is saved to App.config and persists across sessions.
Stores development project configurations.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Name | TEXT | NOT NULL | Project display name |
| Path | TEXT | NOT NULL | Root project directory |
| Type | TEXT | DEFAULT 'Web' | "Web" or "Windows" |
| ResourceFile | TEXT | Path to .resx file (Windows only) | |
| ResourceFolder | TEXT | Resource folder within project | |
| SaveIconsTo | TEXT | DEFAULT 'Folder' | "Folder", "File", or "Both" |
| ProjectFile | TEXT | Path to .csproj file | |
| UpdateProjectFile | INTEGER | DEFAULT 1 | Auto-update project file flag (1=yes, 0=no) |
Top-level groupings for icon sets.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Name | TEXT | NOT NULL | Collection name |
| Description | TEXT | Long description | |
| Comments | TEXT | Internal notes | |
| License | INTEGER | FOREIGN KEY → Licenses(Id) | License reference |
Folder-based icon sources within collections.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Collection | INTEGER | NOT NULL, FOREIGN KEY → Collections(Id) | Parent collection |
| Name | TEXT | NOT NULL | Vein name |
| Description | TEXT | Description | |
| Path | TEXT | NOT NULL | Folder path on disk |
| IsIcon | INTEGER | DEFAULT 0 | Contains .ico files |
| IsImage | INTEGER | DEFAULT 1 | Contains image files |
| IsSvg | INTEGER | DEFAULT 0 | Contains .svg files |
Logical icon entities (may have multiple files/sizes).
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Name | TEXT | NOT NULL | Icon name (typically filename) |
| Sizes | TEXT | Available sizes (comma-separated) | |
| Ico | INTEGER | Has .ico format | |
| IcoLegacy | INTEGER | Has legacy .ico format | |
| Svg | INTEGER | Has .svg format | |
| Vein | INTEGER | NOT NULL, FOREIGN KEY → Veins(Id) | Parent vein |
Individual icon files with binary data.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Icon | INTEGER | NOT NULL, FOREIGN KEY → Icons(Id) | Parent icon |
| FileName | TEXT | NOT NULL | File name without extension |
| Extension | TEXT | NOT NULL | File extension with dot |
| Type | TEXT | DEFAULT 'Image' | "Icon", "SVG", "Image", or "*" |
| Size | INTEGER | NOT NULL | Width × Height |
| OriginalPath | TEXT | Source file path | |
| BinData | BLOB | Binary file content | |
| IsMerged | INTEGER | DEFAULT 0 | Created via merge |
| Hash | TEXT | NOT NULL | SHA256 for deduplication |
Index: idx_IconFiles ON IconFiles(Icon)
Searchable tags for icons.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Icon | INTEGER | NOT NULL, FOREIGN KEY → Icons(Id) | Icon reference |
| Tag | TEXT | NOT NULL | Tag text (lowercase recommended) |
Indexes:
idx_IconTagsONIconTags(Tag)- Fast tag lookupidx_IconTags2ONIconTags(Icon)- Fast icon lookupidx_IconTags3ONIconTags(Icon, Tag)- Composite index
Temporary working space for selected icons.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| IconFile | INTEGER | NOT NULL, FOREIGN KEY → IconFiles(Id) | Icon file reference |
| CreationDate | TEXT | ISO8601 timestamp |
Track icon exports to projects.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Project | INTEGER | FOREIGN KEY → Projects(Id) | Target project |
| IconFile | INTEGER | NOT NULL, FOREIGN KEY → IconFiles(Id) | Exported icon file |
| SentToProject | INTEGER | Export success flag (1=success, 0=failed) | |
| DateSent | TEXT | ISO8601 timestamp |
Record of merged icon configurations.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| BigIcon | INTEGER | NOT NULL, FOREIGN KEY → IconFiles(Id) | Base icon file |
| SmallIcon | INTEGER | NOT NULL, FOREIGN KEY → IconFiles(Id) | Overlay icon file |
| SmallIconPosition | TEXT | NOT NULL | Position: "Top", "TopRight", "Right", "BottomRight", "Bottom", "BottomLeft", "Left", "TopLeft", "Center" |
| IconResult | INTEGER | FOREIGN KEY → IconFiles(Id) | Resulting merged icon file |
License definitions for icon collections.
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| Name | TEXT | NOT NULL | License name (e.g., "MIT", "CC BY 4.0") |
| Description | TEXT | NOT NULL | Full license text |
Track licenses for merged icons (may inherit multiple).
| Column | Type | Constraints | Description |
|---|---|---|---|
| Id | INTEGER | PRIMARY KEY | Unique identifier |
| IconFile | INTEGER | NOT NULL, FOREIGN KEY → IconFiles(Id) | Merged icon file |
| License | INTEGER | NOT NULL, FOREIGN KEY → Licenses(Id) | License reference |
These tables are used during bulk import operations and cleared afterward:
Temporary keyword storage during JSON import.
| Column | Type | Description |
|---|---|---|
| Id | INTEGER NOT NULL | Keyword ID from JSON |
| Keyword | TEXT NOT NULL | Keyword text |
Temporary keyword-icon relationships during import.
| Column | Type | Description |
|---|---|---|
| IconBuffer | INTEGER NOT NULL | Icon buffer ID |
| KeywordBuffer | INTEGER NOT NULL | Keyword buffer ID |
All database operations return SqliteResponse<T> objects:
public class SqliteResponse<T>
{
public T Result { get; set; } // Single result
public List<T> Results { get; set; } // Multiple results
public List<ErrorOnResponse> Errors { get; set; }
public bool IsOK => Errors == null || Errors.Count == 0;
public bool IsFailure => !IsOK;
public DateTime ExecutionTime { get; set; }
public int RowsAffected { get; set; }
public TimeSpan ExecutionLapse { get; set; }
}Usage Example:
var response = connector.ExecuteTable("SELECT * FROM Icons WHERE Vein = @vein",
new SQLiteParameter("@vein", veinId));
if (response.IsOK)
{
DataTable icons = response.Result;
// Process data
Console.WriteLine($"Loaded {response.RowsRead} icons in {response.ExecutionLapse.TotalMilliseconds}ms");
}
else
{
foreach (var error in response.Errors)
MessageBox.Show($"Error: {error.Message}");
}Dynamic form generation for CRUD operations:
// Get table schema
var schemaResponse = connector.ExecuteTable("SELECT * FROM Collections LIMIT 0");
DataTable schema = schemaResponse.Result;
// Configure form generator
UIGenerator formGen = new UIGenerator(schema, "Collections");
formGen.SetPrimaryKey("Id");
formGen.SetExclusion("Id"); // Hide from form
formGen.SetRequired("Name");
formGen.SetFieldType("Description", FieldType.MultilineText);
formGen.SetForeignKey("License", licensesTable);
// Show insert dialog
if (formGen.ShowInsertDialog() == DialogResult.OK)
{
string sql = formGen.GetInsertScript();
var result = connector.ExecuteNonQuery(sql);
if (result.IsOK)
LoadCollections(); // Refresh grid
}
// Show update dialog
if (formGen.ShowUpdateDialog(selectedRow) == DialogResult.OK)
{
string sql = formGen.GetUpdateScript();
connector.ExecuteNonQuery(sql);
}Consistent theming across all forms:
// In Form Designer
private ThemeManager themeManager1;
// In Form_Load
private void Form_Load(object sender, EventArgs e)
{
// Load saved theme
string themeName = ConfigurationManager.AppSettings["SelectedTheme"];
themeManager1.Theme = (ZidThemes)Enum.Parse(typeof(ZidThemes), themeName);
themeManager1.ApplyTheme();
}
// Save theme preference
private void SaveTheme(ZidThemes selectedTheme)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
config.AppSettings.Settings["SelectedTheme"].Value = selectedTheme.ToString();
config.Save();
ConfigurationManager.RefreshSection("appSettings");
}Asynchronous operations with progress reporting:
// Configure BackgroundWorker
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.DoWork += bgWorker_DoWork;
bgWorker.ProgressChanged += bgWorker_ProgressChanged;
bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
// DoWork handler (runs on background thread)
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < items.Count; i++)
{
// Check for cancellation
if (bgWorker.CancellationPending)
{
e.Cancel = true;
return;
}
ProcessItem(items[i]);
// Report progress
int percentComplete = (i + 1) * 100 / items.Count;
bgWorker.ReportProgress(percentComplete, $"Processed {i + 1} of {items.Count}");
}
}
// ProgressChanged handler (runs on UI thread)
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
statusLabel.Text = e.UserState.ToString();
}
// RunWorkerCompleted handler (runs on UI thread)
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
MessageBox.Show("Operation cancelled");
else if (e.Error != null)
MessageBox.Show($"Error: {e.Error.Message}");
else
MessageBox.Show("Operation completed successfully");
}SQL injection prevention and BLOB handling:
// Simple parameters
string sql = "SELECT * FROM Icons WHERE Vein = @vein AND Name LIKE @name";
var parameters = new[]
{
new SQLiteParameter("@vein", veinId),
new SQLiteParameter("@name", $"%{searchTerm}%")
};
var response = connector.ExecuteTable(sql, parameters);
// Binary data parameters
string insertSql = @"INSERT INTO IconFiles
(Icon, FileName, Extension, BinData, Hash)
VALUES (@icon, @fileName, @extension, @binData, @hash)";
var insertParams = new[]
{
new SQLiteParameter("@icon", iconId),
new SQLiteParameter("@fileName", fileName),
new SQLiteParameter("@extension", extension),
new SQLiteParameter("@binData", DbType.Binary) { Value = binData },
new SQLiteParameter("@hash", hash)
};
connector.ExecuteNonQuery(insertSql, insertParams);MainForm (Application Root)
├── SqliteConnector (Database Layer)
│ ├── SqliteResponse<T> (Response Pattern)
│ ├── Transaction Management
│ └── Logging Infrastructure
│
├── ThemeManager (UI Theming)
│ └── ZidThemes Engine
│
├── Child Forms
│ ├── CollectionsForm (CRUD)
│ │ ├── ZidGrid (Data Display)
│ │ └── UIGenerator (Dynamic Forms)
│ │
│ ├── VeinsForm (CRUD)
│ │ ├── ZidGrid
│ │ └── UIGenerator
│ │
│ └── VeinImport (Bulk Import)
│ ├── BackgroundWorker
│ ├── AddressBar (Navigation)
│ ├── TreeView (Folder Structure)
│ └── Progress Reporting
│
└── Models
├── Project
├── Mapping
├── IconexMapper
└── ComboboxItem
Prerequisites:
- Visual Studio 2019 or later
- .NET Framework 4.8 SDK
- NuGet Package Manager
Build Commands:
# Restore packages
nuget restore IconCommander.sln
# Build Debug
msbuild IconCommander.sln /p:Configuration=Debug /v:minimal
# Build Release
msbuild IconCommander.sln /p:Configuration=Release /v:minimal
# Clean
msbuild IconCommander.sln /t:Clean /p:Configuration=DebugIconCommander/
├── Program.cs # Application entry point (STAThread)
├── MainForm.cs # Main window — search, pagination, buffer, export
├── ComboboxItem.cs # Display/value helper for ComboBox data-binding
├── ExportManager.cs # Icon export engine (file, .resx, .csproj)
│
├── Controls/
│ └── IconDisplayControl.cs # Custom UserControl for rendering + interacting with icons
│
├── DataAccess/
│ ├── IIconCommanderDb.cs # Database abstraction interface
│ ├── SqliteConnector.cs # SQLite implementation
│ ├── SqlConnector.cs # SQL Server implementation
│ ├── SqlResponse.cs # Generic response wrapper
│ ├── ErrorOnResponse.cs # Error detail record
│ └── Delegates.cs # ProcessingQuery delegate
│
├── Forms/
│ ├── CollectionsForm.cs # Collections CRUD
│ ├── VeinsForm.cs # Veins CRUD
│ ├── VeinImport.cs # Bulk vein import wizard (BackgroundWorker)
│ ├── IconImport.cs # Individual icon import dialog
│ ├── IconsForm.cs # Icons browse / management
│ ├── IconBufferForm.cs # Buffer zone management
│ ├── TagEditForm.cs # Tag add/remove dialog (token-select)
│ ├── MergeForm.cs # Icon compositing dialog (9-position overlay)
│ └── CleanupUtilityForm.cs # Database cleanup / orphan removal
│
├── Models/
│ ├── Project.cs # Project POCO
│ ├── Mapping.cs # Keyword→icon mapping element
│ └── IconexMapper.cs # Iconex JSON root object
│
├── Properties/
│ ├── AssemblyInfo.cs
│ ├── Resources.resx
│ └── Settings.settings
│
└── Resources/
└── [UI Icons: Bookmark.png, Search.png, etc.]
-
Create the form in Visual Studio Designer
- Right-click
Formsfolder → Add → Form (Windows Forms) - Design UI in Visual Studio Designer
- Right-click
-
Add ThemeManager component
- Drag
ThemeManagerfrom Toolbox to form - Set name to
themeManager1
- Drag
-
Apply theme in Form_Load
private void MyForm_Load(object sender, EventArgs e) { string themeName = ConfigurationManager.AppSettings["SelectedTheme"]; themeManager1.Theme = (ZidThemes)Enum.Parse(typeof(ZidThemes), themeName); themeManager1.ApplyTheme(); }
-
Pass SqliteConnector from MainForm
public class MyForm : Form { private SqliteConnector connector; public MyForm(SqliteConnector connector) { InitializeComponent(); this.connector = connector; } } // In MainForm MyForm form = new MyForm(this.connector); form.ShowDialog();
Add custom methods to SqliteConnector.Custom.cs:
public partial class SqliteConnector
{
public SqliteResponse<int> MyTable_Insert(string field1, string field2)
{
string sql = "INSERT INTO MyTable (Field1, Field2) VALUES (@field1, @field2)";
var parameters = new[]
{
new SQLiteParameter("@field1", field1),
new SQLiteParameter("@field2", field2)
};
return ExecuteNonQuery(sql, parameters);
}
public SqliteResponse<DataTable> MyTable_GetAll()
{
string sql = "SELECT * FROM MyTable ORDER BY Field1";
return ExecuteTable(sql);
}
public SqliteResponse<int> MyTable_Update(int id, string field1, string field2)
{
string sql = @"UPDATE MyTable
SET Field1 = @field1, Field2 = @field2
WHERE Id = @id";
var parameters = new[]
{
new SQLiteParameter("@id", id),
new SQLiteParameter("@field1", field1),
new SQLiteParameter("@field2", field2)
};
return ExecuteNonQuery(sql, parameters);
}
public SqliteResponse<int> MyTable_Delete(int id)
{
string sql = "DELETE FROM MyTable WHERE Id = @id";
return ExecuteNonQuery(sql, new SQLiteParameter("@id", id));
}
}Save binary file to database:
// Read file
byte[] binData = File.ReadAllBytes(filePath);
string hash = Crypter.ToSHA256(filePath);
// Check for duplicates
var existingId = connector.GetIconFileId(hash);
if (existingId.IsOK && existingId.Result != null)
{
Console.WriteLine($"File already exists with ID {existingId.Result}");
return;
}
// Save new file
string sql = @"INSERT INTO IconFiles
(Icon, FileName, Extension, BinData, Hash, Size)
VALUES (@icon, @fileName, @extension, @binData, @hash, @size)";
var parameters = new[]
{
new SQLiteParameter("@icon", iconId),
new SQLiteParameter("@fileName", Path.GetFileNameWithoutExtension(filePath)),
new SQLiteParameter("@extension", Path.GetExtension(filePath)),
new SQLiteParameter("@binData", DbType.Binary) { Value = binData },
new SQLiteParameter("@hash", hash),
new SQLiteParameter("@size", binData.Length)
};
var response = connector.ExecuteNonQuery(sql, parameters);Load binary file from database:
var response = connector.ExecuteTable(
"SELECT BinData, Extension FROM IconFiles WHERE Id = @id",
new SQLiteParameter("@id", iconFileId));
if (response.IsOK && response.Result.Rows.Count > 0)
{
DataRow row = response.Result.Rows[0];
byte[] binData = (byte[])row["BinData"];
string extension = row["Extension"].ToString();
// Display in PictureBox
using (MemoryStream ms = new MemoryStream(binData))
{
Image img = Image.FromStream(ms);
pictureBox1.Image = img;
}
// Or save to file
File.WriteAllBytes($"exported_icon{extension}", binData);
}Test with sample folder structure:
D:\TestIcons\
├── MaterialDesign\
│ ├── account.png
│ ├── settings.png
│ └── home.svg
└── FontAwesome\
├── fa-user.png
├── fa-gear.png
└── fa-house.png
Steps:
- Create a Collection: "Test Collection"
- Create a Vein: "MaterialDesign" → Path:
D:\TestIcons\MaterialDesign - Import Vein → Select vein → Import
- Verify in database:
SELECT i.Name, COUNT(if.Id) as FileCount, GROUP_CONCAT(t.Tag) as Tags FROM Icons i LEFT JOIN IconFiles if ON i.Id = if.Icon LEFT JOIN IconTags t ON i.Id = t.Icon WHERE i.Vein = (SELECT Id FROM Veins WHERE Name = 'MaterialDesign') GROUP BY i.Id
Problem: "Database is locked" error during operations
Causes:
- Another process has the database open
- Transaction not committed
- Background operation in progress
Solutions:
// Increase timeout in connection string
SQLiteConnectionStringBuilder csb = new SQLiteConnectionStringBuilder
{
DefaultTimeout = 10000, // Increase to 10 seconds
// ...
};
// Ensure transactions are committed
try
{
BeginTransaction();
// Operations
CommitTransaction();
}
catch
{
RollbackTransaction();
throw;
}Problem: Import hangs or crashes with many files
Causes:
- Memory exhaustion from loading all files
- No progress reporting for large batches
Solutions:
- Process files in batches of 1000
- Report progress more frequently
- Use streaming for large files:
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
// Process buffer
}Problem: SVG files show Size = 0
Causes:
- Missing width/height attributes
- Viewbox-only SVG files
Solutions:
XDocument xdoc = XDocument.Load(filePath);
XElement svgElement = xdoc.Root;
// Try width/height attributes
var widthAttr = svgElement.Attribute("width")?.Value.Replace("px", "");
var heightAttr = svgElement.Attribute("height")?.Value.Replace("px", "");
if (string.IsNullOrEmpty(widthAttr) || string.IsNullOrEmpty(heightAttr))
{
// Fall back to viewBox
var viewBox = svgElement.Attribute("viewBox")?.Value;
if (!string.IsNullOrEmpty(viewBox))
{
var parts = viewBox.Split(' ');
if (parts.Length == 4)
{
widthAttr = parts[2];
heightAttr = parts[3];
}
}
}
int width = int.Parse(widthAttr ?? "0");
int height = int.Parse(heightAttr ?? "0");Problem: Form shows default theme instead of saved theme
Causes:
- Theme not applied in Form_Load
- ThemeManager component missing
- Invalid theme name in config
Solutions:
// Verify theme name is valid
private void LoadTheme()
{
try
{
string themeName = ConfigurationManager.AppSettings["SelectedTheme"];
if (Enum.TryParse(themeName, out ZidThemes theme))
{
themeManager1.Theme = theme;
themeManager1.ApplyTheme();
}
else
{
// Fall back to default
themeManager1.Theme = ZidThemes.Default;
themeManager1.ApplyTheme();
}
}
catch
{
themeManager1.Theme = ZidThemes.Default;
themeManager1.ApplyTheme();
}
}Problem: JSON tags file doesn't populate tags
Causes:
- Invalid JSON format
- Incorrect mapping structure
- Temporary tables not cleared
Solutions:
// Validate JSON structure
try
{
string json = File.ReadAllText(jsonPath);
IconexMapper mapper = JsonConvert.DeserializeObject<IconexMapper>(json);
if (mapper.Mappings == null || mapper.Mappings.Count == 0)
throw new Exception("No mappings found in JSON");
// Clear temp tables before import
connector.ExecuteNonQuery("DELETE FROM KeywordBuffer");
connector.ExecuteNonQuery("DELETE FROM IconBuffer");
connector.ExecuteNonQuery("DELETE FROM IconKeywordBuffer");
// Process mappings
// ...
}
catch (JsonException ex)
{
MessageBox.Show($"Invalid JSON format: {ex.Message}");
}Problem: Queries take too long with large datasets
Solutions:
-- Add indexes for frequently queried columns
CREATE INDEX IF NOT EXISTS idx_IconTags ON IconTags(Tag);
CREATE INDEX IF NOT EXISTS idx_IconTags2 ON IconTags(Icon);
CREATE INDEX IF NOT EXISTS idx_IconTags3 ON IconTags(Icon, Tag);
CREATE INDEX IF NOT EXISTS idx_IconFiles ON IconFiles(Icon);
-- Use EXPLAIN QUERY PLAN to analyze queries
EXPLAIN QUERY PLAN
SELECT i.* FROM Icons i
JOIN IconTags t ON i.Id = t.Icon
WHERE t.Tag = 'account';Problem: Database file grows very large
Solutions:
-- Run VACUUM to reclaim space
VACUUM;
-- Enable auto-vacuum in connection string
JournalMode=WAL;AutoVacuum=Full;Problem: Out of memory exceptions during bulk import
Solutions:
// Process in smaller batches
const int BATCH_SIZE = 100;
for (int i = 0; i < files.Length; i++)
{
ProcessFile(files[i]);
if (i % BATCH_SIZE == 0)
{
GC.Collect(); // Force garbage collection
GC.WaitForPendingFinalizers();
}
}
// Dispose images after processing
Image img = Image.FromFile(filePath);
try
{
int size = img.Width * img.Height;
// Use size
}
finally
{
img.Dispose();
}We welcome contributions to IconCommander! Please follow these guidelines:
-
Fork the Repository
git clone https://github.com/yourusername/IconCommander.git cd IconCommander git checkout -b feature/my-new-feature -
Follow Coding Standards
- Use C# naming conventions (PascalCase for methods, camelCase for fields)
- Add XML documentation comments for public methods
- Follow existing patterns (Response pattern, UIGenerator, etc.)
- Place controls in Designer, not at runtime
-
Write Tests
- Add unit tests for new database methods
- Test with various file formats and sizes
- Verify theme application on new forms
-
Update Documentation
- Update CLAUDE.md with new features
- Add examples to README.md
- Document database schema changes
-
Submit Pull Request
- Describe what your changes do
- Reference related issues
- Include screenshots for UI changes
High Priority:
- Icon export engine (Windows/Web projects)
- Icon merge drawing functionality
- Search/filter interface
- Icon buffer browsing UI
Medium Priority:
- License management UI
- Project file XML manipulation
- Recent projects functionality
- Batch icon operations
Low Priority:
- Icon format conversion
- Advanced search operators
- Plugin system
- REST API for external tools
Before submitting a pull request, ensure:
- All new forms have ThemeManager component
- Database operations use parameterized queries
- Long operations use BackgroundWorker
- Response pattern used for all database methods
- No SQL injection vulnerabilities
- Binary data uses DbType.Binary parameter
- Transactions committed/rolled back properly
- Progress reporting for operations >1 second
- Cancellation support for long operations
- CLAUDE.md updated with new features
This project is licensed under the MIT License. See LICENSE file for details.
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/yourusername/IconCommander/issues
- Email: support@iconcommander.com
- Documentation: See CLAUDE.md for detailed technical documentation
- ZidUtilities: UI component library providing ThemeManager, UIGenerator, ZidGrid, and AddressBar
- SQLite: High-performance embedded database
- Newtonsoft.Json: JSON serialization/deserialization
- ICSharpCode.TextEditor: Syntax-highlighting text editor
- Initial release
- Collections and Veins CRUD
- Bulk import with JSON keyword mapping
- Theme management
- Project configuration
- Database-driven icon storage with SHA256 deduplication
- Icon export to Windows/Web projects
- Icon merge functionality
- Search and filter UI
- Icon buffer management
- License tracking
Built with ❤️ for developers who value organized icon libraries