diff --git a/MaddoShared/ImageCreationStuff.cs b/MaddoShared/ImageCreationStuff.cs
index ac78603..778a7e8 100644
--- a/MaddoShared/ImageCreationStuff.cs
+++ b/MaddoShared/ImageCreationStuff.cs
@@ -96,7 +96,7 @@ namespace MaddoShared
catch (Exception e)
{
logger.LogError(e, "Error in reporting update");
- throw;
+ // Don't rethrow - continue processing other images
}
}
finally
diff --git a/MaddoShared/MaddoShared.csproj b/MaddoShared/MaddoShared.csproj
index 4753af8..502345d 100644
--- a/MaddoShared/MaddoShared.csproj
+++ b/MaddoShared/MaddoShared.csproj
@@ -9,6 +9,7 @@
+
all
diff --git a/imagecatalog/Commands/ButtonCommandBinder.cs b/imagecatalog/Commands/ButtonCommandBinder.cs
new file mode 100644
index 0000000..2fc301c
--- /dev/null
+++ b/imagecatalog/Commands/ButtonCommandBinder.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Windows.Forms;
+using System.Windows.Input;
+
+namespace ImageCatalog_2.Commands
+{
+ ///
+ /// Helper class to bind WinForms Button controls to ICommand implementations
+ ///
+ public static class ButtonCommandBinder
+ {
+ ///
+ /// Binds a Button's Click event to an ICommand
+ ///
+ /// The button to bind
+ /// The command to execute
+ /// Optional parameter to pass to the command
+ public static void BindCommand(this Button button, ICommand command, object commandParameter = null)
+ {
+ if (button == null) throw new ArgumentNullException(nameof(button));
+ if (command == null) throw new ArgumentNullException(nameof(command));
+
+ // Wire up the Click event to execute the command
+ button.Click += (sender, e) =>
+ {
+ if (command.CanExecute(commandParameter))
+ {
+ command.Execute(commandParameter);
+ }
+ };
+
+ // Wire up CanExecuteChanged to enable/disable the button
+ command.CanExecuteChanged += (sender, e) =>
+ {
+ button.Enabled = command.CanExecute(commandParameter);
+ };
+
+ // Set initial enabled state
+ button.Enabled = command.CanExecute(commandParameter);
+ }
+
+ ///
+ /// Binds multiple buttons to commands at once
+ ///
+ public static void BindCommands(params (Button button, ICommand command, object parameter)[] bindings)
+ {
+ foreach (var (button, command, parameter) in bindings)
+ {
+ button.BindCommand(command, parameter);
+ }
+ }
+ }
+}
diff --git a/imagecatalog/DataModel.cs b/imagecatalog/DataModel.cs
index ce02440..49a715d 100644
--- a/imagecatalog/DataModel.cs
+++ b/imagecatalog/DataModel.cs
@@ -1,14 +1,19 @@
using ImageCatalog_2.Commands;
using ImageCatalog_2.Services;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing.Text;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Forms;
using System.Windows.Input;
+using AutoMapper;
+using MaddoShared;
using Microsoft.Extensions.Logging;
namespace ImageCatalog_2
@@ -29,17 +34,25 @@ namespace ImageCatalog_2
private readonly ITestService _service;
private readonly ILogger _logger;
private readonly ISettingsService _settingsService;
+ private readonly ImageCreationStuff _imageCreationService;
+ private readonly PicSettings _picSettings;
+ private readonly IMapper _mapper;
// ComboBox collections
public List AvailableFonts { get; }
public List VerticalPositions { get; } = new() { "Alto", "Centro", "Basso" };
public List HorizontalAlignments { get; } = new() { "Sinistra", "Centro", "Destra" };
- public DataModel(ITestService testService, ISettingsService settingsService, ILogger logger)
+ public DataModel(ITestService testService, ISettingsService settingsService,
+ ImageCreationStuff imageCreationService, PicSettings picSettings,
+ IMapper mapper, ILogger logger)
{
_service = testService;
_logger = logger;
_settingsService = settingsService;
+ _imageCreationService = imageCreationService;
+ _picSettings = picSettings;
+ _mapper = mapper;
TestCommand = new RelayCommand(Test);
AsyncTestCommand = new AsyncCommand(TestAsync);
@@ -774,6 +787,67 @@ namespace ImageCatalog_2
}
}
+ // Image processing progress and status
+ private string _processingStatus = "";
+ public string ProcessingStatus
+ {
+ get => _processingStatus;
+ set
+ {
+ _processingStatus = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ private int _processedImagesCount = 0;
+ public int ProcessedImagesCount
+ {
+ get => _processedImagesCount;
+ set
+ {
+ _processedImagesCount = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ private int _totalImagesCount = 0;
+ public int TotalImagesCount
+ {
+ get => _totalImagesCount;
+ set
+ {
+ _totalImagesCount = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ private int _progressBarValue = 0;
+ public int ProgressBarValue
+ {
+ get => _progressBarValue;
+ set
+ {
+ _progressBarValue = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ private int _progressBarMaximum = 100;
+ public int ProgressBarMaximum
+ {
+ get => _progressBarMaximum;
+ set
+ {
+ _progressBarMaximum = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ private ConcurrentBag _results = new();
+ private int _currentAmount = 0;
+ private int _previousAmount = 0;
+ private System.Threading.Timer? _speedTimer;
+
private void Test(object parameter)
{
Debug.WriteLine("Yep");
@@ -787,6 +861,123 @@ namespace ImageCatalog_2
private async Task ProcessImages()
{
+ _logger.LogInformation("Avvio elaborazione...");
+ UiEnabled = false;
+
+ MainToken?.Dispose();
+ MainToken = new CancellationTokenSource();
+ var token = MainToken.Token;
+
+ // Fix paths
+ FixPaths();
+
+ // Reset counters
+ ProcessingStatus = "Elaborazione in corso...";
+ TotalImagesCount = 0;
+ ProcessedImagesCount = 0;
+ SpeedCounter = "-f/m";
+ ProgressBarValue = 0;
+ ProgressBarMaximum = 100;
+
+ // Update PicSettings from DataModel using AutoMapper
+ _mapper.Map(this, _picSettings);
+
+ var imageCreationOptions = new ImageCreationStuff.Options
+ {
+ AggiornaSottodirectory = UpdateSubdirectories,
+ CreaSottocartelle = CreateSubfolders,
+ FilePerCartella = FilesPerFolder,
+ SuffissoCartelle = FolderSuffix,
+ CifreContatore = CounterDigits,
+ NumerazioneType = UseProgressiveNumbering ? NumerazioneType.Progressiva : NumerazioneType.Files,
+ SourcePath = SourcePath,
+ DestinationPath = DestinationPath,
+ MaxThreads = ThreadsCount,
+ ChunksSize = ChunkSize,
+ LinearExecution = UseSequentialProcessing
+ };
+
+ try
+ {
+ _results = new ConcurrentBag();
+ _currentAmount = 0;
+ _previousAmount = 0;
+
+ // Start speed timer (every minute)
+ _speedTimer = new System.Threading.Timer(UpdateSpeedCounter, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
+
+ var time = await _imageCreationService.CreaCatalogoParallel(
+ imageCreationOptions,
+ _results,
+ OnImageProcessed,
+ token);
+
+ SpeedCounter = time;
+ _speedTimer?.Dispose();
+ _speedTimer = null;
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogInformation("Operazione Cancellata");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Errore durante l'elaborazione delle immagini");
+ ProcessingStatus = $"Errore: {ex.Message}";
+ }
+ finally
+ {
+ MainToken?.Dispose();
+ MainToken = null;
+ _speedTimer?.Dispose();
+ _speedTimer = null;
+ }
+
+ ProcessingStatus = "Finito";
+ UiEnabled = true;
+ }
+
+ private void UpdateSpeedCounter(object? state)
+ {
+ _previousAmount = _currentAmount;
+ _currentAmount = _results.Count;
+ int diff = _currentAmount - _previousAmount;
+ SpeedCounter = $"{diff} f/m";
+ }
+
+ private void OnImageProcessed(object? sender, Tuple args)
+ {
+ ProcessedImagesCount = _results.Count;
+ TotalImagesCount = args.Item2;
+ ProgressBarMaximum = args.Item2;
+ ProgressBarValue = _results.Count;
+ ProcessingStatus = args.Item1;
+ }
+
+ private void FixPaths()
+ {
+ SourcePath = FixPath(SourcePath);
+ DestinationPath = FixPath(DestinationPath);
+ }
+
+ private string FixPath(string path)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ return string.Empty;
+ }
+
+ // Trim leading/trailing quotes
+ path = path.Trim().Trim('"');
+
+ // Normalize directory separators
+ path = path.Replace('/', System.IO.Path.DirectorySeparatorChar)
+ .Replace('\\', System.IO.Path.DirectorySeparatorChar);
+
+ // Remove trailing separators then add one back
+ path = path.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar;
+
+ return path;
}
private async Task CancelOperation()
diff --git a/imagecatalog/ImageCatalog 2.csproj b/imagecatalog/ImageCatalog 2.csproj
index 6473071..aed7099 100644
--- a/imagecatalog/ImageCatalog 2.csproj
+++ b/imagecatalog/ImageCatalog 2.csproj
@@ -39,6 +39,7 @@
+
diff --git a/imagecatalog/MainForm.Designer.cs b/imagecatalog/MainForm.Designer.cs
index fdee7ba..266fa4f 100644
--- a/imagecatalog/MainForm.Designer.cs
+++ b/imagecatalog/MainForm.Designer.cs
@@ -1835,7 +1835,6 @@ namespace ImageCatalog
_btnCreaCatalogoAsync.TabIndex = 68;
_btnCreaCatalogoAsync.Text = "CREA";
_btnCreaCatalogoAsync.UseVisualStyleBackColor = true;
- _btnCreaCatalogoAsync.Click += Button1_Click;
//
// dataModelBindingSource1
//
@@ -2268,16 +2267,7 @@ namespace ImageCatalog
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
- if (_btnCreaCatalogoAsync != null)
- {
- _btnCreaCatalogoAsync.Click -= Button1_Click;
- }
-
_btnCreaCatalogoAsync = value;
- if (_btnCreaCatalogoAsync != null)
- {
- _btnCreaCatalogoAsync.Click += Button1_Click;
- }
}
}
}
diff --git a/imagecatalog/MainForm.cs b/imagecatalog/MainForm.cs
index f18ab89..c5e7b12 100644
--- a/imagecatalog/MainForm.cs
+++ b/imagecatalog/MainForm.cs
@@ -13,9 +13,11 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using ImageCatalog_2;
+using ImageCatalog_2.Commands;
using ImageCatalog_2.Services;
using MaddoShared;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
namespace ImageCatalog;
@@ -25,8 +27,6 @@ public partial class MainForm
private readonly ILogger _logger;
- private readonly ImageCreationStuff _imageCreationService;
-
private readonly ParametriSetup _parametriSetup;
private readonly PicSettings _picSettings;
@@ -34,7 +34,6 @@ public partial class MainForm
ParametriSetup parametriSetup, ILogger logger)
{
Model = model;
- _imageCreationService = imageCreationStuff;
_parametriSetup = parametriSetup;
_picSettings = picSettings;
_logger = logger;
@@ -42,77 +41,37 @@ public partial class MainForm
_logger.LogDebug("Start");
InitializeComponent();
- BindControls();
- // Subscribe to DataModel events
+ // Set this form as the control for thread marshalling in the DataModel
+ Model.SetControl(this);
+
+ BindControls();
+
+ var version = Assembly.GetExecutingAssembly().GetName().Version;
+ _Label27.Text = $"Version: {version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
+ }
+
+ protected void BindControls()
+ {
+ // Bind buttons to ViewModel commands using command binding
+ _btnCreaCatalogoAsync.BindCommand(Model.ProcessImagesCommand);
+ button1.BindCommand(Model.ProcessImagesCommand);
+ _Button2.BindCommand(Model.SelectSourceFolderCommand);
+ _Button3.BindCommand(Model.SelectDestinationFolderCommand);
+ _Button4.BindCommand(Model.SelectLogoFileCommand);
+ _Button5.BindCommand(Model.SaveSettingsCommand);
+ _Button6.BindCommand(Model.LoadSettingsCommand);
+ _Button8.BindCommand(Model.SelectColorCommand);
+
+ // Subscribe to ViewModel events for UI dialogs (these need UI context)
Model.SelectSourceFolderRequested += OnSelectSourceFolderRequested;
Model.SelectDestinationFolderRequested += OnSelectDestinationFolderRequested;
Model.SelectLogoFileRequested += OnSelectLogoFileRequested;
Model.SaveSettingsRequested += OnSaveSettingsRequested;
Model.LoadSettingsRequested += OnLoadSettingsRequested;
Model.SelectColorRequested += OnSelectColorRequested;
-
- var version = Assembly.GetExecutingAssembly().GetName().Version;
- _Label27.Text = $"Version: {version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
-
- _results = [];
- UiUpdateEvent += OnUiUpdateEvent;
}
- protected void BindControls()
- {
- // Wire up buttons to ViewModel commands
- _Button2.Click += (s, e) => Model.SelectSourceFolderCommand.Execute(null);
- _Button3.Click += (s, e) => Model.SelectDestinationFolderCommand.Execute(null);
- _Button4.Click += (s, e) => Model.SelectLogoFileCommand.Execute(null);
- _Button5.Click += (s, e) => Model.SaveSettingsCommand.Execute(null);
- _Button6.Click += (s, e) => Model.LoadSettingsCommand.Execute(null);
- _Button8.Click += (s, e) => Model.SelectColorCommand.Execute(null);
- }
-
- private event EventHandler> UiUpdateEvent;
-
- delegate void SetTextCallback(Label target, string text);
-
- private void SetText(Label target, string text)
- {
- if (InvokeRequired)
- {
- var d = new SetTextCallback(SetText);
- this.Invoke(d, new object[] { target, text });
- }
- else
- {
- target.Text = text;
- }
- }
-
- private delegate void SetProgressCallback(ProgressBar target, int amount, int maximum);
-
- private void SetProgress(ProgressBar target, int amount, int maximum)
- {
- if (InvokeRequired)
- {
- var d = new SetProgressCallback(SetProgress);
- this.Invoke(d, new object[] { target, amount, maximum });
- }
- else
- {
- target.Maximum = maximum;
- target.Value = amount;
- }
- }
-
- private void OnUiUpdateEvent(object sender, Tuple args)
- {
- SetProgress(ProgressBar1, _results.Count, args.Item2);
- SetText(Label18, _results.Count.ToString());
- SetText(Label10, args.Item1);
- SetText(lblFotoTotaliNum, args.Item2.ToString());
- }
-
- private ConcurrentBag _results;
-
private void SetDefaults()
{
// Bind ComboBoxes to Model using proper data binding
@@ -135,6 +94,19 @@ public partial class MainForm
ComboBox5.DataSource = new List { "Alto", "Centro", "Basso" };
ComboBox5.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.LogoVerticalPosition),
false, DataSourceUpdateMode.OnPropertyChanged));
+
+ // Bind progress bar and status labels
+ ProgressBar1.DataBindings.Add(new Binding("Maximum", bindingSource1, nameof(Model.ProgressBarMaximum),
+ false, DataSourceUpdateMode.OnPropertyChanged));
+ ProgressBar1.DataBindings.Add(new Binding("Value", bindingSource1, nameof(Model.ProgressBarValue),
+ false, DataSourceUpdateMode.OnPropertyChanged));
+
+ Label18.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessedImagesCount),
+ false, DataSourceUpdateMode.OnPropertyChanged));
+ lblFotoTotaliNum.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.TotalImagesCount),
+ false, DataSourceUpdateMode.OnPropertyChanged));
+ Label10.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessingStatus),
+ false, DataSourceUpdateMode.OnPropertyChanged));
}
@@ -147,50 +119,6 @@ public partial class MainForm
_logger.LogInformation("Programma Avviato");
}
- private void FixPaths()
- {
- Model.SourcePath = FixPath(Model.SourcePath);
- Model.DestinationPath = FixPath(Model.DestinationPath);
- }
-
- private string FixPath(string path)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- return string.Empty;
- }
-
- // Trim leading/trailing quotes
- path = path.Trim().Trim('"');
-
- // Normalize directory separators
- path = path.Replace('/', Path.DirectorySeparatorChar)
- .Replace('\\', Path.DirectorySeparatorChar);
-
- // Remove trailing separators then add one back
- path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
-
- return path;
- }
-
- private void lockUI()
- {
- Model.UiEnabled = false;
- //TabControl1.Enabled = false;
- //Button5.Enabled = false;
- //Button6.Enabled = false;
- //btnCreaCatalogoAsync.Enabled = false;
- }
-
- private void unlockUI()
- {
- Model.UiEnabled = true;
- //TabControl1.Enabled = true;
- //Button5.Enabled = true;
- //Button6.Enabled = true;
- //btnCreaCatalogoAsync.Enabled = true;
- }
-
private string CalcTime(DateTime timeStart, DateTime timeStop, int numFoto)
{
long timediffH, timediffS;
@@ -224,6 +152,26 @@ public partial class MainForm
return FixPath(dialog.SelectedPath);
}
+ private string FixPath(string path)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ return string.Empty;
+ }
+
+ // Trim leading/trailing quotes
+ path = path.Trim().Trim('"');
+
+ // Normalize directory separators
+ path = path.Replace('/', Path.DirectorySeparatorChar)
+ .Replace('\\', Path.DirectorySeparatorChar);
+
+ // Remove trailing separators then add one back
+ path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
+
+ return path;
+ }
+
private void OnSelectSourceFolderRequested(object sender, EventArgs e)
{
var dialogResult = SelectFolder(Model.SourcePath);
@@ -356,76 +304,6 @@ public partial class MainForm
}
}
- private void SetPicSettings(string SourcePath, string DestPath)
- {
- _picSettings.DirectorySorgente = SourcePath;
- _picSettings.DirectoryDestinazione = DestPath;
-
- // Font and text settings from Model
- _picSettings.DimStandard = Model.FontSize;
- _picSettings.DimStandardMiniatura = Model.FontSizeThumbnail;
- _picSettings.IlFont = Model.FontName;
- _picSettings.Grassetto = Model.FontBold;
- _picSettings.Posizione = Model.VerticalPosition;
- _picSettings.Allineamento = Model.HorizontalAlignment;
- _picSettings.Trasparenza = Model.TextTransparency;
- _picSettings.Margine = Model.TextMargin;
- _picSettings.FontColoreRGB = ColorTranslator.FromHtml(Model.TextColorRGB);
-
- // Thumbnail settings from Model
- _picSettings.AltezzaSmall = Model.ThumbnailHeight;
- _picSettings.LarghezzaSmall = Model.ThumbnailWidth;
- _picSettings.Suffisso = Model.ThumbnailPrefix;
- _picSettings.CreaMiniature = Model.CreateThumbnails;
- _picSettings.JpegQualityMin = Model.JpegQualityThumbnail;
- _picSettings.DimMin = Model.FontSizeThumbnail;
-
- // Big photo settings from Model
- _picSettings.AltezzaBig = Model.PhotoBigHeight;
- _picSettings.LarghezzaBig = Model.PhotoBigWidth;
- _picSettings.FotoGrandeDimOrigina = Model.KeepOriginalDimensions;
- _picSettings.JpegQuality = Model.JpegQuality;
- _picSettings.Codice = Model.BigPhotoSuffix;
-
- // Logo settings from Model
- _picSettings.LogoAggiungi = Model.AddLogo;
- _picSettings.LogoNomeFile = Model.LogoFile;
- _picSettings.LogoAltezza = Model.LogoHeight;
- _picSettings.LogoLarghezza = Model.LogoWidth;
- _picSettings.LogoMargine = Model.LogoMargin.ToString();
- _picSettings.LogoTrasparenza = Model.LogoTransparency.ToString();
- _picSettings.LogoPosizioneH = Model.LogoHorizontalPosition;
- _picSettings.LogoPosizioneV = Model.LogoVerticalPosition;
-
- // Text content from Model
- _picSettings.TestoFirmaStart = Model.HorizontalText;
- _picSettings.TestoFirmaStartV = Model.VerticalText;
-
- // Vertical text settings from Model
- _picSettings.DimVert = Model.VerticalTextSize;
- _picSettings.MargVert = Model.VerticalTextMargin;
-
- // Boolean flags from Model
- _picSettings.UsaRotazioneAutomatica = Model.AutomaticRotation;
- _picSettings.UsaForzaJpg = Model.ForceJpeg;
- _picSettings.TestoNome = Model.ShowPhotoNumber;
- _picSettings.NomeData = Model.ShowDate;
- _picSettings.UsaOrarioTestoApplicare = Model.AddTime;
- _picSettings.UsaTempoGaraTestoApplicare = Model.AddRaceTime;
- _picSettings.OverwriteFiles = Model.OverwriteImages;
-
- // Additional settings from Model
- _picSettings.UsaOrarioMiniatura = Model.AddTimeToThumbnails;
- _picSettings.DataPartenza = Model.RaceStartDate;
- _picSettings.TestoOrario = Model.TimeLabel;
- _picSettings.TestoMin = Model.ShowFileNameOnThumbnails;
-
- // Thumbnail text options from Model
- _picSettings.AggiungiScritteMiniature = Model.AddTextToThumbnails;
- _picSettings.AggTempoGaraMin = Model.AddRaceTimeToThumbnails;
- _picSettings.AggNumTempMin = Model.AddNumberAndTimeToThumbnails;
- }
-
private void setLabel18Text(string text)
{
if (Label18.InvokeRequired)
@@ -437,99 +315,6 @@ public partial class MainForm
Label18.Text = text;
}
}
-
- private NumerazioneType GetNumerazioneEnum()
- {
- NumerazioneType numerazioneType;
- if (rdbNumProgressiva.Checked)
- {
- numerazioneType = NumerazioneType.Progressiva;
- }
- else
- {
- numerazioneType = NumerazioneType.Files;
- }
-
- return numerazioneType;
- }
-
-
- private async void Button1_Click(object sender, EventArgs e)
- {
- _logger.LogInformation("Avvio elaborazione...");
- lockUI();
-
- Model.MainToken?.Dispose();
- Model.MainToken = new CancellationTokenSource();
- var token = Model.MainToken.Token;
-
- // timeStart = TimeOfDay
- FixPaths();
- Label10.Text = "Elaborazione in corso...";
- lblFotoTotaliNum.Text = "0";
- Label18.Text = "0";
- Model.SpeedCounter = "-f/m";
- SetPicSettings(Model.SourcePath, Model.DestinationPath);
- ProgressBar1.Minimum = 0;
- ProgressBar1.Step = 1;
- ProgressBar1.Value = 0;
-
- var imageCreationOptions = new ImageCreationStuff.Options
- {
- AggiornaSottodirectory = chkAggiornaSottodirectory.Checked,
- CreaSottocartelle = chkCreaSottocartelle.Checked,
- FilePerCartella = int.Parse(txtFilePerCartella.Text),
- SuffissoCartelle = txtSuffissoCartelle.Text,
- CifreContatore = int.Parse(txtCifreContatore.Text),
- NumerazioneType = GetNumerazioneEnum(),
- SourcePath = Model.SourcePath,
- DestinationPath = Model.DestinationPath,
- MaxThreads = Model.ThreadsCount,
- ChunksSize = Model.ChunkSize,
- LinearExecution = rdbVecchioMetodo.Checked
- };
-
- try
- {
- _results = [];
- _currentAmount = 0;
- _previousAmount = 0;
- timer1.Tick += Timer1OnTick;
-
- timer1.Interval = 1000 * 60;
- timer1.Enabled = true;
-
- var time =
- await _imageCreationService.CreaCatalogoParallel(imageCreationOptions, _results, UiUpdateEvent, token);
- Model.SpeedCounter = time;
- timer1.Enabled = false;
- }
- catch (OperationCanceledException operationCanceledException)
- {
- _logger.LogInformation("Operazione Cancellata");
- }
- finally
- {
- Model.MainToken?.Dispose();
- Model.MainToken = null;
-
- timer1.Tick -= Timer1OnTick;
- }
-
- Label10.Text = "Finito";
- unlockUI();
- }
-
- private int _currentAmount = 0;
- private int _previousAmount = 0;
-
- private void Timer1OnTick(object sender, EventArgs e)
- {
- _previousAmount = _currentAmount;
- _currentAmount = _results.Count;
- int diff = _currentAmount - _previousAmount;
- Model.SpeedCounter = $"{diff} f/m";
- }
}
public class PicInfo
diff --git a/imagecatalog/Mappings/DataModelMappingProfile.cs b/imagecatalog/Mappings/DataModelMappingProfile.cs
new file mode 100644
index 0000000..fd33654
--- /dev/null
+++ b/imagecatalog/Mappings/DataModelMappingProfile.cs
@@ -0,0 +1,94 @@
+using System.Drawing;
+using AutoMapper;
+using MaddoShared;
+
+namespace ImageCatalog_2.Mappings;
+
+///
+/// AutoMapper profile for mapping between DataModel and PicSettings
+///
+public class DataModelMappingProfile : Profile
+{
+ public DataModelMappingProfile()
+ {
+ CreateMap()
+ // Paths
+ .ForMember(dest => dest.DirectorySorgente, opt => opt.MapFrom(src => src.SourcePath))
+ .ForMember(dest => dest.DirectoryDestinazione, opt => opt.MapFrom(src => src.DestinationPath))
+
+ // Font and text settings
+ .ForMember(dest => dest.DimStandard, opt => opt.MapFrom(src => src.FontSize))
+ .ForMember(dest => dest.DimStandardMiniatura, opt => opt.MapFrom(src => src.FontSizeThumbnail))
+ .ForMember(dest => dest.IlFont, opt => opt.MapFrom(src => src.FontName))
+ .ForMember(dest => dest.Grassetto, opt => opt.MapFrom(src => src.FontBold))
+ .ForMember(dest => dest.Posizione, opt => opt.MapFrom(src => src.VerticalPosition))
+ .ForMember(dest => dest.Allineamento, opt => opt.MapFrom(src => src.HorizontalAlignment))
+ .ForMember(dest => dest.Trasparenza, opt => opt.MapFrom(src => src.TextTransparency))
+ .ForMember(dest => dest.Margine, opt => opt.MapFrom(src => src.TextMargin))
+ .ForMember(dest => dest.FontColoreRGB, opt => opt.MapFrom(src => ColorTranslator.FromHtml(src.TextColorRGB)))
+
+ // Thumbnail settings
+ .ForMember(dest => dest.AltezzaSmall, opt => opt.MapFrom(src => src.ThumbnailHeight))
+ .ForMember(dest => dest.LarghezzaSmall, opt => opt.MapFrom(src => src.ThumbnailWidth))
+ .ForMember(dest => dest.Suffisso, opt => opt.MapFrom(src => src.ThumbnailPrefix))
+ .ForMember(dest => dest.CreaMiniature, opt => opt.MapFrom(src => src.CreateThumbnails))
+ .ForMember(dest => dest.JpegQualityMin, opt => opt.MapFrom(src => src.JpegQualityThumbnail))
+ .ForMember(dest => dest.DimMin, opt => opt.MapFrom(src => src.FontSizeThumbnail))
+
+ // Big photo settings
+ .ForMember(dest => dest.AltezzaBig, opt => opt.MapFrom(src => src.PhotoBigHeight))
+ .ForMember(dest => dest.LarghezzaBig, opt => opt.MapFrom(src => src.PhotoBigWidth))
+ .ForMember(dest => dest.FotoGrandeDimOrigina, opt => opt.MapFrom(src => src.KeepOriginalDimensions))
+ .ForMember(dest => dest.JpegQuality, opt => opt.MapFrom(src => src.JpegQuality))
+ .ForMember(dest => dest.Codice, opt => opt.MapFrom(src => src.BigPhotoSuffix))
+
+ // Logo settings
+ .ForMember(dest => dest.LogoAggiungi, opt => opt.MapFrom(src => src.AddLogo))
+ .ForMember(dest => dest.LogoNomeFile, opt => opt.MapFrom(src => src.LogoFile))
+ .ForMember(dest => dest.LogoAltezza, opt => opt.MapFrom(src => src.LogoHeight))
+ .ForMember(dest => dest.LogoLarghezza, opt => opt.MapFrom(src => src.LogoWidth))
+ .ForMember(dest => dest.LogoMargine, opt => opt.MapFrom(src => src.LogoMargin.ToString()))
+ .ForMember(dest => dest.LogoTrasparenza, opt => opt.MapFrom(src => src.LogoTransparency.ToString()))
+ .ForMember(dest => dest.LogoPosizioneH, opt => opt.MapFrom(src => src.LogoHorizontalPosition))
+ .ForMember(dest => dest.LogoPosizioneV, opt => opt.MapFrom(src => src.LogoVerticalPosition))
+
+ // Text content
+ .ForMember(dest => dest.TestoFirmaStart, opt => opt.MapFrom(src => src.HorizontalText))
+ .ForMember(dest => dest.TestoFirmaStartV, opt => opt.MapFrom(src => src.VerticalText))
+
+ // Vertical text settings
+ .ForMember(dest => dest.DimVert, opt => opt.MapFrom(src => src.VerticalTextSize))
+ .ForMember(dest => dest.MargVert, opt => opt.MapFrom(src => src.VerticalTextMargin))
+
+ // Boolean flags
+ .ForMember(dest => dest.UsaRotazioneAutomatica, opt => opt.MapFrom(src => src.AutomaticRotation))
+ .ForMember(dest => dest.UsaForzaJpg, opt => opt.MapFrom(src => src.ForceJpeg))
+ .ForMember(dest => dest.TestoNome, opt => opt.MapFrom(src => src.ShowPhotoNumber))
+ .ForMember(dest => dest.NomeData, opt => opt.MapFrom(src => src.ShowDate))
+ .ForMember(dest => dest.UsaOrarioTestoApplicare, opt => opt.MapFrom(src => src.AddTime))
+ .ForMember(dest => dest.UsaTempoGaraTestoApplicare, opt => opt.MapFrom(src => src.AddRaceTime))
+ .ForMember(dest => dest.OverwriteFiles, opt => opt.MapFrom(src => src.OverwriteImages))
+
+ // Additional settings
+ .ForMember(dest => dest.UsaOrarioMiniatura, opt => opt.MapFrom(src => src.AddTimeToThumbnails))
+ .ForMember(dest => dest.DataPartenza, opt => opt.MapFrom(src => src.RaceStartDate))
+ .ForMember(dest => dest.TestoOrario, opt => opt.MapFrom(src => src.TimeLabel))
+ .ForMember(dest => dest.TestoMin, opt => opt.MapFrom(src => src.ShowFileNameOnThumbnails))
+
+ // Thumbnail text options
+ .ForMember(dest => dest.AggiungiScritteMiniature, opt => opt.MapFrom(src => src.AddTextToThumbnails))
+ .ForMember(dest => dest.AggTempoGaraMin, opt => opt.MapFrom(src => src.AddRaceTimeToThumbnails))
+ .ForMember(dest => dest.AggNumTempMin, opt => opt.MapFrom(src => src.AddNumberAndTimeToThumbnails))
+
+ // Ignore unmapped properties
+ .ForMember(dest => dest.DestDir, opt => opt.Ignore())
+ .ForMember(dest => dest.SecretDefault, opt => opt.Ignore())
+ .ForMember(dest => dest.SecretBig, opt => opt.Ignore())
+ .ForMember(dest => dest.SecretSmall, opt => opt.Ignore())
+ .ForMember(dest => dest.SecretPathSmall, opt => opt.Ignore())
+ .ForMember(dest => dest.SecretPathBig, opt => opt.Ignore())
+ .ForMember(dest => dest.FotoRuotaADestra, opt => opt.Ignore())
+ .ForMember(dest => dest.FotoRuotaASinistra, opt => opt.Ignore())
+ .ForMember(dest => dest.TempMinText, opt => opt.Ignore());
+ }
+}
diff --git a/imagecatalog/Program.cs b/imagecatalog/Program.cs
index 24ac1ab..2a5e2b2 100644
--- a/imagecatalog/Program.cs
+++ b/imagecatalog/Program.cs
@@ -3,6 +3,7 @@ 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;
@@ -79,6 +80,9 @@ static class Program
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();
services.AddTransient();
diff --git a/imagecatalog/ViewModelBase.cs b/imagecatalog/ViewModelBase.cs
index bedbb78..9807106 100644
--- a/imagecatalog/ViewModelBase.cs
+++ b/imagecatalog/ViewModelBase.cs
@@ -4,20 +4,66 @@ using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Forms;
namespace ImageCatalog_2
{
public class ViewModelBase : INotifyPropertyChanged
{
+ private readonly SynchronizationContext? _synchronizationContext;
+ private Control? _control;
+
+ protected ViewModelBase()
+ {
+ // Capture the synchronization context (UI thread context)
+ _synchronizationContext = SynchronizationContext.Current;
+ }
+
+ ///
+ /// Set a Control to use for thread marshalling in WinForms applications.
+ /// This is required for proper cross-thread handling with data binding.
+ ///
+ public void SetControl(Control control)
+ {
+ _control = control;
+ }
+
public event PropertyChangedEventHandler? PropertyChanged;
+
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
- if (PropertyChanged != null)
+ if (PropertyChanged == null)
+ return;
+
+ // If we have a Control reference (WinForms), use Control.Invoke for proper marshalling
+ if (_control != null)
{
+ if (_control.InvokeRequired)
+ {
+ _control.Invoke(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
+ }
+ else
+ {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+ // Fallback to SynchronizationContext if available
+ else if (_synchronizationContext != null && SynchronizationContext.Current != _synchronizationContext)
+ {
+ // We're on a different thread, marshal to the UI thread
+ _synchronizationContext.Send(_ =>
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }, null);
+ }
+ else
+ {
+ // We're already on the UI thread or no sync context available
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}