BackgroundBuilder/ViewModels/MainWindowViewModel.cs
Giuliano Paschoalino 2636848f6d Adiciona funcionalidade de upload de imagens PNG
Foi adicionada a funcionalidade de upload de imagens PNG para um
local de rede específico. No backend, a interface `IImageService`
e o serviço `ImageService` foram atualizados com o método
assíncrono `MoveFile`, que move arquivos para o destino desejado,
criando diretórios se necessário e exibindo mensagens de erro em
caso de falha.

No frontend, foi adicionado um botão "Upload" à interface gráfica
(`MainWindow.xaml`), vinculado ao comando `UploadImageCommand` no
`MainWindowViewModel`. Este comando utiliza o método privado
`UploadImageAsync` para abrir uma caixa de diálogo, permitir a
seleção de um arquivo PNG e mover o arquivo utilizando o serviço
de imagens. Feedback ao usuário é exibido para sucesso ou erro.
2025-08-28 15:24:44 -03:00

346 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using BackgroundBuilder.Models;
using BackgroundBuilder.Repositories;
using BackgroundBuilder.Services;
using BackgroundBuilder.Utils;
using ClosedXML.Excel;
using Microsoft.Win32;
namespace BackgroundBuilder.ViewModels
{
public class MainWindowViewModel : ObservableObject
{
private readonly IContatoRepository _repo;
private readonly IImageService _imageService;
private readonly ITaskbarService _taskbarService;
public ObservableCollection<Contato> RawContatos { get; } = [];
public ObservableCollection<Contato> Comando { get; } = [];
public ObservableCollection<Contato> Aniversarios { get; } = [];
public ObservableCollection<Contato> ContatosSemCMDFirstHalf { get; } = [];
public ObservableCollection<Contato> ContatosSemCMDSecondHalf { get; } = [];
private Contato? _selectedContato;
public Contato? SelectedContato{ get => _selectedContato; set { _selectedContato = value; OnPropertyChanged(); DeleteCommand.RaiseCanExecuteChanged(); } }
private ObservableCollection<Contato>? _selectedContatos;
public ObservableCollection<Contato> SelectedContatos { get => _selectedContatos ?? RawContatos; set { _selectedContatos = value; OnPropertyChanged(); UpdateCommand.RaiseCanExecuteChanged(); } }
private string? _caminhoBGImage;
public string? CaminhoBGImage{ get => _caminhoBGImage; set { _caminhoBGImage = value; OnPropertyChanged(); }}
private BitmapImage? _backgroundImage;
public BitmapImage? BackgroundImage{ get => _backgroundImage; private set { _backgroundImage = value; OnPropertyChanged(); }}
private int _taskbarSize;
public int TaskbarSize{ get => _taskbarSize; private set { _taskbarSize = value; OnPropertyChanged(); }}
private string _orientation = "Horizontal";
public string Orientation{ get => _orientation; private set { _orientation = value; OnPropertyChanged(); }}
public FrameworkElement? OverlayElement { get; set; }
// Commands
public RelayCommand DeleteCommand { get; }
public RelayCommand AddCommand { get; }
public RelayCommand SelectImageCommand { get; }
public ICommand RefreshCommand { get; }
public RelayCommand UpdateCommand { get; }
public RelayCommand ExportImageCommand { get; }
public RelayCommand ImportExcelCommand { get; }
public RelayCommand UploadImageCommand { get; }
public MainWindowViewModel(
IContatoRepository repo,
IImageService imageService,
ITaskbarService taskbarService)
{
_repo = repo;
_imageService = imageService;
_taskbarService = taskbarService;
RefreshTaskbarInfo();
DeleteCommand = new RelayCommand(async _ => await DeleteAsync(), _ => SelectedContato != null);
AddCommand = new RelayCommand(async _ => await AddContactAsync());
SelectImageCommand = new RelayCommand(async _ => await SelectBackground());
RefreshCommand = new RelayCommand(async _ => await LoadRawAsync());
UpdateCommand = new RelayCommand(async _ => await UpdateAsync(), _ => SelectedContatos != null);
ExportImageCommand = new RelayCommand(async _ => await RenderImageAsync(), _ => BackgroundImage != null && OverlayElement != null);
ImportExcelCommand = new RelayCommand(async _ => await ImportExcelAsync());
UploadImageCommand = new RelayCommand(async _ => await UploadImageAsync());
}
public void RefreshTaskbarInfo()
{
TaskbarSize = _taskbarService.GetTaskbarSize();
Orientation = _taskbarService.IsHorizontal() ? "Horizontal" : "Vertical";
}
private async Task DeleteAsync()
{
if (SelectedContato == null) return;
var result = MessageBox.Show(
"Delete selected record?",
"Confirm Delete",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result != MessageBoxResult.Yes) return;
try
{
await _repo.DeleteAsync(SelectedContato.Ramal);
RawContatos.Remove(SelectedContato);
ApplyFilters();
}
catch
{
MessageBox.Show(
"Error deleting record.",
"Delete Failed",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
private async Task AddContactAsync()
{
var novo = new Contato { Ramal = "", Nome = "" };
RawContatos.Add(novo);
SelectedContato = novo; // Set the newly added contact as the selected one
await Task.CompletedTask; // preserve async signature
}
private async Task SelectBackground()
{
var dlg = new OpenFileDialog
{
Filter = "Image Files|*.png;*.jpg;*.jpeg;*.bmp"
};
if (dlg.ShowDialog() == true)
{
CaminhoBGImage = dlg.FileName;
BackgroundImage = await _imageService.LoadAsync(dlg.FileName);
if (!string.IsNullOrEmpty(CaminhoBGImage))
{
ExportImageCommand.RaiseCanExecuteChanged();
}
}
}
public async Task LoadRawAsync()
{
var all = await _repo.GetAllAsync();
RawContatos.Clear();
foreach (var c in all.Where(x => x.IsComando)) RawContatos.Add(c);
foreach (var c in all.Where(x => !x.IsComando).OrderBy(x => x.Nome)) RawContatos.Add(c);
ApplyFilters();
}
private async Task RenderImageAsync()
{
var dlg = new SaveFileDialog
{
Filter = "PNG Image|*.png"
};
if (dlg.ShowDialog() == true
&& OverlayElement is FrameworkElement overlay
&& BackgroundImage is BitmapImage bg)
{
await _imageService.SaveAsync(overlay, bg, dlg.FileName, dlg.FileName.Replace(".png", "_1.png"));
MessageBox.Show(
"Concluído!",
"Confirm Save",
MessageBoxButton.OK,
MessageBoxImage.Information);
//Open recently generated image
if (File.Exists(dlg.FileName))
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = dlg.FileName,
UseShellExecute = true
});
}
}
}
private async Task UploadImageAsync()
{
var dlg = new OpenFileDialog
{
Filter = "PNG Image|*.png"
};
if (dlg.ShowDialog() == true)
{
try
{
await _imageService.MoveFile(dlg.FileName);
MessageBox.Show(
"Concluído!",
"Confirm Upload",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(
$"Erro ao mover a imagem:\n{ex.Message}",
"Upload Failed",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
}
public async Task UpdateAsync()
{
if (SelectedContatos == null) return;
var result = MessageBox.Show(
"Save selected records?",
"Confirm Save",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result != MessageBoxResult.Yes) return;
foreach (var contato in SelectedContatos)
{
if (string.IsNullOrWhiteSpace(contato.Ramal)
|| string.IsNullOrWhiteSpace(contato.Nome))
{
MessageBox.Show(
"Ramal and Nome are required.",
"Validation Error",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
try
{
await _repo.InsertUpdateAsync(contato);
}
catch
{
MessageBox.Show(
"Error saving record.",
"Save Failed",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
await LoadRawAsync();
MessageBox.Show(
"Concluído!",
"Confirm Save",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
private void ApplyFilters()
{
Comando.Clear();
Aniversarios.Clear();
foreach (var item in RawContatos.Where(x => x.IsComando))
Comando.Add(item);
foreach (var item in RawContatos.Where(x => !x.IsComando).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Day).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Month))
Aniversarios.Add(item);
LoadContatosSemCMD(RawContatos.Where(x => !x.IsComando).OrderBy(x => x.Nome));
}
private void LoadContatosSemCMD(IEnumerable<Contato> contatos)
{
ContatosSemCMDFirstHalf.Clear();
ContatosSemCMDSecondHalf.Clear();
var list = contatos.ToList();
int half = (list.Count + 1) / 2;
for (int i = 0; i < list.Count; i++)
{
if (i < half)
ContatosSemCMDFirstHalf.Add(list[i]);
else
ContatosSemCMDSecondHalf.Add(list[i]);
}
}
private async Task ImportExcelAsync()
{
var openFileDialog = new OpenFileDialog
{
Filter = "Excel Files|*.xlsx;*.xls"
};
if (openFileDialog.ShowDialog() != true)
return;
var filePath = openFileDialog.FileName;
var ramais = new List<Contato>();
using (var workbook = new XLWorkbook(filePath))
{
var ws = workbook.Worksheets.First();
var table = ws.Cell("B2").CurrentRegion;
var nomeCol = table.Row(1).Search("Nome").First().Address.ColumnNumber;
foreach (var row in table.Rows().Skip(1)) // Skip header
{
var contato = new Contato
{
Ramal = row.Cell(4).GetString(),
Nome = row.Cell(2).GetString(),
Email = row.Cell(3).GetString(),
Area = row.Cell(5).GetString(),
IsComando = false
};
ramais.Add(contato);
}
table = ws.Cell("H2").CurrentRegion;
foreach (var row in table.Rows().Skip(1)) // Skip header
{
var name = row.Cell(1).GetString();
var match = ramais.Find(x => x.Nome == name);
if (match is not null)
{
match.Aniversario = row.Cell(2).GetDateTime();
}
}
}
foreach (var contato in ramais)
{
await _repo.InsertUpdateAsync(contato);
}
await LoadRawAsync(); // Refresh UI
MessageBox.Show(
"Concluído!",
"Confirm Save",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
}
}