#if WINDOWS using System.Windows; using MahApps.Metro.Controls; using ControlzEx.Theming; using Microsoft.Extensions.DependencyInjection; using System; using System.IO; using System.Windows.Media.Imaging; using System.Diagnostics; using Microsoft.Win32; using System.Windows.Forms; namespace ImageCatalog_2 { public partial class MainWindow : MetroWindow { private readonly DataModel _model; private bool _isDarkTheme = false; public MainWindow(DataModel model) { InitializeComponent(); _model = model; DataContext = _model; // Set product version in status bar (use ProductVersion rather than AssemblyVersion) try { var entry = System.Reflection.Assembly.GetEntryAssembly(); string version = string.Empty; if (entry is not null && !string.IsNullOrEmpty(entry.Location)) { try { version = FileVersionInfo.GetVersionInfo(entry.Location).ProductVersion ?? string.Empty; } catch { } } if (string.IsNullOrWhiteSpace(version)) { // fallback to assembly version version = entry?.GetName().Version?.ToString() ?? string.Empty; } VersionTextBlock.Text = string.IsNullOrWhiteSpace(version) ? string.Empty : $"v{version}"; } catch { } // Ensure MahApps resource dictionaries are loaded so chrome/styles are available EnsureMahAppsResourcesLoaded(); // Apply theme based on user preference or system setting (default to light) ApplyTheme(isDark: false); // Subscribe to DataModel events that require UI dialogs _model.SelectSourceFolderRequested += Model_SelectSourceFolderRequested; _model.SelectDestinationFolderRequested += Model_SelectDestinationFolderRequested; _model.SelectLogoFileRequested += Model_SelectLogoFileRequested; _model.SaveSettingsRequested += Model_SaveSettingsRequested; _model.LoadSettingsRequested += Model_LoadSettingsRequested; _model.SelectColorRequested += Model_SelectColorRequested; _model.SelectTransparentColorRequested += Model_SelectTransparentColorRequested; _model.SelectModelsFolderRequested += Model_SelectModelsFolderRequested; _model.SelectCsvOutputRequested += Model_SelectCsvOutputRequested; // Watch for logo changes to update preview _model.PropertyChanged += Model_PropertyChanged; } private void ApplyTheme(bool isDark) { try { var rd = isDark ? (ResourceDictionary)Resources["DarkTheme"] : (ResourceDictionary)Resources["LightTheme"]; foreach (var key in rd.Keys) { // If the theme dictionary uses suffixed keys (e.g. "WindowBackgroundBrush.Dark"), // map them to the base key ("WindowBackgroundBrush") so existing DynamicResource lookups update. string outKey = key?.ToString() ?? string.Empty; if (outKey.EndsWith(".Light", StringComparison.OrdinalIgnoreCase)) outKey = outKey.Substring(0, outKey.Length - ".Light".Length); else if (outKey.EndsWith(".Dark", StringComparison.OrdinalIgnoreCase)) outKey = outKey.Substring(0, outKey.Length - ".Dark".Length); Resources[outKey] = rd[key]; } } catch { // ignore theme failures } } private void Model_SelectModelsFolderRequested(object? sender, EventArgs e) { var dlg = new System.Windows.Forms.FolderBrowserDialog(); var starting = string.IsNullOrWhiteSpace(_model.ModelsFolderPath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) : _model.ModelsFolderPath; dlg.SelectedPath = starting; if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { _model.ModelsFolderPath = dlg.SelectedPath + Path.DirectorySeparatorChar; } } private void OpenModelsFolder_Click(object sender, RoutedEventArgs e) { try { var path = _model.ModelsFolderPath; if (string.IsNullOrWhiteSpace(path)) return; path = path.Trim().Trim('"'); if (File.Exists(path)) { System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{path}\""); return; } if (Directory.Exists(path)) { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = path, UseShellExecute = true }); return; } } catch { } } private void Model_SelectCsvOutputRequested(object? sender, EventArgs e) { var dlg = new Microsoft.Win32.SaveFileDialog(); dlg.Filter = "CSV file (*.csv)|*.csv|All files (*.*)|*.*"; if (!string.IsNullOrWhiteSpace(_model.CsvOutputPath)) dlg.FileName = _model.CsvOutputPath; var result = dlg.ShowDialog(this); if (result == true) { _model.CsvOutputPath = dlg.FileName; } } private void OpenCsvOutputFolder_Click(object sender, RoutedEventArgs e) { try { var path = _model.CsvOutputPath; if (string.IsNullOrWhiteSpace(path)) return; path = path.Trim().Trim('"'); if (File.Exists(path)) { System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{path}\""); return; } var dir = Path.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir)) { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = dir, UseShellExecute = true }); return; } } catch { } } private void Model_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e is null || string.IsNullOrWhiteSpace(e.PropertyName)) return; if (e.PropertyName == nameof(_model.LogoFile)) { UpdateLogoPreview(_model.LogoFile); } } private void Model_SelectSourceFolderRequested(object? sender, EventArgs e) { var dlg = new System.Windows.Forms.FolderBrowserDialog(); var starting = string.IsNullOrWhiteSpace(_model.SourcePath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) : _model.SourcePath; dlg.SelectedPath = starting; if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { _model.SourcePath = dlg.SelectedPath + Path.DirectorySeparatorChar; } } private void OpenSourceFolder_Click(object sender, RoutedEventArgs e) { try { var path = _model.SourcePath; if (string.IsNullOrWhiteSpace(path)) return; path = path.Trim().Trim('"'); if (File.Exists(path)) { System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{path}\""); return; } if (Directory.Exists(path)) { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = path, UseShellExecute = true }); return; } } catch (Exception ex) { // ignore for now, or could show a message } } private void Model_SelectDestinationFolderRequested(object? sender, EventArgs e) { var dlg = new System.Windows.Forms.FolderBrowserDialog(); var starting = string.IsNullOrWhiteSpace(_model.DestinationPath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) : _model.DestinationPath; dlg.SelectedPath = starting; if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { _model.DestinationPath = dlg.SelectedPath + Path.DirectorySeparatorChar; } } private void OpenDestinationFolder_Click(object sender, RoutedEventArgs e) { try { var path = _model.DestinationPath; if (string.IsNullOrWhiteSpace(path)) return; path = path.Trim().Trim('"'); if (File.Exists(path)) { System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{path}\""); return; } if (Directory.Exists(path)) { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = path, UseShellExecute = true }); return; } } catch (Exception ex) { // ignore for now } } private void Model_SelectLogoFileRequested(object? sender, EventArgs e) { var dlg = new Microsoft.Win32.OpenFileDialog(); dlg.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif"; if (!string.IsNullOrWhiteSpace(_model.LogoFile)) dlg.FileName = _model.LogoFile; var result = dlg.ShowDialog(this); if (result == true) { _model.LogoFile = dlg.FileName; } } private async void Model_SaveSettingsRequested(object? sender, string filePath) { var dlg = new Microsoft.Win32.SaveFileDialog(); dlg.Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*"; var result = dlg.ShowDialog(this); if (result == true) { await _model.SaveSettingsToFileAsync(dlg.FileName); } } private async void Model_LoadSettingsRequested(object? sender, string filePath) { var dlg = new Microsoft.Win32.OpenFileDialog(); dlg.Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*"; var result = dlg.ShowDialog(this); if (result == true) { await _model.LoadSettingsFromFileAsync(dlg.FileName); } } private void Model_SelectColorRequested(object? sender, EventArgs e) { var dlg = new System.Windows.Forms.ColorDialog { AllowFullOpen = true }; if (!string.IsNullOrWhiteSpace(_model.TextColorRGB)) { try { dlg.Color = System.Drawing.ColorTranslator.FromHtml(_model.TextColorRGB); } catch { } } if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { _model.TextColorRGB = System.Drawing.ColorTranslator.ToHtml(dlg.Color); } } private void Model_SelectTransparentColorRequested(object? sender, EventArgs e) { var dlg = new System.Windows.Forms.ColorDialog { AllowFullOpen = true }; try { dlg.Color = System.Drawing.ColorTranslator.FromHtml(_model.TransparentColor); } catch { } if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { _model.TransparentColor = System.Drawing.ColorTranslator.ToHtml(dlg.Color); } } private void ToggleTheme_Click(object? sender, RoutedEventArgs e) { ToggleTheme(); } private void ToggleTheme() { try { _isDarkTheme = !_isDarkTheme; // Use MahApps ThemeManager to change the application theme (handles chrome and brushes) try { var themeName = _isDarkTheme ? "Dark.Blue" : "Light.Blue"; ThemeManager.Current.ChangeTheme(System.Windows.Application.Current, themeName); } catch { // Fall back silently if ThemeManager isn't available } // Still apply local resource overrides so any app-specific keys update ApplyTheme(_isDarkTheme); } catch { // ignore toggle failures } } private void EnsureMahAppsResourcesLoaded() { try { var app = System.Windows.Application.Current; if (app is null) return; var mds = app.Resources.MergedDictionaries; // Helper to add if missing void AddIfMissing(string uriString) { if (!mds.Any(d => d.Source is not null && d.Source.OriginalString.Equals(uriString, StringComparison.OrdinalIgnoreCase))) { mds.Add(new ResourceDictionary { Source = new Uri(uriString) }); } } AddIfMissing("pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml"); AddIfMissing("pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml"); // Ensure a default theme is present if (!mds.Any(d => d.Source is not null && d.Source.OriginalString.IndexOf("/MahApps.Metro;component/Styles/Themes/", StringComparison.OrdinalIgnoreCase) >= 0)) { AddIfMissing("pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml"); _isDarkTheme = false; } } catch { // ignore; styling will fallback to local resources } } private void UpdateLogoPreview(string? path) { if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) { LogoPreview.Source = null; return; } try { var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.UriSource = new Uri(path); bitmap.EndInit(); LogoPreview.Source = bitmap; } catch { LogoPreview.Source = null; } } } } #endif