Introduce UI option to choose between System.Graphics and ImageSharp for image processing. Update DataModel and MainForm for robust binding and synchronization. Rewrite ImageCreatorAlternate to use ImageSharp for core operations and GDI+ for overlays. Remove test buttons, add radio group for library selection. Update project dependencies to support new features and modernize image handling.
566 lines
No EOL
21 KiB
C#
566 lines
No EOL
21 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Text;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
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;
|
|
|
|
namespace ImageCatalog;
|
|
|
|
public partial class MainForm
|
|
{
|
|
private readonly DataModel Model;
|
|
|
|
private readonly ILogger<MainForm> _logger;
|
|
|
|
private readonly ParametriSetup _parametriSetup;
|
|
private readonly PicSettings _picSettings;
|
|
// Prevent re-entrant updates between UI events and model PropertyChanged handling
|
|
private bool _suppressRadioUpdates = false;
|
|
|
|
public MainForm(DataModel model, ImageCreationStuff imageCreationStuff, PicSettings picSettings,
|
|
ParametriSetup parametriSetup, ILogger<MainForm> logger)
|
|
{
|
|
Model = model;
|
|
_parametriSetup = parametriSetup;
|
|
_picSettings = picSettings;
|
|
_logger = logger;
|
|
|
|
_logger.LogDebug("Start");
|
|
|
|
InitializeComponent();
|
|
// Set this form as the control for thread marshalling in the DataModel
|
|
Model.SetControl(this);
|
|
|
|
// Ensure the designer data bindings have a concrete DataSource immediately so
|
|
// that UI controls (radio buttons) reflect the ViewModel state and propagate
|
|
// user changes back to the ViewModel.
|
|
bindingSource1.DataSource = Model;
|
|
|
|
BindControls();
|
|
|
|
// The designer originally bound the radio buttons to boolean helpers on the ViewModel.
|
|
// Those two separate bindings can fight with each other. Clear designer bindings and
|
|
// wire explicit Click handlers that update the single authoritative property
|
|
// `Model.ImageLibrary`. Also keep a PropertyChanged listener to reflect external
|
|
// changes back into the radio buttons.
|
|
rdbLibrary1.DataBindings.Clear();
|
|
rdbLibrary2.DataBindings.Clear();
|
|
|
|
// Initialize radio state from model
|
|
rdbLibrary1.Checked = Model.UseSystemGraphics;
|
|
rdbLibrary2.Checked = Model.UseImageSharp;
|
|
|
|
// Use Click handlers (not CheckedChanged) to avoid competing binding updates
|
|
rdbLibrary1.Click += (_, _) =>
|
|
{
|
|
if (_suppressRadioUpdates) return;
|
|
if (Model.ImageLibrary != "System.Graphics")
|
|
Model.ImageLibrary = "System.Graphics";
|
|
};
|
|
rdbLibrary2.Click += (_, _) =>
|
|
{
|
|
if (_suppressRadioUpdates) return;
|
|
if (Model.ImageLibrary != "ImageSharp")
|
|
Model.ImageLibrary = "ImageSharp";
|
|
};
|
|
|
|
// Watch for model changes so we can reflect external updates
|
|
Model.PropertyChanged += Model_PropertyChanged;
|
|
|
|
// Save user preferences on form close instead of immediately when dialogs are used
|
|
this.FormClosing += MainForm_FormClosing;
|
|
|
|
// Wire up 'Open folder in Explorer' buttons
|
|
btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click;
|
|
btnOpenDestFolder.Click += BtnOpenDestFolder_Click;
|
|
|
|
// Version label is data-bound to DataModel.AppVersion; DataModel is populated with the version via DI
|
|
}
|
|
|
|
private void RdbLibrary_CheckedChanged(object? sender, EventArgs e)
|
|
{
|
|
// Keep behavior simple: when a radio button becomes checked, update the ViewModel
|
|
// so that the designer binding and PicSettings stay in sync.
|
|
if (sender is RadioButton rb && rb.Checked)
|
|
{
|
|
_logger?.LogDebug("Radio library changed: {RadioName}", rb.Name);
|
|
if (rb == rdbLibrary2)
|
|
{
|
|
Model.ImageLibrary = "ImageSharp";
|
|
}
|
|
else if (rb == rdbLibrary1)
|
|
{
|
|
Model.ImageLibrary = "System.Graphics";
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Model_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName is null) return;
|
|
if (e.PropertyName == nameof(Model.ImageLibrary) || e.PropertyName == nameof(Model.UseImageSharp) || e.PropertyName == nameof(Model.UseSystemGraphics))
|
|
{
|
|
_logger?.LogDebug("Model property changed: {Property} => ImageLibrary={ImageLibrary}, PicSettings.Provider={Provider}", e.PropertyName, Model.ImageLibrary, _picSettings.ImageCreatorProvider);
|
|
|
|
// Reflect authoritative model value into the radio buttons in a thread-safe, re-entrancy-safe way
|
|
try
|
|
{
|
|
_suppressRadioUpdates = true;
|
|
rdbLibrary1.Checked = Model.UseSystemGraphics;
|
|
rdbLibrary2.Checked = Model.UseImageSharp;
|
|
}
|
|
finally
|
|
{
|
|
_suppressRadioUpdates = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void BindControls()
|
|
{
|
|
// Bind buttons to ViewModel commands using command binding
|
|
_btnCreaCatalogoAsync.BindCommand(Model.ProcessImagesCommand);
|
|
// Note: `button1` control does not exist in the designer. Use the primary create button only.
|
|
_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;
|
|
// Show message requests (from ViewModel validation)
|
|
Model.ShowMessageRequested += OnShowMessageRequested;
|
|
}
|
|
|
|
private void OnShowMessageRequested(object? sender, Tuple<string, string, MessageBoxIcon> args)
|
|
{
|
|
if (args is null) return;
|
|
// Ensure call on UI thread
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new Action(() => OnShowMessageRequested(sender, args)));
|
|
return;
|
|
}
|
|
|
|
MessageBox.Show(this, args.Item1, args.Item2, MessageBoxButtons.OK, args.Item3);
|
|
}
|
|
|
|
private void SetDefaults()
|
|
{
|
|
// Bind ComboBoxes to Model using proper data binding
|
|
ComboBox1.DataSource = new List<string>(Model.VerticalPositions);
|
|
ComboBox1.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.VerticalPosition),
|
|
false, DataSourceUpdateMode.OnPropertyChanged));
|
|
|
|
ComboBox2.DataSource = new List<string>(Model.HorizontalAlignments);
|
|
ComboBox2.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.HorizontalAlignment),
|
|
false, DataSourceUpdateMode.OnPropertyChanged));
|
|
|
|
ComboBox3.DataSource = new List<string>(Model.AvailableFonts);
|
|
ComboBox3.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.FontName),
|
|
false, DataSourceUpdateMode.OnPropertyChanged));
|
|
|
|
ComboBox4.DataSource = new List<string>(Model.HorizontalAlignments);
|
|
ComboBox4.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.LogoHorizontalPosition),
|
|
false, DataSourceUpdateMode.OnPropertyChanged));
|
|
|
|
ComboBox5.DataSource = new List<string> { "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));
|
|
}
|
|
|
|
|
|
private void Form1_Load(object sender, EventArgs e)
|
|
{
|
|
bindingSource1.DataSource = Model;
|
|
Application.EnableVisualStyles();
|
|
SetDefaults();
|
|
|
|
_logger.LogInformation("Programma Avviato");
|
|
}
|
|
|
|
private string CalcTime(DateTime timeStart, DateTime timeStop, int numFoto)
|
|
{
|
|
long timediffH, timediffS;
|
|
long timediffM;
|
|
|
|
TimeSpan timeDiff = timeStop - timeStart;
|
|
|
|
timediffM = (int)timeDiff.TotalMinutes;
|
|
timediffS = (int)timeDiff.TotalSeconds;
|
|
timediffH = (int)timeDiff.TotalHours;
|
|
|
|
// timediffM = DateAndTime.DateDiff(DateInterval.Minute, timeStart, timeStop);
|
|
// timediffS = DateAndTime.DateDiff(DateInterval.Second, timeStart, timeStop);
|
|
// timediffH = DateAndTime.DateDiff(DateInterval.Hour, timeStart, timeStop);
|
|
|
|
double fotoSec = numFoto / (double)timediffS;
|
|
double fotoMin = numFoto / (double)timediffM;
|
|
double fotoOra = numFoto / (double)timediffH;
|
|
string s = "S: " + timediffS.ToString() + "; F/s: " +
|
|
fotoSec.ToString(
|
|
"0.000"); // + " F/m: " + fotoMin.ToString("0.00") + " F/h: " + fotoOra.ToString("0.00")
|
|
return s;
|
|
}
|
|
|
|
private string SelectFolder(string startingFolder)
|
|
{
|
|
var dialog = new FolderBrowserDialog();
|
|
dialog.InitialDirectory = startingFolder;
|
|
if (dialog.ShowDialog() != DialogResult.OK) return string.Empty;
|
|
|
|
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)
|
|
{
|
|
// Prefer model value; if empty fall back to last-used value stored in user prefs
|
|
var starting = !string.IsNullOrWhiteSpace(Model.SourcePath)
|
|
? Model.SourcePath
|
|
: _parametriSetup.LeggiParametroString("LastSourceFolder");
|
|
|
|
var dialogResult = SelectFolder(starting);
|
|
if (!string.IsNullOrWhiteSpace(dialogResult))
|
|
{
|
|
Model.SourcePath = dialogResult;
|
|
_parametriSetup.AggiornaParametro("LastSourceFolder", dialogResult);
|
|
_parametriSetup.SalvaParametriSetup();
|
|
}
|
|
}
|
|
|
|
private void BtnOpenSourceFolder_Click(object? sender, EventArgs e)
|
|
{
|
|
// Prefer the model value but fall back to the textbox if needed
|
|
var path = string.IsNullOrWhiteSpace(Model.SourcePath) ? txtSorgente.Text : Model.SourcePath;
|
|
OpenFolder(path);
|
|
}
|
|
|
|
private void BtnOpenDestFolder_Click(object? sender, EventArgs e)
|
|
{
|
|
var path = string.IsNullOrWhiteSpace(Model.DestinationPath) ? txtDestinazione.Text : Model.DestinationPath;
|
|
OpenFolder(path);
|
|
}
|
|
|
|
private void OpenFolder(string? path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
MessageBox.Show(this, "Folder path is empty.", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
path = path.Trim().Trim('"');
|
|
|
|
try
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
// If a file was provided, open its folder and select it
|
|
Process.Start("explorer.exe", $"/select,\"{path}\"");
|
|
return;
|
|
}
|
|
|
|
if (Directory.Exists(path))
|
|
{
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = path,
|
|
UseShellExecute = true
|
|
});
|
|
return;
|
|
}
|
|
|
|
MessageBox.Show(this, $"Folder does not exist: {path}", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger?.LogError(ex, "Failed to open folder {Path}", path);
|
|
MessageBox.Show(this, $"Failed to open folder: {ex.Message}", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private void OnSelectDestinationFolderRequested(object sender, EventArgs e)
|
|
{
|
|
var starting = !string.IsNullOrWhiteSpace(Model.DestinationPath)
|
|
? Model.DestinationPath
|
|
: _parametriSetup.LeggiParametroString("LastDestinationFolder");
|
|
|
|
var dialogResult = SelectFolder(starting);
|
|
if (!string.IsNullOrWhiteSpace(dialogResult))
|
|
{
|
|
Model.DestinationPath = dialogResult;
|
|
_parametriSetup.AggiornaParametro("LastDestinationFolder", dialogResult);
|
|
_parametriSetup.SalvaParametriSetup();
|
|
}
|
|
}
|
|
|
|
private void OnSelectLogoFileRequested(object sender, EventArgs e)
|
|
{
|
|
var dialog = new OpenFileDialog();
|
|
dialog.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif";
|
|
if (!string.IsNullOrWhiteSpace(Model.LogoFile))
|
|
{
|
|
dialog.FileName = Model.LogoFile;
|
|
}
|
|
else
|
|
{
|
|
var lastLogoFolder = _parametriSetup.LeggiParametroString("LastLogoFolder");
|
|
if (!string.IsNullOrWhiteSpace(lastLogoFolder) && Directory.Exists(lastLogoFolder))
|
|
{
|
|
dialog.InitialDirectory = lastLogoFolder;
|
|
}
|
|
}
|
|
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
Model.LogoFile = dialog.FileName;
|
|
UpdateLogoPictureBox(Model.LogoFile);
|
|
try
|
|
{
|
|
var folder = Path.GetDirectoryName(dialog.FileName) ?? string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(folder))
|
|
{
|
|
_parametriSetup.AggiornaParametro("LastLogoFolder", folder);
|
|
_parametriSetup.SalvaParametriSetup();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore preferences save failures
|
|
}
|
|
}
|
|
}
|
|
|
|
private async void OnSaveSettingsRequested(object sender, string e)
|
|
{
|
|
var saveDialog = new SaveFileDialog
|
|
{
|
|
Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*",
|
|
FilterIndex = 0,
|
|
RestoreDirectory = true
|
|
};
|
|
var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
|
|
if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
|
|
saveDialog.InitialDirectory = lastSettings;
|
|
|
|
if (saveDialog.ShowDialog() != DialogResult.OK) return;
|
|
|
|
await Model.SaveSettingsToFileAsync(saveDialog.FileName);
|
|
Text = "Image Catalog - " + Path.GetFileName(saveDialog.FileName);
|
|
|
|
try
|
|
{
|
|
var folder = Path.GetDirectoryName(saveDialog.FileName) ?? string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(folder))
|
|
{
|
|
_parametriSetup.AggiornaParametro("LastSettingsFolder", folder);
|
|
_parametriSetup.SalvaParametriSetup();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
private async void OnLoadSettingsRequested(object sender, string e)
|
|
{
|
|
var openDialog = new OpenFileDialog
|
|
{
|
|
Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*",
|
|
FilterIndex = 0,
|
|
RestoreDirectory = true
|
|
};
|
|
|
|
var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
|
|
if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
|
|
openDialog.InitialDirectory = lastSettings;
|
|
|
|
if (openDialog.ShowDialog() != DialogResult.OK) return;
|
|
|
|
try
|
|
{
|
|
await Model.LoadSettingsFromFileAsync(openDialog.FileName);
|
|
|
|
// Explicitly ensure UI is enabled after loading
|
|
Model.UiEnabled = true;
|
|
|
|
// Update logo preview if logo file exists
|
|
if (File.Exists(Model.LogoFile))
|
|
{
|
|
UpdateLogoPictureBox(Model.LogoFile);
|
|
}
|
|
|
|
Text = "Image Catalog - " + Path.GetFileName(openDialog.FileName);
|
|
|
|
try
|
|
{
|
|
var folder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(folder))
|
|
{
|
|
_parametriSetup.AggiornaParametro("LastSettingsFolder", folder);
|
|
_parametriSetup.SalvaParametriSetup();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore preferences save failures
|
|
}
|
|
|
|
_logger.LogInformation($"Settings loaded successfully from {openDialog.FileName}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error loading settings");
|
|
MessageBox.Show($"Error loading settings: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private void MainForm_FormClosing(object? sender, FormClosingEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// Persist last-used dialogs paths (user preferences)
|
|
// These keys are managed independently from settings files
|
|
// and must be saved when the form closes.
|
|
_parametriSetup.AggiornaParametro("LastSourceFolder", Model.SourcePath ?? string.Empty);
|
|
_parametriSetup.AggiornaParametro("LastDestinationFolder", Model.DestinationPath ?? string.Empty);
|
|
_parametriSetup.AggiornaParametro("LastLogoFolder", Path.GetDirectoryName(Model.LogoFile ?? string.Empty) ?? string.Empty);
|
|
_parametriSetup.SalvaParametriSetup();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger?.LogWarning(ex, "Failed to save user preferences on exit");
|
|
}
|
|
}
|
|
|
|
private void OnSelectColorRequested(object sender, EventArgs e)
|
|
{
|
|
var colorDialog = new ColorDialog
|
|
{
|
|
AllowFullOpen = true
|
|
};
|
|
|
|
if (!string.IsNullOrWhiteSpace(Model.TextColorRGB))
|
|
{
|
|
try
|
|
{
|
|
colorDialog.Color = ColorTranslator.FromHtml(Model.TextColorRGB);
|
|
}
|
|
catch
|
|
{
|
|
// Invalid color, use default
|
|
}
|
|
}
|
|
|
|
if (colorDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
Model.TextColorRGB = ColorTranslator.ToHtml(colorDialog.Color);
|
|
TextBox34.BackColor = colorDialog.Color;
|
|
}
|
|
}
|
|
|
|
private void UpdateLogoPictureBox(string logoPath)
|
|
{
|
|
try
|
|
{
|
|
PictureBox1.Image = Image.FromFile(logoPath);
|
|
if (PictureBox1.Image.Height >= PictureBox1.Image.Width)
|
|
{
|
|
PictureBox1.Height = 160;
|
|
PictureBox1.Width = (int)(160 * PictureBox1.Image.Width / (double)PictureBox1.Image.Height);
|
|
}
|
|
else
|
|
{
|
|
PictureBox1.Width = 160;
|
|
PictureBox1.Height = (int)(160 * PictureBox1.Image.Height / (double)PictureBox1.Image.Width);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Image loading failed, ignore
|
|
}
|
|
}
|
|
|
|
private void setLabel18Text(string text)
|
|
{
|
|
if (Label18.InvokeRequired)
|
|
{
|
|
Label18.Invoke(new Action<string>(setLabel18Text), text);
|
|
}
|
|
else
|
|
{
|
|
Label18.Text = text;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PicInfo
|
|
{
|
|
public DirectoryInfo DirSource, DirDest, DirDestStart;
|
|
public string NomeImmagine;
|
|
|
|
public PicInfo(DirectoryInfo Dir_Source, DirectoryInfo Dir_Dest, DirectoryInfo Dir_DestStart,
|
|
string Nome_Immagine)
|
|
{
|
|
DirSource = Dir_Source;
|
|
DirDest = Dir_Dest;
|
|
DirDestStart = Dir_DestStart;
|
|
NomeImmagine = Nome_Immagine;
|
|
}
|
|
} |