2026-02-15 11:14:19 +01:00
|
|
|
using System.Windows;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Windows.Media.Imaging;
|
2026-02-17 20:51:35 +01:00
|
|
|
using System.Diagnostics;
|
2026-02-15 11:14:19 +01:00
|
|
|
using Microsoft.Win32;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
|
|
|
|
namespace ImageCatalog_2
|
|
|
|
|
{
|
|
|
|
|
public partial class MainWindow : Window
|
|
|
|
|
{
|
|
|
|
|
private readonly DataModel _model;
|
|
|
|
|
public MainWindow(DataModel model)
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
_model = model;
|
|
|
|
|
DataContext = _model;
|
2026-02-17 20:51:35 +01:00
|
|
|
// 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 { }
|
2026-02-16 18:32:04 +01:00
|
|
|
// Apply theme based on user preference or system setting (default to light)
|
|
|
|
|
ApplyTheme(isDark: false);
|
2026-02-15 11:14:19 +01:00
|
|
|
// 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;
|
2026-02-16 18:32:04 +01:00
|
|
|
_model.SelectModelsFolderRequested += Model_SelectModelsFolderRequested;
|
|
|
|
|
_model.SelectCsvOutputRequested += Model_SelectCsvOutputRequested;
|
2026-02-15 11:14:19 +01:00
|
|
|
|
|
|
|
|
// Watch for logo changes to update preview
|
|
|
|
|
_model.PropertyChanged += Model_PropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 18:32:04 +01:00
|
|
|
private void ApplyTheme(bool isDark)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var rd = isDark ? (ResourceDictionary)Resources["DarkTheme"] : (ResourceDictionary)Resources["LightTheme"];
|
|
|
|
|
foreach (var key in rd.Keys)
|
|
|
|
|
{
|
|
|
|
|
Resources[key] = 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 { }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 11:14:19 +01:00
|
|
|
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 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|