Catalog/imagecatalog/Program.cs
MaddoScientisto 214e540170 Mutually exclusive thumbnail mode selection in UI
Implement mutually exclusive logic for thumbnail display modes in DataModel and MainForm. Add ThumbnailMode property for authoritative state. Replace radio button bindings with event handlers. Synchronize UI with model changes. Explicitly map thumbnail flags to PicSettings. Force WinForms startup, disabling WPF branch.
2026-02-16 18:58:15 +01:00

194 lines
No EOL
7.7 KiB
C#

using System.Runtime.InteropServices;
using ImageCatalog;
using ImageCatalog_2.Services;
using MaddoShared;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using System.IO;
using Microsoft.Extensions.Options;
namespace ImageCatalog_2;
static class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
private const int STD_OUTPUT_HANDLE = -11;
private const int STD_ERROR_HANDLE = -12;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetStdHandle(int nStdHandle, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
private const uint GENERIC_WRITE = 0x40000000;
private const uint OPEN_EXISTING = 3;
private static void RedirectConsoleOutput()
{
var stdOutHandle = CreateFile("CONOUT$", GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
var safeFileHandle = new Microsoft.Win32.SafeHandles.SafeFileHandle(stdOutHandle, true);
var fileStream = new FileStream(safeFileHandle, FileAccess.Write);
var standardOutput = new StreamWriter(fileStream) { AutoFlush = true };
Console.SetOut(standardOutput);
Console.SetError(standardOutput);
}
public static IServiceProvider ServiceProvider { get; private set; }
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
AllocConsole();
RedirectConsoleOutput();
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
ServiceProvider = serviceCollection.BuildServiceProvider();
// Resolve WPF MainWindow when available, otherwise fall back to WinForms MainForm
var serviceProvider = ServiceProvider;
// NOTE: By default the app will attempt to run the WPF window when available and fall back
// to the WinForms MainForm. If you want to force the WinForms UI for testing, uncomment
// the block below and comment out the WPF branch.
// -----------------------------------------------------------------------------
// // Force WinForms UI (uncomment to enable)
var mainForm = serviceProvider.GetRequiredService<MainForm>();
// If you want to set the DataModel explicitly on the WinForms form use the lines below
// var mainViewModel = serviceProvider.GetRequiredService<DataModel>();
// mainForm.Model = mainViewModel;
Application.Run(mainForm);
// -----------------------------------------------------------------------------
//if (serviceProvider.GetService(typeof(ImageCatalog_2.MainWindow)) is ImageCatalog_2.MainWindow wpfMain)
//{
// // Start WPF app
// var app = new System.Windows.Application();
// app.Run(wpfMain);
//}
//else
//{
// var mainForm = serviceProvider.GetRequiredService<MainForm>();
// Application.Run(mainForm);
//}
}
private static void ConfigureServices(ServiceCollection services)
{
// Register AutoMapper (new AddAutoMapper overload — provide config and marker types)
services.AddAutoMapper(cfg => { }, typeof(Program));
// Register your services here
services.AddTransient<ITestService, TestService>();
services.AddTransient<ISettingsService, SettingsService>();
services.AddTransient<DataModel>(sp =>
{
// Resolve optional version provider and pass to DataModel
var testService = sp.GetRequiredService<ITestService>();
var settingsService = sp.GetRequiredService<ISettingsService>();
var imageCreation = sp.GetRequiredService<ImageCreationStuff>();
var picSettings = sp.GetRequiredService<PicSettings>();
var mapper = sp.GetRequiredService<IMapper>();
var logger = sp.GetRequiredService<ILogger<DataModel>>();
var versionProvider = sp.GetService<MaddoShared.IVersionProvider>();
return new DataModel(testService, settingsService, imageCreation, picSettings, mapper, logger, versionProvider);
});
services.AddTransient<ImageCreationStuff>();
services.AddTransient<ImageCreatorSharp>();
services.AddTransient<ImageCreatorAlternate>();
services.AddTransient<ImageCreatorMapper>();
// Register IImageCreator to be resolved via ImageCreatorMapper which selects concrete implementation at call time
services.AddTransient<IImageCreator>(sp => sp.GetRequiredService<ImageCreatorMapper>());
// Register a ParametriSetup singleton that persists user preferences in LocalApplicationData
var userPrefsPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"ImageCatalog", "userprefs.xml");
services.AddSingleton(new ParametriSetup(userPrefsPath));
services.AddSingleton<PicSettings>();
// Register your forms
services.AddTransient<MainForm>();
// Register WPF MainWindow so it can be resolved with the existing DataModel
services.AddTransient<ImageCatalog_2.MainWindow>();
// Version provider for UI and logging
services.AddSingleton<MaddoShared.IVersionProvider, MaddoShared.VersionProvider>();
services.AddLogging(configure =>
{
configure.AddCustomFormatter();
configure.AddConsole();
configure.SetMinimumLevel(LogLevel.Debug);
});
}
}
public static class ConsoleLoggerExtensions
{
public static ILoggingBuilder AddCustomFormatter(
this ILoggingBuilder builder) =>
builder
.AddConsole(options => options.FormatterName = nameof(CustomLoggingFormatter))
.AddConsoleFormatter<CustomLoggingFormatter, ConsoleFormatterOptions>()
.AddFilter("LuckyPennySoftware.AutoMapper.License", LogLevel.None);
}
public sealed class CustomLoggingFormatter : ConsoleFormatter, IDisposable
{
private readonly IDisposable _optionsReloadToken;
private ConsoleFormatterOptions _formatterOptions;
public CustomLoggingFormatter(IOptionsMonitor<ConsoleFormatterOptions> options)
// Case insensitive
: base(nameof(CustomLoggingFormatter)) =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);
private void ReloadLoggerOptions(ConsoleFormatterOptions options) =>
_formatterOptions = options;
public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter? textWriter)
{
string? message =
logEntry.Formatter?.Invoke(
logEntry.State, logEntry.Exception);
if (message is null)
{
return;
}
textWriter.WriteLine($"{message}");
}
public void Dispose() => _optionsReloadToken?.Dispose();
}