diff --git a/src/CamBooth/CamBooth.App/App.xaml.cs b/src/CamBooth/CamBooth.App/App.xaml.cs index 72216aa..831dc4a 100644 --- a/src/CamBooth/CamBooth.App/App.xaml.cs +++ b/src/CamBooth/CamBooth.App/App.xaml.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System.IO; + using System.Windows; using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.Logging; @@ -25,31 +26,65 @@ public partial class App : Application base.OnStartup(e); var services = new ServiceCollection(); - ConfigureServices(services); + + // Register base services + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // Zuerst den Provider bauen, um AppSettings zu laden + var tempProvider = services.BuildServiceProvider(); + var appSettings = tempProvider.GetRequiredService(); + var logger = tempProvider.GetRequiredService(); + + // Stelle sicher, dass das PictureLocation-Verzeichnis existiert + try + { + if (!Directory.Exists(appSettings.PictureLocation)) + { + Directory.CreateDirectory(appSettings.PictureLocation); + logger.Info($"Picture directory created: {appSettings.PictureLocation}"); + } + } + catch (Exception ex) + { + logger.Error($"Failed to create picture directory: {ex.Message}"); + } + + // Jetzt die Camera Services basierend auf AppSettings registrieren + // Mit Try-Catch für fehlende DLL-Abhängigkeiten + try + { + if (appSettings.UseMockCamera) + { + services.AddSingleton(); + services.AddSingleton(); + } + else + { + services.AddSingleton(); + services.AddSingleton(); + } + } + catch (DllNotFoundException ex) + { + // Falls EDSDK DLL nicht gefunden, fallback auf Mock + MessageBox.Show( + $"EDSDK konnte nicht geladen werden. Verwende Mock-Kamera.\n\nFehler: {ex.Message}", + "DLL nicht gefunden", + MessageBoxButton.OK, + MessageBoxImage.Warning); + + services.AddSingleton(); + services.AddSingleton(); + } + + services.AddTransient(); _serviceProvider = services.BuildServiceProvider(); var mainWindow = _serviceProvider.GetRequiredService(); mainWindow.Show(); } - - - private void ConfigureServices(IServiceCollection services) - { - // Register your services and view models here - services.AddTransient(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); -#if DEBUG - services.AddSingleton(); - services.AddSingleton(); -#else - services.AddSingleton(); - services.AddSingleton(); -#endif - - - } } \ No newline at end of file diff --git a/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs b/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs index 0d4c6c8..b7183cb 100644 --- a/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs +++ b/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs @@ -1,4 +1,4 @@ -using CamBooth.App.Core.Logging; +using CamBooth.App.Core.Logging; namespace CamBooth.App.Core.AppSettings; @@ -21,21 +21,42 @@ public class AppSettingsService private void Initialize() { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"; + loadedConfigFile = "Core/AppSettings/app.settings.json"; -#if DEBUG - loadedConfigFile = "Core/AppSettings/app.settings.dev.json"; -#endif - configuration = new ConfigurationBuilder() + var configBuilder = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) - .AddJsonFile(loadedConfigFile, optional: false, reloadOnChange: true) - .Build(); + .AddJsonFile(loadedConfigFile, optional: false, reloadOnChange: true); + + // Lade umgebungsspezifische Konfigurationsdatei + if (environment == "Development") + { + configBuilder.AddJsonFile("Core/AppSettings/app.settings.dev.json", optional: true, reloadOnChange: true); + loadedConfigFile = "Core/AppSettings/app.settings.dev.json"; + } + + configuration = configBuilder.Build(); + + _logger.Info($"Konfiguration geladen aus: {loadedConfigFile} (Environment: {environment})"); } + public bool IsDebugMode => bool.Parse(configuration["AppSettings:IsDebugMode"] ?? "false"); + public string? AppName => configuration["AppSettings:AppName"]; public bool IsDebugConsoleVisible => bool.Parse(configuration["AppSettings:DebugConsoleVisible"] ?? string.Empty); public string? PictureLocation => configuration["AppSettings:PictureLocation"]; + + public int PhotoCountdownSeconds => int.Parse(configuration["AppSettings:PhotoCountdownSeconds"] ?? "5"); + + public int FocusDelaySeconds => int.Parse(configuration["AppSettings:FocusDelaySeconds"] ?? "2"); + + public int FocusTimeoutMs => int.Parse(configuration["AppSettings:FocusTimeoutMs"] ?? "3000"); + + public bool IsShutdownEnabled => bool.Parse(configuration["AppSettings:IsShutdownEnabled"] ?? "false"); + + public bool UseMockCamera => bool.Parse(configuration["AppSettings:UseMockCamera"] ?? "false"); public string? ConnectionString => configuration.GetConnectionString("DefaultConnection"); diff --git a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json index 99f6f84..4341bb2 100644 --- a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json +++ b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json @@ -2,8 +2,14 @@ "AppSettings": { "AppName": "Meine Anwendung", "Version": "1.0.0", + "IsDebugMode": true, "PictureLocation": "C:\\tmp\\cambooth", - "DebugConsoleVisible": "true" + "DebugConsoleVisible": "true", + "PhotoCountdownSeconds": 2, + "FocusDelaySeconds": 1, + "FocusTimeoutMs": 1000, + "IsShutdownEnabled": false, + "UseMockCamera": true }, "ConnectionStrings": { "DefaultConnection": "Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;" diff --git a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json index bc47f37..12bf7ad 100644 --- a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json +++ b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json @@ -2,8 +2,14 @@ "AppSettings": { "AppName": "Meine Anwendung", "Version": "1.0.0", + "IsDebugMode": false, "PictureLocation": "C:\\cambooth\\pictures", - "DebugConsoleVisible": "true" + "DebugConsoleVisible": "false", + "PhotoCountdownSeconds": 5, + "FocusDelaySeconds": 2, + "FocusTimeoutMs": 3000, + "IsShutdownEnabled": true, + "UseMockCamera": true }, "ConnectionStrings": { "DefaultConnection": "Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;" diff --git a/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs b/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs index 6ef452a..7d46f80 100644 --- a/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs +++ b/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs @@ -317,7 +317,10 @@ public class CameraService : IDisposable Info.FileName = $"img_{Guid.NewGuid().ToString()}.jpg"; sender.DownloadFile(Info, this._appSettings.PictureLocation); this._logger.Info("Download complete: " + Path.Combine(this._appSettings.PictureLocation, Info.FileName)); - Application.Current.Dispatcher.Invoke(() => { this._pictureGalleryService.LoadThumbnailsToCache(); }); + Application.Current.Dispatcher.Invoke(() => { + this._pictureGalleryService.IncrementNewPhotoCount(); + this._pictureGalleryService.LoadThumbnailsToCache(); + }); } catch (Exception ex) { diff --git a/src/CamBooth/CamBooth.App/Features/DebugConsole/DebugConsolePage.xaml.cs b/src/CamBooth/CamBooth.App/Features/DebugConsole/DebugConsolePage.xaml.cs index 37353db..ab03827 100644 --- a/src/CamBooth/CamBooth.App/Features/DebugConsole/DebugConsolePage.xaml.cs +++ b/src/CamBooth/CamBooth.App/Features/DebugConsole/DebugConsolePage.xaml.cs @@ -14,7 +14,6 @@ public partial class DebugConsolePage : Page this.InitializeComponent(); } - private void Logger_OnErrorLog(string text) { this.tbDebugOutput.Text = this.tbDebugOutput.Text.Insert(0, text + "\n"); diff --git a/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryPage.xaml.cs b/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryPage.xaml.cs index 2b70591..2fef41e 100644 --- a/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryPage.xaml.cs +++ b/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryPage.xaml.cs @@ -24,6 +24,8 @@ public partial class PictureGalleryPage : Page private readonly PictureGalleryService _pictureGalleryService; + private ContentDialog? _openContentDialog; + public PictureGalleryPage(AppSettingsService appSettingsService, Logger logger, PictureGalleryService pictureGalleryService) { @@ -90,21 +92,54 @@ public partial class PictureGalleryPage : Page } + public void CloseOpenDialog() + { + void CloseDialog() + { + if (this._openContentDialog is null) + { + return; + } + + this._openContentDialog.ButtonClicked -= this.ContentDialog_OnButtonClicked; + this._openContentDialog.GetType().GetMethod("Hide", Type.EmptyTypes)?.Invoke(this._openContentDialog, null); + this.RootContentDialogPresenter.Content = null; + this._openContentDialog = null; + } + + if (this.Dispatcher.CheckAccess()) + { + CloseDialog(); + return; + } + + this.Dispatcher.Invoke(CloseDialog); + } + + private void Hyperlink_OnClick(object sender, RoutedEventArgs e) { Uri? picturePathUri = ((Hyperlink)sender).Tag as Uri; + if (picturePathUri is null) + { + return; + } + + string picturePath = picturePathUri.AbsolutePath; Application.Current.Dispatcher.BeginInvoke( async () => { - ContentDialog contentDialog = new (this.RootContentDialogPresenter); + this.CloseOpenDialog(); + ContentDialog contentDialog = new(this.RootContentDialogPresenter); + this._openContentDialog = contentDialog; Image imageToShow = new() { MaxHeight = 570, Background = new SolidColorBrush(Colors.White), VerticalAlignment = VerticalAlignment.Center, - Source = PictureGalleryService.CreateThumbnail(picturePathUri.AbsolutePath, 450, 300) + Source = PictureGalleryService.CreateThumbnail(picturePath, 450, 300) }; - + contentDialog.VerticalAlignment = VerticalAlignment.Top; contentDialog.PrimaryButtonAppearance = ControlAppearance.Primary; contentDialog.CloseButtonAppearance = ControlAppearance.Light; @@ -117,10 +152,23 @@ public partial class PictureGalleryPage : Page // contentDialog.SetCurrentValue(ContentDialog.PrimaryButtonIconProperty, PictureGalleryService.CreateRegularSymbolIcon(SymbolRegular.Print48, Colors.Tomato)); - contentDialog.Tag = picturePathUri.AbsolutePath; + contentDialog.Tag = picturePath; contentDialog.ButtonClicked += this.ContentDialog_OnButtonClicked; - await contentDialog.ShowAsync(); + try + { + await contentDialog.ShowAsync(); + } + finally + { + contentDialog.ButtonClicked -= this.ContentDialog_OnButtonClicked; + if (ReferenceEquals(this._openContentDialog, contentDialog)) + { + this._openContentDialog = null; + } + } }); } -} \ No newline at end of file +} + + diff --git a/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryService.cs b/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryService.cs index f3ed87c..5e87300 100644 --- a/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryService.cs +++ b/src/CamBooth/CamBooth.App/Features/PictureGallery/PictureGalleryService.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -17,6 +17,10 @@ public class PictureGalleryService private readonly Dictionary thumbnails = new(); + private int _newPhotoCount = 0; + + public event EventHandler? NewPhotoCountChanged; + public PictureGalleryService(AppSettingsService appSettings, Logger logger) { @@ -30,6 +34,25 @@ public class PictureGalleryService .Select(ordered => ordered.Value) .ToList(); + public int NewPhotoCount => _newPhotoCount; + + public void IncrementNewPhotoCount() + { + _newPhotoCount++; + OnNewPhotoCountChanged(_newPhotoCount); + } + + public void ResetNewPhotoCount() + { + _newPhotoCount = 0; + OnNewPhotoCountChanged(_newPhotoCount); + } + + private void OnNewPhotoCountChanged(int count) + { + NewPhotoCountChanged?.Invoke(this, count); + } + public async Task LoadThumbnailsToCache(int cacheSize = 0) { @@ -37,7 +60,33 @@ public class PictureGalleryService string? pictureLocation = this._appSettings.PictureLocation; int loop = 0; - List picturePaths = Directory.EnumerateFiles(pictureLocation).ToList(); + // Sicherstellen, dass das Verzeichnis existiert + if (!Directory.Exists(pictureLocation)) + { + this._logger.Info($"Picture directory does not exist: '{pictureLocation}'. Creating it..."); + try + { + Directory.CreateDirectory(pictureLocation); + this._logger.Info($"Picture directory created: '{pictureLocation}'"); + } + catch (Exception ex) + { + this._logger.Error($"Failed to create picture directory: {ex.Message}"); + return; + } + } + + // Filter nur Bilddateien + string[] imageExtensions = { ".jpg", ".jpeg", ".png", ".bmp", ".gif" }; + List picturePaths = Directory.EnumerateFiles(pictureLocation) + .Where(f => imageExtensions.Contains(Path.GetExtension(f).ToLower())) + .ToList(); + + if (picturePaths.Count == 0) + { + this._logger.Info($"No pictures found in directory: '{pictureLocation}'"); + return; + } await Task.Run( () => diff --git a/src/CamBooth/CamBooth.App/MainWindow.xaml b/src/CamBooth/CamBooth.App/MainWindow.xaml index b0f34ce..9014aa2 100644 --- a/src/CamBooth/CamBooth.App/MainWindow.xaml +++ b/src/CamBooth/CamBooth.App/MainWindow.xaml @@ -56,13 +56,12 @@ -