diff --git a/.gitignore b/.gitignore
index 9491a2f..1753ed8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -360,4 +360,5 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
-FodyWeavers.xsd
\ No newline at end of file
+FodyWeavers.xsd
+.history/
\ No newline at end of file
diff --git a/App.xaml b/App.xaml
index 919c746..25fa19c 100644
--- a/App.xaml
+++ b/App.xaml
@@ -14,7 +14,7 @@
-
+
diff --git a/App.xaml.cs b/App.xaml.cs
index ed4a219..f85cdb4 100644
--- a/App.xaml.cs
+++ b/App.xaml.cs
@@ -17,8 +17,6 @@ namespace BackgroundBuilder
public App()
{
_host = Host.CreateDefaultBuilder()
- .ConfigureAppConfiguration(cfg =>
- cfg.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true))
.ConfigureServices((_, services) =>
{
// Database & repository
diff --git a/BackgroundBuilder.csproj b/BackgroundBuilder.csproj
index f70ee4d..a438ff2 100644
--- a/BackgroundBuilder.csproj
+++ b/BackgroundBuilder.csproj
@@ -1,29 +1,44 @@
-
- WinExe
- net9.0-windows7.0
- true
- BackgroundBuilder
- BackgroundBuilder
- enable
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Always
-
-
+
+ WinExe
+ net9.0-windows7.0
+ true
+ BackgroundBuilder
+ BackgroundBuilder
+ enable
+ true
+ smart.ico
+ smart.ico
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ \
+
+
diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..700b5d6
--- /dev/null
+++ b/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// O código foi gerado por uma ferramenta.
+// Versão de Tempo de Execução:4.0.30319.42000
+//
+// As alterações ao arquivo poderão causar comportamento incorreto e serão perdidas se
+// o código for gerado novamente.
+//
+//------------------------------------------------------------------------------
+
+namespace BackgroundBuilder.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Properties/Settings.settings b/Properties/Settings.settings
new file mode 100644
index 0000000..049245f
--- /dev/null
+++ b/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/README.md b/README.md
index b3c668e..99167f1 100644
--- a/README.md
+++ b/README.md
@@ -1,46 +1,65 @@
# BackgroundBuilder
-An MVVM WPF application (.NET 6) providing an Excel-like editor for the `contatos` table in PostgreSQL.
+[](https://dotnet.microsoft.com/) [](https://learn.microsoft.com/dotnet/desktop/wpf/) [](https://learn.microsoft.com/dotnet/desktop/wpf/xaml/) [](https://learn.microsoft.com/dotnet/csharp/) [](https://learn.microsoft.com/pt-br/dotnet/architecture/maui/mvvm) [](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection) [](https://www.postgresql.org/) [](https://www.npgsql.org/) [](https://github.com/DapperLib/Dapper) [](https://www.microsoft.com/windows)
-## Prerequisites
+## 📝 Project description
-- .NET 6 SDK
-- PostgreSQL database with table:
+> 🖥️ An MVVM WPF application (.NET 8) providing an Excel-like editor for the `contatos` table in PostgreSQL
- ```sql
- CREATE TABLE public.contatos (
- ramal text PRIMARY KEY NOT NULL,
- nome text NOT NULL,
- email text,
- area text,
- aniversario date,
- "isComando" boolean NOT NULL
- );
- ```
-## Setup
-1. Edit appsettings.json, set your ConnectionStrings:ContatosDb.
-2. In a terminal:
- ```bash
- dotnet restore
- dotnet build
- dotnet run --project BackgroundBuilder.csproj
- ```
-3. The main window will appear; on load it fetches and displays all contatos.
-
-## Architecture
-- MVVM with MVVM with ObservableObject, RelayCommand.
-- DI via Microsoft.Extensions.Hosting.
-- Repositories (PostgresContatoRepository) handle all DB I/O with Dapper.
-- Services (DatabaseService) manage the Npgsql connection.
-- ViewModels free of data-access logic: only orchestration.
---
-### New Background & Export Features
+## 📑 Prerequisites
-- **Select Background…**
+- [](https://dotnet.microsoft.com/)
+- [](https://www.postgresql.org/) **database** with table:
+
+```sql
+ CREATE TABLE public.contatos (
+ ramal text PRIMARY KEY NOT NULL,
+ nome text NOT NULL,
+ email text,
+ area text,
+ aniversario date,
+ "isComando" boolean NOT NULL
+ );
+```
+
+---
+
+## 🔧 Setup
+
+1. ✏️ Edit `appsettings.json`, set your `ConnectionStrings:ContatosDb`.
+2. 🖥️ In a terminal:
+
+ ```bash
+ dotnet restore
+ dotnet build
+ dotnet run --project BackgroundBuilder.csproj
+ ```
+
+3. 🪟 The main window will appear; on load it fetches and displays all contatos.
+
+---
+
+## 🏗️ Architecture
+
+- 🏛️ **MVVM** with `ObservableObject`, `RelayCommand`
+- 🧩 **DI** via `Microsoft.Extensions.Hosting`
+- 💾 **Repositories** (`PostgresContatoRepository`) handle all DB I/O with Dapper
+- 🔌 **Services** (`DatabaseService`) manage the Npgsql connection
+- 🧠 **ViewModels** free of data-access logic: only orchestration
+
+---
+
+## 🚀 Releases
+
+### 📆 21/05/2025: 🆕 Background & Export Features
+
+- 🎨 **Select Background…**
Opens a file picker—choose any image (PNG, JPG, BMP). That image becomes your canvas.
-- **Create Image…**
+- 🖼️ **Create Image…**
Saves the current DataGrid overlaid on the background as a single PNG.
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
----
\ No newline at end of file
+
+---
diff --git a/Services/ContactService.cs b/Services/ContactService.cs
new file mode 100644
index 0000000..688989a
--- /dev/null
+++ b/Services/ContactService.cs
@@ -0,0 +1,36 @@
+// BackgroundBuilder\Services\ContactService.cs
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using BackgroundBuilder.Models;
+using BackgroundBuilder.Repositories;
+
+namespace BackgroundBuilder.Services
+{
+ public class ContactService(IContatoRepository repo) : IContactService
+ {
+ private readonly IContatoRepository _repo = repo;
+
+ public Task> GetAllAsync()
+ => _repo.GetAllAsync();
+
+ public Task InsertUpdateAsync(Contato contato)
+ => _repo.InsertUpdateAsync(contato);
+
+ public Task AddAsync(Contato contato)
+ => _repo.InsertUpdateAsync(contato);
+
+ public Task DeleteAsync(string ramal)
+ => _repo.DeleteAsync(ramal);
+
+ public IEnumerable GetComando(IEnumerable all)
+ => all.Where(c => c.IsComando);
+
+ public IEnumerable GetSemComando(IEnumerable all)
+ => all.Where(c => !c.IsComando).OrderBy(x => x.Nome);
+
+ public IEnumerable GetAniversarios(IEnumerable all)
+ => all.Where(c => c.Aniversario.HasValue).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Day).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Month);
+ }
+}
diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs
index 20ad871..179a036 100644
--- a/Services/DatabaseService.cs
+++ b/Services/DatabaseService.cs
@@ -4,10 +4,9 @@ using Npgsql;
namespace BackgroundBuilder.Services
{
- public class DatabaseService(IConfiguration config)
+ public class DatabaseService()
{
- private readonly string _connString = config.GetConnectionString("ContatosDb")
- ?? throw new InvalidOperationException("Missing connection string 'ContatosDb'.");
+ private readonly string _connString = "Host=192.168.10.248;Username=postgres;Password=gds21;Database=Smart Energia";
public NpgsqlConnection CreateConnection()
{
diff --git a/Services/IContactService.cs b/Services/IContactService.cs
new file mode 100644
index 0000000..4bbb4a6
--- /dev/null
+++ b/Services/IContactService.cs
@@ -0,0 +1,18 @@
+// BackgroundBuilder\Services\IContactService.cs
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using BackgroundBuilder.Models;
+
+namespace BackgroundBuilder.Services
+{
+ public interface IContactService
+ {
+ Task InsertUpdateAsync(Contato contato);
+ Task DeleteAsync(string ramal);
+
+ Task> GetAllAsync();
+ IEnumerable GetComando(IEnumerable all);
+ IEnumerable GetSemComando(IEnumerable all);
+ IEnumerable GetAniversarios(IEnumerable all);
+ }
+}
diff --git a/Services/IImageService.cs b/Services/IImageService.cs
index 56f9144..b4b8e5f 100644
--- a/Services/IImageService.cs
+++ b/Services/IImageService.cs
@@ -17,7 +17,7 @@ namespace BackgroundBuilder.Services
/// • overlayPath (optional): overlay alone
/// Returns the actual paths written.
///
- Task<(string primaryPath, string? overlayPath)> SaveAsync(
+ Task SaveAsync(
FrameworkElement overlay,
BitmapImage background,
string primaryPath,
diff --git a/Services/IMessageService.cs b/Services/IMessageService.cs
new file mode 100644
index 0000000..3fee25e
--- /dev/null
+++ b/Services/IMessageService.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+namespace BackgroundBuilder.Services
+{
+ public interface IMessageService
+ {
+ void Show(string message, string caption, MessageBoxButton buttons, MessageBoxImage icon);
+ MessageBoxResult ShowConfirm(string message, string caption, MessageBoxButton buttons, MessageBoxImage icon);
+ }
+}
\ No newline at end of file
diff --git a/Services/ImageService.cs b/Services/ImageService.cs
index bae1c37..0274814 100644
--- a/Services/ImageService.cs
+++ b/Services/ImageService.cs
@@ -26,14 +26,12 @@ namespace BackgroundBuilder.Services
return await Task.FromResult(bmp);
}
- public async Task<(string primaryPath, string? overlayPath)> SaveAsync(
+ public async Task SaveAsync(
FrameworkElement overlay,
BitmapImage background,
string primaryPath,
string? overlayPath = null)
{
- var compositeBmp = RenderComposite(overlay, background, OverlayOffset);
- SaveBitmap(compositeBmp, primaryPath);
string? savedOverlayPath = null;
if (!string.IsNullOrWhiteSpace(overlayPath))
@@ -43,7 +41,10 @@ namespace BackgroundBuilder.Services
savedOverlayPath = overlayPath;
}
- return await Task.FromResult((primaryPath, savedOverlayPath));
+ var compositeBmp = RenderComposite(overlay, background, OverlayOffset);
+ SaveBitmap(compositeBmp, primaryPath);
+
+ await Task.FromResult((primaryPath, savedOverlayPath));
}
///
@@ -54,11 +55,79 @@ namespace BackgroundBuilder.Services
private static RenderTargetBitmap RenderComposite(
FrameworkElement mainGrid,
BitmapImage? background,
- Thickness offset)
+ Thickness offset,
+ int taskbarHeight = 0)
{
- // Determine canvas size
- int width = background?.PixelWidth ?? (int)mainGrid.ActualWidth;
- int height = background?.PixelHeight ?? (int)mainGrid.ActualHeight;
+ double width = background?.PixelWidth ?? mainGrid.ActualWidth;
+ double height = background?.PixelHeight ?? mainGrid.ActualHeight;
+
+ // Measure & arrange the mainGrid so ActualWidth/Height are valid
+ mainGrid.Measure(new Size(width, height));
+ mainGrid.Arrange(new Rect(0, 0, mainGrid.DesiredSize.Width, mainGrid.DesiredSize.Height));
+
+ double gridWidth = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width;
+ double gridHeight = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height;
+
+ double x;
+ double y;
+ double scale;
+
+ if (background != null)
+ {
+ // Calculate max allowed size (half background, minus taskbar for height)
+ double maxWidth = width;
+ double maxHeight = (height - taskbarHeight);
+
+ // Compute scale factor to fit within maxWidth/maxHeight, preserving aspect ratio
+ scale = Math.Min(1.0, Math.Min(maxWidth / gridWidth, maxHeight / gridHeight));
+
+ double scaledWidth = gridWidth * scale;
+ double scaledHeight = gridHeight * scale;
+
+ // Place at lower right, offset
+ x = width - scaledWidth - offset.Right;
+ y = height - scaledHeight - offset.Bottom;
+
+ gridWidth = scaledWidth + offset.Right;
+ gridHeight = scaledHeight + offset.Bottom;
+ }
+ else
+ {
+ // When rendering without background, use the mainGrid's own size and render at (0,0)
+ // --- Fix: Ensure full layout and hide scrollbars before rendering ---
+ mainGrid.UpdateLayout();
+
+ // If mainGrid is a ScrollViewer or contains one, hide scrollbars
+ if (mainGrid is System.Windows.Controls.ScrollViewer sv)
+ {
+ sv.HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden;
+ sv.VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden;
+ }
+ // If mainGrid contains a DataGrid or similar, try to hide scrollbars
+ if (mainGrid is System.Windows.Controls.Panel panel)
+ {
+ foreach (var child in panel.Children)
+ {
+ if (child is System.Windows.Controls.DataGrid dg)
+ {
+ dg.HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden;
+ dg.VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden;
+ }
+ }
+ }
+
+ // Re-measure after hiding scrollbars
+ mainGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ mainGrid.Arrange(new Rect(0, 0, mainGrid.DesiredSize.Width, mainGrid.DesiredSize.Height));
+ mainGrid.UpdateLayout();
+
+ gridWidth = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width;
+ gridHeight = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height;
+ width = (int)Math.Ceiling(gridWidth);
+ height = (int)Math.Ceiling(gridHeight);
+ x = 0;
+ y = 0;
+ }
var dv = new DrawingVisual();
using (var ctx = dv.RenderOpen())
@@ -66,30 +135,20 @@ namespace BackgroundBuilder.Services
// Draw background if provided
if (background != null)
{
- ctx.DrawImage(
- background,
- new Rect(0, 0, width, height));
+ ctx.DrawImage(background, new Rect(0, 0, width, height));
}
- // Measure & arrange the mainGrid so ActualWidth/Height are valid
- mainGrid.Measure(new Size(width, height));
- mainGrid.Arrange(new Rect(0, 0, mainGrid.DesiredSize.Width, mainGrid.DesiredSize.Height));
-
- double ow = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width;
- double oh = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height;
-
- double x = width - ow - offset.Left;
- double y = height - oh - offset.Right;
-
- // Draw mainGrid at either origin or bottom-right with offset
- var brush = new VisualBrush(mainGrid);
-
- ctx.DrawRectangle(brush, null, new Rect(x, y, ow, oh));
+ // Draw mainGrid at calculated position, with scaling if needed
+ var brush = new VisualBrush(mainGrid)
+ {
+ Stretch = Stretch.Uniform,
+ };
+ ctx.DrawRectangle(brush, null, new Rect(x, y, gridWidth, gridHeight));
}
var rtb = new RenderTargetBitmap(
- width,
- height,
+ (int)width,
+ (int)height,
96,
96,
PixelFormats.Pbgra32);
@@ -103,8 +162,11 @@ namespace BackgroundBuilder.Services
///
private static void SaveBitmap(RenderTargetBitmap bitmap, string path)
{
- // Ensure directory exists
- Directory.CreateDirectory(Path.GetDirectoryName(path)!);
+ var directory = Path.GetDirectoryName(path);
+ if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory!);
+ }
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write);
var encoder = new PngBitmapEncoder();
diff --git a/Services/MessageService.cs b/Services/MessageService.cs
new file mode 100644
index 0000000..b88d37f
--- /dev/null
+++ b/Services/MessageService.cs
@@ -0,0 +1,13 @@
+using System.Windows;
+
+namespace BackgroundBuilder.Services
+{
+ public class MessageService : IMessageService
+ {
+ public void Show(string message, string caption, MessageBoxButton buttons, MessageBoxImage icon)
+ => MessageBox.Show(message, caption, buttons, icon);
+
+ public MessageBoxResult ShowConfirm(string message, string caption, MessageBoxButton buttons, MessageBoxImage icon)
+ => MessageBox.Show(message, caption, buttons, icon);
+ }
+}
\ No newline at end of file
diff --git a/Thumbs.db b/Thumbs.db
new file mode 100644
index 0000000..da31ada
Binary files /dev/null and b/Thumbs.db differ
diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs
index 8adef02..9893804 100644
--- a/ViewModels/MainWindowViewModel.cs
+++ b/ViewModels/MainWindowViewModel.cs
@@ -27,9 +27,11 @@ namespace BackgroundBuilder.ViewModels
public ObservableCollection RawContatos { get; } = [];
public ObservableCollection Comando { get; } = [];
- public ObservableCollection ContatosSemCMD { get; } = [];
public ObservableCollection Aniversarios { get; } = [];
+ public ObservableCollection ContatosSemCMDFirstHalf { get; } = new();
+ public ObservableCollection ContatosSemCMDSecondHalf { get; } = new();
+
private Contato? _selectedContato;
public Contato? SelectedContato{ get => _selectedContato; set { _selectedContato = value; OnPropertyChanged(); DeleteCommand.RaiseCanExecuteChanged(); } }
@@ -212,18 +214,30 @@ namespace BackgroundBuilder.ViewModels
private void ApplyFilters()
{
Comando.Clear();
- ContatosSemCMD.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.Nome))
- ContatosSemCMD.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 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]);
+ }
}
}
}
\ No newline at end of file
diff --git a/Views/MainWindow.xaml b/Views/MainWindow.xaml
index 41ef816..419ed06 100644
--- a/Views/MainWindow.xaml
+++ b/Views/MainWindow.xaml
@@ -1,110 +1,39 @@
+ Title="BackgroundBuilder" WindowStartupLocation="CenterScreen" WindowState="Maximized" MinHeight="950" MinWidth="1290" MaxHeight="1048" MaxWidth="2250">
-
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
@@ -132,52 +62,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Views/MainWindow.xaml.cs b/Views/MainWindow.xaml.cs
index 479153f..eac2d33 100644
--- a/Views/MainWindow.xaml.cs
+++ b/Views/MainWindow.xaml.cs
@@ -25,5 +25,13 @@ namespace BackgroundBuilder.Views
// Load contatos on window load
Loaded += async (_, __) => await _vm.LoadRawAsync();
}
+
+ private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (sender is DataGrid gridView && gridView.SelectedItem != null)
+ {
+ gridView.ScrollIntoView(gridView.SelectedItem);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/appsettings.json b/appsettings.json
deleted file mode 100644
index 3a3ec26..0000000
--- a/appsettings.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "ConnectionStrings": {
- "ContatosDb": "Host=192.168.10.248;Username=postgres;Password=gds21;Database=Smart Energia"
- }
-}
\ No newline at end of file
diff --git a/resume.md b/resume.md
index ab01b31..d8751c8 100644
--- a/resume.md
+++ b/resume.md
@@ -1,5 +1,7 @@
-DateNoDotConverter.cs:
-[
+# Resume
+
+DateNoDotConverter.cs:
+
```cs
//BackgroundBuilder\Converters\DateNoDotConverter.cs
using System;
@@ -29,9 +31,9 @@ namespace BackgroundBuilder.Converters
}
}
```
-]
+
Contato.cs:
-[
+
```cs
//BackgroundBuilder\Models\Contato.cs
using System;
@@ -49,10 +51,9 @@ namespace BackgroundBuilder.Models
}
}
```
-]
IContatoRepository.cs:
-[
+
```cs
//BackgroundBuilder\Repositories\IContatoRepository.cs
using System.Collections.Generic;
@@ -69,10 +70,9 @@ namespace BackgroundBuilder.Repositories
}
}
```
-]
PostgresContatoRepository.cs:
-[
+
```cs
//BackgroundBuilder\Repositories\PostgresContatoRepository.cs
using System.Collections.Generic;
@@ -147,10 +147,9 @@ namespace BackgroundBuilder.Repositories
}
}
```
-]
DatabaseService.cs:
-[
+
```cs
//BackgroundBuilder\Services\DatabaseService.cs
using System;
@@ -173,10 +172,9 @@ namespace BackgroundBuilder.Services
}
}
```
-]
IImageService.cs:
-[
+
```cs
//BackgroundBuilder\Services\IImageService.cs
using System.Threading.Tasks;
@@ -206,10 +204,9 @@ namespace BackgroundBuilder.Services
}
}
```
-]
ImageService.cs:
-[
+
```cs
//BackgroundBuilder\Services\ImageService.cs
using System;
@@ -328,10 +325,9 @@ namespace BackgroundBuilder.Services
}
}
```
-]
ITaskbarService.cs:
-[
+
```cs
//BackgroundBuilder\Services\ITaskbarService.cs
namespace BackgroundBuilder.Services
@@ -348,10 +344,9 @@ namespace BackgroundBuilder.Services
}
}
```
-]
TaskbarService.cs:
-[
+
```cs
//BackgroundBuilder\Services\TaskbarService.cs
using System;
@@ -409,10 +404,9 @@ namespace BackgroundBuilder.Services
}
}
```
-]
ObservableObject.cs:
-[
+
```cs
//BackgroundBuilder\Utils\ObservableObject.cs
using System.ComponentModel;
@@ -428,10 +422,9 @@ namespace BackgroundBuilder.Utils
}
}
```
-]
RelayCommand.cs:
-[
+
```cs
//BackgroundBuilder\Utils\RelayCommand.cs
using System;
@@ -453,10 +446,9 @@ namespace BackgroundBuilder.Utils
}
}
```
-]
MainWindowViewModel.cs:
-[
+
```cs
//BackgroundBuilder\ViewModels\MainWindowViewModel.cs
using System;
@@ -689,15 +681,14 @@ namespace BackgroundBuilder.ViewModels
}
}
```
-]
MainWindow.xaml
-[
+
```xml
@@ -880,10 +871,9 @@ MainWindow.xaml
```
-]
MainWindow.xaml.cs:
-[
+
```cs
//BackgroundBuilder\Views\MainWindow.xaml.cs
using System.ComponentModel;
@@ -916,10 +906,9 @@ namespace BackgroundBuilder.Views
}
}
```
-]
App.xaml
-[
+
```xml
```
-]
App.xaml.cs:
-[
+
```cs
//BackgroundBuilder\App.xaml.cs
using System;
@@ -1037,10 +1025,9 @@ namespace BackgroundBuilder
}
}
```
-]
appsettings.json:
-[
+
```json
//BackgroundBuilder\appsettings.json
{
@@ -1049,56 +1036,72 @@ appsettings.json:
}
}
```
-]
README.md:
-[
+
```md
# BackgroundBuilder
-An MVVM WPF application (.NET 6) providing an Excel-like editor for the `contatos` table in PostgreSQL.
+[](https://dotnet.microsoft.com/) [](https://learn.microsoft.com/dotnet/desktop/wpf/) [](https://learn.microsoft.com/dotnet/desktop/wpf/xaml/) [](https://learn.microsoft.com/dotnet/csharp/) [](https://learn.microsoft.com/pt-br/dotnet/architecture/maui/mvvm) [](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection) [](https://www.postgresql.org/) [](https://www.npgsql.org/) [](https://github.com/DapperLib/Dapper) [](https://www.microsoft.com/windows)
-## Prerequisites
+## 📝 Project description
-- .NET 6 SDK
-- PostgreSQL database with table:
+> 🖥️ An MVVM WPF application (.NET 8) providing an Excel-like editor for the `contatos` table in PostgreSQL
- ```sql
- CREATE TABLE public.contatos (
- ramal text PRIMARY KEY NOT NULL,
- nome text NOT NULL,
- email text,
- area text,
- aniversario date,
- "isComando" boolean NOT NULL
- );
- ```
-## Setup
-1. Edit appsettings.json, set your ConnectionStrings:ContatosDb.
-2. In a terminal:
- ```bash
- dotnet restore
- dotnet build
- dotnet run --project BackgroundBuilder.csproj
- ```
-3. The main window will appear; on load it fetches and displays all contatos.
-
-## Architecture
-- MVVM with MVVM with ObservableObject, RelayCommand.
-- DI via Microsoft.Extensions.Hosting.
-- Repositories (PostgresContatoRepository) handle all DB I/O with Dapper.
-- Services (DatabaseService) manage the Npgsql connection.
-- ViewModels free of data-access logic: only orchestration.
---
-### New Background & Export Features
+## 📑 Prerequisites
-- **Select Background…**
+- [](https://dotnet.microsoft.com/)
+- [](https://www.postgresql.org/) **database** with table:
+
+```sql
+ CREATE TABLE public.contatos (
+ ramal text PRIMARY KEY NOT NULL,
+ nome text NOT NULL,
+ email text,
+ area text,
+ aniversario date,
+ "isComando" boolean NOT NULL
+ );
+```
+
+---
+
+## 🔧 Setup
+
+1. ✏️ Edit `appsettings.json`, set your `ConnectionStrings:ContatosDb`.
+2. 🖥️ In a terminal:
+
+ ```bash
+ dotnet restore
+ dotnet build
+ dotnet run --project BackgroundBuilder.csproj
+ ```
+
+3. 🪟 The main window will appear; on load it fetches and displays all contatos.
+
+---
+
+## 🏗️ Architecture
+
+- 🏛️ **MVVM** with `ObservableObject`, `RelayCommand`
+- 🧩 **DI** via `Microsoft.Extensions.Hosting`
+- 💾 **Repositories** (`PostgresContatoRepository`) handle all DB I/O with Dapper
+- 🔌 **Services** (`DatabaseService`) manage the Npgsql connection
+- 🧠 **ViewModels** free of data-access logic: only orchestration
+
+---
+
+## 🚀 Releases
+
+### 📆 21/05/2025: 🆕 Background & Export Features
+
+- 🎨 **Select Background…**
Opens a file picker—choose any image (PNG, JPG, BMP). That image becomes your canvas.
-- **Create Image…**
+- 🖼️ **Create Image…**
Saves the current DataGrid overlaid on the background as a single PNG.
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
+
---
-```
-]
\ No newline at end of file
diff --git a/smart.ico b/smart.ico
new file mode 100644
index 0000000..79c4b0a
Binary files /dev/null and b/smart.ico differ