BackgroundBuilder/ViewModels/MainWindowViewModel.cs
Giuliano Paschoalino 5231c5cf9d Adiciona importação de Excel e melhorias gerais
Adicionada funcionalidade para importar contatos de arquivos Excel
usando a biblioteca ClosedXML, incluindo o comando
`ImportExcelCommand` e o método `ImportExcelAsync` na classe
`MainWindowViewModel`. Ajustado o layout da interface gráfica
para incluir um botão "Importar".

Outras alterações incluem:
- Adição de pacotes ao projeto (`ClosedXML`, `Dapper`, etc.).
- Ajustes no cálculo de `gridWidth` e `gridHeight` em `ImageService`.
- Alteração do valor da constante `OverlayOffset` em `ImageService`.
- Refatoração do método `SaveBitmap` para tratamento de exceções.
- Correção na substituição de extensão ao salvar imagens.
- Reorganização de inicialização de coleções em `MainWindowViewModel`.

Essas mudanças melhoram a funcionalidade e a consistência do projeto.
2025-08-20 17:56:47 -03:00

293 lines
10 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 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());
}
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"));
}
}
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;
foreach (var row in table.Rows().Skip(1)) // Skip header
{
var contato = new Contato
{
Ramal = row.Cell(3).GetString(),
Nome = row.Cell(1).GetString(),
Email = row.Cell(2).GetString(),
Area = row.Cell(4).GetString(),
IsComando = false
};
ramais.Add(contato);
}
table = ws.Cell("G2").CurrentRegion;
foreach (var row in table.Rows().Skip(1)) // Skip header
{
ramais.Find(x => x.Nome == row.Cell(1).GetString()).Aniversario = row.Cell(2).GetDateTime();
}
}
foreach (var contato in ramais)
{
await _repo.InsertUpdateAsync(contato);
}
await LoadRawAsync(); // Refresh UI
}
}
}