using CMM.Library.Config; using CMM.Library.Method; using CMM.Library.ViewModel; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; namespace DellMonitorControl; public partial class MainWindow : Window { private List<(XMonitor Monitor, List Options)> _loadedMonitors = new(); private Storyboard? _spinnerStoryboard; public MainWindow() { InitializeComponent(); _spinnerStoryboard = (Storyboard)FindResource("SpinnerAnimation"); } public void ShowNearTray() { var workArea = SystemParameters.WorkArea; Left = workArea.Right - Width - 10; Top = workArea.Bottom - 350; Show(); Activate(); // Start spinner _spinnerStoryboard?.Begin(this, true); Dispatcher.BeginInvoke(new Action(() => { Top = workArea.Bottom - ActualHeight - 10; }), System.Windows.Threading.DispatcherPriority.Loaded); } private void Window_Deactivated(object sender, EventArgs e) { Hide(); _spinnerStoryboard?.Stop(this); } private void ExitButton_Click(object sender, RoutedEventArgs e) { Application.Current.Shutdown(); } public async Task LoadMonitors() { var newChildren = new List(); _loadedMonitors.Clear(); try { await CMMCommand.ScanMonitor(); var monitors = (await CMMCommand.ReadMonitorsData()).ToList(); if (!monitors.Any()) { 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) { var brightness = await CMMCommand.GetBrightness(m.SerialNumber) ?? 50; var contrast = await CMMCommand.GetContrast(m.SerialNumber) ?? 50; var inputSource = await CMMCommand.GetInputSource(m.SerialNumber); var inputOptions = await CMMCommand.GetInputSourceOptions(m.SerialNumber); var powerStatus = await CMMCommand.GetMonPowerStatus(m.SerialNumber) ?? "Unknown"; _loadedMonitors.Add((m, inputOptions)); // Apply config to filter hidden ports and use custom labels var filteredOptions = MonitorConfigManager.ApplyConfigToOptions(m.SerialNumber, inputOptions); // Monitor name header with Config button newChildren.Add(CreateMonitorHeader(m, inputOptions)); newChildren.Add(CreateSliderRow("Brightness", brightness, m.SerialNumber, CMMCommand.SetBrightness)); newChildren.Add(CreateSliderRow("Contrast", contrast, m.SerialNumber, CMMCommand.SetContrast)); if (filteredOptions.Count > 0) newChildren.Add(CreateInputRow(inputSource, filteredOptions, 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); } // Load quick-switch toolbar LoadQuickSwitchToolbar(); } catch (Exception ex) { newChildren.Clear(); newChildren.Add(new TextBlock { Text = $"Error: {ex.Message}", Foreground = Brushes.OrangeRed, FontSize = 11, TextWrapping = TextWrapping.Wrap }); } // Stop spinner _spinnerStoryboard?.Stop(this); sp.VerticalAlignment = VerticalAlignment.Top; sp.Children.Clear(); foreach (var child in newChildren) sp.Children.Add(child); Dispatcher.BeginInvoke(new Action(() => { var workArea = SystemParameters.WorkArea; Top = workArea.Bottom - ActualHeight - 10; }), System.Windows.Threading.DispatcherPriority.Loaded); } private void LoadQuickSwitchToolbar() { quickSwitchPanel.Children.Clear(); var quickItems = MonitorConfigManager.GetQuickSwitchItems(); if (quickItems.Count == 0) { quickSwitchPanel.Visibility = Visibility.Collapsed; return; } quickSwitchPanel.Visibility = Visibility.Visible; foreach (var item in quickItems) { var btn = new Button { Content = $"{item.PortLabel}", ToolTip = $"{item.MonitorName}: {item.PortLabel}", Margin = new Thickness(0, 0, 6, 6), Style = (Style)FindResource("QuickSwitchButton"), Tag = item }; btn.Click += async (s, e) => { if (s is Button b && b.Tag is QuickSwitchItem qsi) { await CMMCommand.SetInputSource(qsi.MonitorSerialNumber, qsi.PortVcpValue); UpdateInputDropdown(qsi.MonitorSerialNumber, qsi.PortVcpValue); } }; quickSwitchPanel.Children.Add(btn); } } private void UpdateInputDropdown(string serialNumber, int newValue) { foreach (var child in sp.Children) { if (child is StackPanel row) { foreach (var element in row.Children) { if (element is ComboBox cb && cb.Tag is string sn && sn == serialNumber) { var options = cb.ItemsSource as List; if (options != null) { var index = options.FindIndex(o => o.Value == newValue); if (index >= 0) cb.SelectedIndex = index; } return; } } } } } private StackPanel CreateMonitorHeader(XMonitor monitor, List allOptions) { var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 8, 0, 6) }; row.Children.Add(new TextBlock { Text = monitor.MonitorName, Foreground = Brushes.White, FontSize = 13, FontWeight = FontWeights.SemiBold, VerticalAlignment = VerticalAlignment.Center }); var configBtn = new Button { Content = "Config", Margin = new Thickness(10, 0, 0, 0), FontSize = 11, Style = (Style)FindResource("DarkButton"), Tag = (monitor, allOptions) }; configBtn.Click += ConfigButton_Click; row.Children.Add(configBtn); return row; } private async void ConfigButton_Click(object sender, RoutedEventArgs e) { if (sender is Button btn && btn.Tag is ValueTuple> data) { var (monitor, options) = data; var configWindow = new ConfigWindow(monitor.SerialNumber, monitor.MonitorName, options); configWindow.Owner = this; configWindow.ShowDialog(); if (configWindow.ConfigChanged) { MonitorConfigManager.ClearCache(); await LoadMonitors(); } } } private StackPanel CreateSliderRow(string label, int value, string serialNumber, Func setCommand) { var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) }; row.Children.Add(new TextBlock { Text = label, Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center }); var slider = new Slider { Minimum = 0, Maximum = 100, Value = value, Width = 140, Tag = serialNumber }; var valueText = new TextBlock { Text = value.ToString(), Foreground = Brushes.White, Width = 30, FontSize = 12, Margin = new Thickness(5, 0, 0, 0) }; slider.ValueChanged += (s, e) => valueText.Text = ((int)e.NewValue).ToString(); slider.PreviewMouseUp += async (s, e) => { if (s is Slider sl && sl.Tag is string sn) await setCommand(sn, (int)sl.Value); }; row.Children.Add(slider); row.Children.Add(valueText); return row; } private StackPanel CreateInputRow(int? currentInput, List options, string serialNumber) { var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) }; row.Children.Add(new TextBlock { Text = "Input", Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center }); var combo = new ComboBox { Width = 170, ItemsSource = options, DisplayMemberPath = "Name", Tag = serialNumber, Style = (Style)FindResource("DarkComboBox"), ItemContainerStyle = (Style)FindResource("DarkComboBoxItem") }; row.Children.Add(combo); // Set selection AFTER adding to visual tree, BEFORE adding event handler if (currentInput.HasValue) { var index = options.FindIndex(o => o.Value == currentInput.Value); if (index >= 0) combo.SelectedIndex = index; } // Add event handler AFTER setting the initial selection combo.SelectionChanged += async (s, e) => { // Only trigger if user actually changed the selection (not initial load) if (s is ComboBox cb && cb.Tag is string sn && cb.SelectedItem is InputSourceOption opt && e.AddedItems.Count > 0 && e.RemovedItems.Count > 0) await CMMCommand.SetInputSource(sn, opt.Value); }; return row; } private StackPanel CreatePowerRow(string status, string serialNumber) { var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) }; row.Children.Add(new TextBlock { Text = "Power", Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center }); var btn = new Button { Content = status, Width = 170, Tag = serialNumber, Style = (Style)FindResource("DarkButton") }; btn.Click += async (s, e) => { if (s is Button b && b.Tag is string sn) { var current = await CMMCommand.GetMonPowerStatus(sn); if (current == "Sleep" || current == "PowerOff") await CMMCommand.PowerOn(sn); else await CMMCommand.Sleep(sn); await Task.Delay(1000); b.Content = await CMMCommand.GetMonPowerStatus(sn); } }; row.Children.Add(btn); return row; } }