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.
293 lines
10 KiB
C#
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
|
|
}
|
|
}
|
|
} |