diff --git a/.claude/settings.local.json b/.claude/settings.local.json
deleted file mode 100644
index abf4b57..0000000
--- a/.claude/settings.local.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "permissions": {
- "allow": [
- "Bash(dir \"C:\\\\Users\\\\logik\\\\source\\\\repos\\\\ControlMyMonitorManagement\\\\Language\")",
- "Bash(find:*)",
- "Bash(dir \"C:\\\\Users\\\\logik\\\\source\\\\repos\\\\ControlMyMonitorManagement\\\\Language\" /S)",
- "Bash(cat:*)",
- "Bash(dotnet build:*)",
- "Bash(dotnet --list-sdks:*)",
- "Bash(dotnet:*)",
- "Bash(timeout /t 5 /nobreak)",
- "Bash(taskkill:*)",
- "Bash(start \"\" \"C:\\\\Users\\\\logik\\\\source\\\\repos\\\\ControlMyMonitorManagement\\\\DellMonitorControl\\\\bin\\\\Debug\\\\net9.0-windows\\\\DellMonitorControl.exe\")",
- "Bash(ping:*)",
- "Bash(git remote:*)"
- ]
- }
-}
diff --git a/.gitignore b/.gitignore
index 9491a2f..8f66922 100644
--- a/.gitignore
+++ b/.gitignore
@@ -360,4 +360,10 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
-FodyWeavers.xsd
\ No newline at end of file
+FodyWeavers.xsd
+
+# Claude Code
+.claude/
+
+# Windows special files
+nul
\ No newline at end of file
diff --git a/DellMonitorControl/App.xaml.cs b/DellMonitorControl/App.xaml.cs
index b5cad1b..7fcae20 100644
--- a/DellMonitorControl/App.xaml.cs
+++ b/DellMonitorControl/App.xaml.cs
@@ -14,19 +14,20 @@ namespace DellMonitorControl
_trayIcon.TrayLeftMouseUp += TrayIcon_Click;
_mainWindow = new MainWindow();
- _mainWindow.Show();
}
- private void TrayIcon_Click(object sender, RoutedEventArgs e)
+ private async void TrayIcon_Click(object sender, RoutedEventArgs e)
{
if (_mainWindow == null) return;
if (_mainWindow.IsVisible)
+ {
_mainWindow.Hide();
+ }
else
{
- _mainWindow.Show();
- _mainWindow.Activate();
+ _mainWindow.ShowNearTray();
+ await _mainWindow.LoadMonitors();
}
}
diff --git a/DellMonitorControl/MainWindow.xaml b/DellMonitorControl/MainWindow.xaml
index ceda82f..7e69143 100644
--- a/DellMonitorControl/MainWindow.xaml
+++ b/DellMonitorControl/MainWindow.xaml
@@ -1,15 +1,40 @@
+ Title="Monitor Control"
+ Width="300" MinHeight="200" MaxHeight="500"
+ SizeToContent="Height"
+ WindowStyle="None"
+ AllowsTransparency="True"
+ Background="Transparent"
+ ResizeMode="NoResize"
+ ShowInTaskbar="False"
+ Topmost="True"
+ Deactivated="Window_Deactivated">
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DellMonitorControl/MainWindow.xaml.cs b/DellMonitorControl/MainWindow.xaml.cs
index 44ff5b4..91d87f5 100644
--- a/DellMonitorControl/MainWindow.xaml.cs
+++ b/DellMonitorControl/MainWindow.xaml.cs
@@ -15,70 +15,67 @@ public partial class MainWindow : Window
public MainWindow()
{
InitializeComponent();
- PositionWindowNearTray();
- Loaded += async (s, e) => await LoadMonitors();
}
- private void PositionWindowNearTray()
+ public void ShowNearTray()
{
var workArea = SystemParameters.WorkArea;
Left = workArea.Right - Width - 10;
- Top = workArea.Bottom - Height - 10;
+ // Use estimated height since ActualHeight is 0 before render
+ Top = workArea.Bottom - 350;
+ Show();
+ Activate();
+
+ // Reposition after layout is complete
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ Top = workArea.Bottom - ActualHeight - 10;
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
}
- private static readonly string LogFile = System.IO.Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "MonitorControl.log");
-
- private static void Log(string msg)
+ private void Window_Deactivated(object sender, EventArgs e)
{
- try { System.IO.File.AppendAllText(LogFile, $"{DateTime.Now:HH:mm:ss.fff} {msg}\n"); }
- catch { }
+ Hide();
}
- private async Task LoadMonitors()
+ public async Task LoadMonitors()
{
var newChildren = new List();
- Log("LoadMonitors started");
try
{
- Log("Calling ScanMonitor...");
await CMMCommand.ScanMonitor();
- Log("ScanMonitor complete");
-
- Log("Calling ReadMonitorsData...");
var monitors = (await CMMCommand.ReadMonitorsData()).ToList();
- Log($"ReadMonitorsData complete, found {monitors.Count} monitors");
if (!monitors.Any())
{
- Log("No monitors found");
- newChildren.Add(new TextBlock { Text = "No DDC/CI monitors detected", Foreground = Brushes.White, FontSize = 14 });
+ newChildren.Add(new TextBlock
+ {
+ Text = "No DDC/CI monitors detected",
+ Foreground = Brushes.LightGray,
+ FontSize = 12,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ Margin = new Thickness(0, 30, 0, 30)
+ });
}
else
{
foreach (var m in monitors)
{
- Log($"Processing monitor: {m.MonitorName} ({m.SerialNumber})");
-
var brightness = await CMMCommand.GetBrightness(m.SerialNumber) ?? 50;
- Log($" Brightness: {brightness}");
var contrast = await CMMCommand.GetContrast(m.SerialNumber) ?? 50;
- Log($" Contrast: {contrast}");
var inputSource = await CMMCommand.GetInputSource(m.SerialNumber);
- Log($" InputSource: {inputSource}");
var inputOptions = await CMMCommand.GetInputSourceOptions(m.SerialNumber);
- Log($" InputOptions count: {inputOptions.Count}");
var powerStatus = await CMMCommand.GetMonPowerStatus(m.SerialNumber) ?? "Unknown";
- Log($" PowerStatus: {powerStatus}");
+ // Monitor name header
newChildren.Add(new TextBlock
{
Text = m.MonitorName,
Foreground = Brushes.White,
- FontSize = 14,
- FontWeight = FontWeights.Bold,
- Margin = new Thickness(0, 10, 0, 5)
+ FontSize = 13,
+ FontWeight = FontWeights.SemiBold,
+ Margin = new Thickness(0, 8, 0, 6)
});
newChildren.Add(CreateSliderRow("Brightness", brightness, m.SerialNumber, CMMCommand.SetBrightness));
@@ -88,22 +85,33 @@ public partial class MainWindow : Window
newChildren.Add(CreateInputRow(inputSource, inputOptions, m.SerialNumber));
newChildren.Add(CreatePowerRow(powerStatus, m.SerialNumber));
+
+ // Separator
+ newChildren.Add(new Border { Height = 1, Background = new SolidColorBrush(Color.FromRgb(80, 80, 80)), Margin = new Thickness(0, 10, 0, 2) });
}
+
+ // Remove last separator
+ if (newChildren.Count > 0 && newChildren.Last() is Border)
+ newChildren.RemoveAt(newChildren.Count - 1);
}
- Log($"Built {newChildren.Count} UI elements");
}
catch (Exception ex)
{
- Log($"ERROR: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}");
newChildren.Clear();
- newChildren.Add(new TextBlock { Text = $"Error: {ex.Message}", Foreground = Brushes.Red, FontSize = 12, TextWrapping = TextWrapping.Wrap });
+ newChildren.Add(new TextBlock { Text = $"Error: {ex.Message}", Foreground = Brushes.OrangeRed, FontSize = 11, TextWrapping = TextWrapping.Wrap });
}
- Log("Updating UI...");
+ sp.VerticalAlignment = VerticalAlignment.Top;
sp.Children.Clear();
foreach (var child in newChildren)
sp.Children.Add(child);
- Log($"UI updated with {sp.Children.Count} children");
+
+ // Reposition after content changes
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ var workArea = SystemParameters.WorkArea;
+ Top = workArea.Bottom - ActualHeight - 10;
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
}
private StackPanel CreateSliderRow(string label, int value, string serialNumber, Func setCommand)
diff --git a/Library/Method/CMMCommand.cs b/Library/Method/CMMCommand.cs
index 2c94ac3..7ebcdee 100644
--- a/Library/Method/CMMCommand.cs
+++ b/Library/Method/CMMCommand.cs
@@ -220,9 +220,6 @@ public static class CMMCommand
}
}
- private static readonly string DebugLog = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "MonitorParse.log");
-
///
/// 取得螢幕清單
///
@@ -232,27 +229,19 @@ public static class CMMCommand
if (!File.Exists(CMMsMonitors)) return monitors;
- // Try reading raw bytes to understand encoding
+ // Read with UTF-16 LE encoding (ControlMyMonitor outputs UTF-16)
var rawBytes = await File.ReadAllBytesAsync(CMMsMonitors);
- File.WriteAllText(DebugLog, $"File size: {rawBytes.Length} bytes\nFirst 20 bytes: {BitConverter.ToString(rawBytes.Take(20).ToArray())}\n\n");
-
- // Try UTF-16 LE (common Windows Unicode)
var content = System.Text.Encoding.Unicode.GetString(rawBytes);
- File.AppendAllText(DebugLog, $"Content preview:\n{content.Substring(0, Math.Min(500, content.Length))}\n\n");
XMonitor? mon = null;
foreach (var line in content.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
- File.AppendAllText(DebugLog, $"LINE: [{line}]\n");
-
var colonIdx = line.IndexOf(':');
if (colonIdx < 0) continue;
var key = line.Substring(0, colonIdx).Trim();
var val = line.Substring(colonIdx + 1).Trim().Trim('"');
- File.AppendAllText(DebugLog, $" KEY=[{key}] VAL=[{val}]\n");
-
if (key.Contains("Monitor Device Name"))
{
mon = new XMonitor { MonitorDeviceName = val };
@@ -272,14 +261,12 @@ public static class CMMCommand
else if (mon != null && key.Contains("Monitor ID"))
{
mon.MonitorID = val;
- File.AppendAllText(DebugLog, $" -> Adding monitor: Name=[{mon.MonitorName}] SN=[{mon.SerialNumber}]\n");
if (!string.IsNullOrEmpty(mon.SerialNumber))
monitors.Add(mon);
mon = null;
}
}
- File.AppendAllText(DebugLog, $"\nTotal monitors with serial: {monitors.Count}\n");
return monitors;
}
diff --git a/README.md b/README.md
index f9786a3..d64ad86 100644
--- a/README.md
+++ b/README.md
@@ -1,74 +1,82 @@
-# ControlMyMonitorManagement
+# Monitor Control Manager
-A Windows desktop application for controlling monitor settings via DDC/CI (Display Data Channel/Command Interface). Adjust brightness, contrast, power state, and other display parameters across multiple monitors.
+A lightweight Windows utility for controlling monitor settings via DDC/CI. Adjust brightness, contrast, input source, and power settings directly from your system tray.
+
+
+
+
## Features
-- **Multi-Monitor Support** - Detect and control multiple connected displays
-- **Brightness & Contrast Sliders** - Easy adjustment with real-time feedback
-- **Input Source Switching** - Switch between VGA, DVI, HDMI, DisplayPort (auto-detected from monitor)
-- **Power Management** - Turn monitors on, off, or into sleep mode
-- **System Tray Integration** - DellMonitorControl app with taskbar tray popup
-- **9 Languages** - Auto-detects system language (en, es, fr, de, zh, ja, pt, it, hi)
+- **System Tray Integration** - Clean popup interface accessible from taskbar
+- **Multi-Monitor Support** - Control all DDC/CI compatible displays
+- **Brightness & Contrast** - Slider controls with real-time feedback
+- **Input Source Switching** - Auto-detects available inputs (HDMI, DisplayPort, DVI, VGA)
+- **Power Management** - On, Sleep, and Off controls
+- **Multi-Language** - Auto-detects system language (English, Spanish, French, German, Chinese, Japanese, Portuguese, Italian, Hindi)
## Requirements
-- Windows OS
-- .NET 7.0 SDK
+- Windows 10/11
+- .NET 9.0 Runtime
- DDC/CI compatible monitor(s)
-## Build
+> **Note:** Most external monitors support DDC/CI, but some laptop internal displays and certain monitors may not support this protocol.
-```bash
-# Clone the repository
-git clone https://github.com/yourusername/ControlMyMonitorManagement.git
+## Installation
+
+### From Release
+Download the latest release and run the executable. The app will appear in your system tray.
+
+### Build from Source
+```powershell
+git clone https://git.marketally.com/misc/ControlMyMonitorManagement.git
cd ControlMyMonitorManagement
-
-# Build the solution
-dotnet build ControlMyMonitorManagement.sln
-
-# Run the main application
-dotnet run --project ControlMyMonitorManagement/ControlMyMonitorManagement.csproj
-
-# Run the Dell-specific application (with system tray)
-dotnet run --project DellMonitorControl/DellMonitorControl.csproj
+dotnet build
+dotnet run --project DellMonitorControl
```
+## Usage
+
+1. Launch the application - it minimizes to system tray
+2. Click the tray icon to open the control panel
+3. Adjust brightness/contrast with sliders
+4. Select input source from dropdown (if multiple available)
+5. Toggle power state with the power button
+6. Click outside the popup to close
+
## Project Structure
| Project | Description |
|---------|-------------|
-| **ControlMyMonitorManagement** | Main WPF application with full UI |
-| **DellMonitorControl** | Lightweight app with system tray integration |
-| **Library** | Core business logic and DDC/CI operations |
-| **CMMModel** | Data models for monitor status |
-| **CMMService** | Service layer interfaces |
-| **Language** | Localization support |
-| **Tester** | NUnit test project |
+| `DellMonitorControl` | System tray application with popup UI |
+| `ControlMyMonitorManagement` | Full WPF application (alternative) |
+| `Library` | Core DDC/CI operations via ControlMyMonitor.exe |
+| `Language` | Localization resources (9 languages) |
+| `CMMModel` | Data models |
+| `Tester` | Unit tests |
-## Common VCP Codes
+## Technical Details
-| Code | Name | Values |
-|------|------|--------|
-| D6 | Power Mode | 1=On, 4=Sleep, 5=Off |
-| 10 | Brightness | 0-100 |
-| 12 | Contrast | 0-100 |
-| 60 | Input Source | 1=VGA, 3=DVI, 15=DisplayPort, 17=HDMI |
+### DDC/CI Protocol
+The application uses DDC/CI (Display Data Channel/Command Interface) to communicate with monitors through the display cable. This allows software control of settings normally accessed via physical monitor buttons.
-## How It Works
-
-This application uses DDC/CI protocol to communicate with monitors through the display cable (HDMI, DisplayPort, DVI, VGA). DDC/CI allows software to send commands directly to the monitor's firmware to adjust settings that would normally require using the monitor's physical buttons.
+### VCP Codes Used
+| Code | Function | Values |
+|------|----------|--------|
+| `10` | Brightness | 0-100 |
+| `12` | Contrast | 0-100 |
+| `60` | Input Source | 1=VGA, 15=DP, 17=HDMI |
+| `D6` | Power Mode | 1=On, 4=Sleep, 5=Off |
## Credits
-- **Original Project**: [DangWang](https://github.com/poyingHAHA) - ControlMyMonitorManagement
-- **DDC/CI Tool**: [Nir Sofer](https://www.nirsoft.net) - ControlMyMonitor.exe (freeware)
-- **Enhancements by David H Friedel Jr**: Brightness/contrast sliders, input source switching, 9-language localization
+- **Original Project**: [rictirse](https://github.com/rictirse) - ControlMyMonitorManagement base
+- **DDC/CI Engine**: [Nir Sofer](https://www.nirsoft.net/utils/control_my_monitor.html) - ControlMyMonitor (freeware)
+- **Enhancements**: David H Friedel Jr - UI improvements, input switching, localization
## License
-This project contains multiple components with different terms:
+MIT License - See [LICENSE](LICENSE) for details.
-- **Original code by DangWang**: No explicit license provided
-- **ControlMyMonitor.exe**: Freeware by Nir Sofer - free distribution allowed, no commercial use, no modification
-- **New contributions (sliders, localization, etc.)**: MIT License
+Note: ControlMyMonitor.exe is freeware by Nir Sofer (free distribution, no modification).