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.
This commit is contained in:
Giuliano Paschoalino 2025-08-20 17:56:47 -03:00
parent 1badf9db58
commit 5231c5cf9d
4 changed files with 85 additions and 19 deletions

View File

@ -15,6 +15,7 @@
<Content Include="smart.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ClosedXML" Version="0.105.0" />
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-preview.3.25171.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-preview.3.25171.5" />

View File

@ -11,7 +11,7 @@ namespace BackgroundBuilder.Services
{
// no longer injected — use a private constant
private static readonly Thickness OverlayOffset = new(10, 58, 10, 58);
private static readonly Thickness OverlayOffset = new(10, 53, 10, 53);
public async Task<BitmapImage> LoadAsync(string path)
{
@ -88,8 +88,8 @@ namespace BackgroundBuilder.Services
x = width - scaledWidth - offset.Right;
y = height - scaledHeight - offset.Bottom;
gridWidth = scaledWidth + offset.Right;
gridHeight = scaledHeight + offset.Bottom;
gridWidth = scaledWidth;
gridHeight = scaledHeight;
}
else
{
@ -161,6 +161,8 @@ namespace BackgroundBuilder.Services
/// Encodes the given bitmap as PNG and writes it to disk.
/// </summary>
private static void SaveBitmap(RenderTargetBitmap bitmap, string path)
{
try
{
var directory = Path.GetDirectoryName(path);
if (!Directory.Exists(directory))
@ -173,5 +175,10 @@ namespace BackgroundBuilder.Services
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(fs);
}
catch (Exception ex)
{
MessageBox.Show($"Erro ao salvar a imagem:\n{ex.Message}\n\nCaminho: {path}");
}
}
}
}

View File

@ -15,6 +15,7 @@ using BackgroundBuilder.Models;
using BackgroundBuilder.Repositories;
using BackgroundBuilder.Services;
using BackgroundBuilder.Utils;
using ClosedXML.Excel;
using Microsoft.Win32;
namespace BackgroundBuilder.ViewModels
@ -29,8 +30,8 @@ namespace BackgroundBuilder.ViewModels
public ObservableCollection<Contato> Comando { get; } = [];
public ObservableCollection<Contato> Aniversarios { get; } = [];
public ObservableCollection<Contato> ContatosSemCMDFirstHalf { get; } = new();
public ObservableCollection<Contato> ContatosSemCMDSecondHalf { get; } = new();
public ObservableCollection<Contato> ContatosSemCMDFirstHalf { get; } = [];
public ObservableCollection<Contato> ContatosSemCMDSecondHalf { get; } = [];
private Contato? _selectedContato;
@ -60,6 +61,7 @@ namespace BackgroundBuilder.ViewModels
public ICommand RefreshCommand { get; }
public RelayCommand UpdateCommand { get; }
public RelayCommand ExportImageCommand { get; }
public RelayCommand ImportExcelCommand { get; }
public MainWindowViewModel(
IContatoRepository repo,
@ -78,6 +80,7 @@ namespace BackgroundBuilder.ViewModels
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()
@ -161,7 +164,7 @@ namespace BackgroundBuilder.ViewModels
&& OverlayElement is FrameworkElement overlay
&& BackgroundImage is BitmapImage bg)
{
await _imageService.SaveAsync(overlay, bg, dlg.FileName, dlg.FileName.Replace(".", "_1."));
await _imageService.SaveAsync(overlay, bg, dlg.FileName, dlg.FileName.Replace(".png", "_1.png"));
}
}
@ -239,5 +242,52 @@ namespace BackgroundBuilder.ViewModels
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
}
}
}

View File

@ -135,6 +135,7 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Row 2: Image-->
<Rectangle Grid.Column="0"
@ -156,16 +157,23 @@
Padding="20,3,20,3"
Height="40"
Margin="5"/>
<Button Content="Importar"
Command="{Binding ImportExcelCommand}"
Grid.Column="3"
FontWeight="Bold"
Padding="20,3,20,3"
Height="40"
Margin="5"/>
<Button Content="Recarregar Contatos"
Command="{Binding RefreshCommand}"
Grid.Column="3"
Grid.Column="4"
FontWeight="Medium"
Padding="5"
Height="40"
Margin="5"/>
<Button Content="+ Nova linha"
Command="{Binding AddCommand}"
Grid.Column = "4"
Grid.Column = "5"
Foreground="Green"
FontWeight="Medium"
Padding="5"
@ -173,7 +181,7 @@
Margin="5"/>
<Button Content="- Deletar selecionada"
Command="{Binding DeleteCommand}"
Grid.Column = "5"
Grid.Column = "6"
Foreground="Red"
FontWeight="Medium"
Padding="5"
@ -182,7 +190,7 @@
<Button Content="Salvar dados"
Command="{Binding UpdateCommand}"
CommandParameter="RawGrid"
Grid.Column = "6"
Grid.Column = "7"
FontWeight="Medium"
Padding="5"
Height="40"
@ -190,7 +198,7 @@
<Button Content="Criar Imagem -->"
Command="{Binding ExportImageCommand}"
CommandParameter="MainGrid"
Grid.Column = "7"
Grid.Column = "8"
Foreground="Blue"
FontWeight="Bold"
Padding="5"