Import inicial: migração de arquivos da rede
This commit is contained in:
parent
b979de68c5
commit
1badf9db58
3
.gitignore
vendored
3
.gitignore
vendored
@ -360,4 +360,5 @@ MigrationBackup/
|
|||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
.history/
|
||||||
2
App.xaml
2
App.xaml
@ -14,7 +14,7 @@
|
|||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
<Setter Property="BorderBrush" Value="Black"/>
|
<Setter Property="BorderBrush" Value="Black"/>
|
||||||
<Setter Property="IsReadOnly" Value="True"/>
|
<Setter Property="IsReadOnly" Value="True"/>
|
||||||
<Setter Property="FontSize" Value="16"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="RowBackground" Value="GhostWhite"/>
|
<Setter Property="RowBackground" Value="GhostWhite"/>
|
||||||
<Setter Property="AlternatingRowBackground" Value="#387f79"/>
|
<Setter Property="AlternatingRowBackground" Value="#387f79"/>
|
||||||
<Setter Property="Margin" Value="5"/>
|
<Setter Property="Margin" Value="5"/>
|
||||||
|
|||||||
@ -17,8 +17,6 @@ namespace BackgroundBuilder
|
|||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
_host = Host.CreateDefaultBuilder()
|
_host = Host.CreateDefaultBuilder()
|
||||||
.ConfigureAppConfiguration(cfg =>
|
|
||||||
cfg.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true))
|
|
||||||
.ConfigureServices((_, services) =>
|
.ConfigureServices((_, services) =>
|
||||||
{
|
{
|
||||||
// Database & repository
|
// Database & repository
|
||||||
|
|||||||
@ -1,29 +1,44 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net9.0-windows7.0</TargetFramework>
|
<TargetFramework>net9.0-windows7.0</TargetFramework>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<RootNamespace>BackgroundBuilder</RootNamespace>
|
<RootNamespace>BackgroundBuilder</RootNamespace>
|
||||||
<AssemblyName>BackgroundBuilder</AssemblyName>
|
<AssemblyName>BackgroundBuilder</AssemblyName>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
<PackageIcon>smart.ico</PackageIcon>
|
||||||
|
<ApplicationIcon>smart.ico</ApplicationIcon>
|
||||||
<ItemGroup>
|
</PropertyGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-preview.3.25171.5" />
|
<Content Include="smart.ico" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-preview.3.25171.5" />
|
</ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.3.25171.5" />
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.3.25171.5" />
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-preview.3.25171.5" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="10.0.0-preview.3.25173.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-preview.3.25171.5" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.3.25171.5" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.3.25171.5" />
|
||||||
<ItemGroup>
|
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||||
<None Update="appsettings.json">
|
<PackageReference Include="System.Drawing.Common" Version="10.0.0-preview.3.25173.2" />
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
</ItemGroup>
|
||||||
</None>
|
<ItemGroup>
|
||||||
</ItemGroup>
|
<Compile Update="Properties\Settings.Designer.cs">
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
<None Update="smart.ico">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
26
Properties/Settings.Designer.cs
generated
Normal file
26
Properties/Settings.Designer.cs
generated
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 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.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Properties/Settings.settings
Normal file
6
Properties/Settings.settings
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
</SettingsFile>
|
||||||
87
README.md
87
README.md
@ -1,46 +1,65 @@
|
|||||||
# BackgroundBuilder
|
# 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
|
> 🖥️ An MVVM WPF application (.NET 8) providing an Excel-like editor for the `contatos` table in PostgreSQL
|
||||||
- PostgreSQL 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 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.
|
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.
|
Saves the current DataGrid overlaid on the background as a single PNG.
|
||||||
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
|
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
|
||||||
---
|
|
||||||
|
---
|
||||||
|
|||||||
36
Services/ContactService.cs
Normal file
36
Services/ContactService.cs
Normal file
@ -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<IEnumerable<Contato>> 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<Contato> GetComando(IEnumerable<Contato> all)
|
||||||
|
=> all.Where(c => c.IsComando);
|
||||||
|
|
||||||
|
public IEnumerable<Contato> GetSemComando(IEnumerable<Contato> all)
|
||||||
|
=> all.Where(c => !c.IsComando).OrderBy(x => x.Nome);
|
||||||
|
|
||||||
|
public IEnumerable<Contato> GetAniversarios(IEnumerable<Contato> all)
|
||||||
|
=> all.Where(c => c.Aniversario.HasValue).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Day).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Month);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,10 +4,9 @@ using Npgsql;
|
|||||||
|
|
||||||
namespace BackgroundBuilder.Services
|
namespace BackgroundBuilder.Services
|
||||||
{
|
{
|
||||||
public class DatabaseService(IConfiguration config)
|
public class DatabaseService()
|
||||||
{
|
{
|
||||||
private readonly string _connString = config.GetConnectionString("ContatosDb")
|
private readonly string _connString = "Host=192.168.10.248;Username=postgres;Password=gds21;Database=Smart Energia";
|
||||||
?? throw new InvalidOperationException("Missing connection string 'ContatosDb'.");
|
|
||||||
|
|
||||||
public NpgsqlConnection CreateConnection()
|
public NpgsqlConnection CreateConnection()
|
||||||
{
|
{
|
||||||
|
|||||||
18
Services/IContactService.cs
Normal file
18
Services/IContactService.cs
Normal file
@ -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<IEnumerable<Contato>> GetAllAsync();
|
||||||
|
IEnumerable<Contato> GetComando(IEnumerable<Contato> all);
|
||||||
|
IEnumerable<Contato> GetSemComando(IEnumerable<Contato> all);
|
||||||
|
IEnumerable<Contato> GetAniversarios(IEnumerable<Contato> all);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ namespace BackgroundBuilder.Services
|
|||||||
/// • overlayPath (optional): overlay alone
|
/// • overlayPath (optional): overlay alone
|
||||||
/// Returns the actual paths written.
|
/// Returns the actual paths written.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<(string primaryPath, string? overlayPath)> SaveAsync(
|
Task SaveAsync(
|
||||||
FrameworkElement overlay,
|
FrameworkElement overlay,
|
||||||
BitmapImage background,
|
BitmapImage background,
|
||||||
string primaryPath,
|
string primaryPath,
|
||||||
|
|||||||
10
Services/IMessageService.cs
Normal file
10
Services/IMessageService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,14 +26,12 @@ namespace BackgroundBuilder.Services
|
|||||||
return await Task.FromResult(bmp);
|
return await Task.FromResult(bmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(string primaryPath, string? overlayPath)> SaveAsync(
|
public async Task SaveAsync(
|
||||||
FrameworkElement overlay,
|
FrameworkElement overlay,
|
||||||
BitmapImage background,
|
BitmapImage background,
|
||||||
string primaryPath,
|
string primaryPath,
|
||||||
string? overlayPath = null)
|
string? overlayPath = null)
|
||||||
{
|
{
|
||||||
var compositeBmp = RenderComposite(overlay, background, OverlayOffset);
|
|
||||||
SaveBitmap(compositeBmp, primaryPath);
|
|
||||||
|
|
||||||
string? savedOverlayPath = null;
|
string? savedOverlayPath = null;
|
||||||
if (!string.IsNullOrWhiteSpace(overlayPath))
|
if (!string.IsNullOrWhiteSpace(overlayPath))
|
||||||
@ -43,7 +41,10 @@ namespace BackgroundBuilder.Services
|
|||||||
savedOverlayPath = overlayPath;
|
savedOverlayPath = overlayPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult((primaryPath, savedOverlayPath));
|
var compositeBmp = RenderComposite(overlay, background, OverlayOffset);
|
||||||
|
SaveBitmap(compositeBmp, primaryPath);
|
||||||
|
|
||||||
|
await Task.FromResult((primaryPath, savedOverlayPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -54,11 +55,79 @@ namespace BackgroundBuilder.Services
|
|||||||
private static RenderTargetBitmap RenderComposite(
|
private static RenderTargetBitmap RenderComposite(
|
||||||
FrameworkElement mainGrid,
|
FrameworkElement mainGrid,
|
||||||
BitmapImage? background,
|
BitmapImage? background,
|
||||||
Thickness offset)
|
Thickness offset,
|
||||||
|
int taskbarHeight = 0)
|
||||||
{
|
{
|
||||||
// Determine canvas size
|
double width = background?.PixelWidth ?? mainGrid.ActualWidth;
|
||||||
int width = background?.PixelWidth ?? (int)mainGrid.ActualWidth;
|
double height = background?.PixelHeight ?? mainGrid.ActualHeight;
|
||||||
int height = background?.PixelHeight ?? (int)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();
|
var dv = new DrawingVisual();
|
||||||
using (var ctx = dv.RenderOpen())
|
using (var ctx = dv.RenderOpen())
|
||||||
@ -66,30 +135,20 @@ namespace BackgroundBuilder.Services
|
|||||||
// Draw background if provided
|
// Draw background if provided
|
||||||
if (background != null)
|
if (background != null)
|
||||||
{
|
{
|
||||||
ctx.DrawImage(
|
ctx.DrawImage(background, new Rect(0, 0, width, height));
|
||||||
background,
|
|
||||||
new Rect(0, 0, width, height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Measure & arrange the mainGrid so ActualWidth/Height are valid
|
// Draw mainGrid at calculated position, with scaling if needed
|
||||||
mainGrid.Measure(new Size(width, height));
|
var brush = new VisualBrush(mainGrid)
|
||||||
mainGrid.Arrange(new Rect(0, 0, mainGrid.DesiredSize.Width, mainGrid.DesiredSize.Height));
|
{
|
||||||
|
Stretch = Stretch.Uniform,
|
||||||
double ow = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width;
|
};
|
||||||
double oh = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height;
|
ctx.DrawRectangle(brush, null, new Rect(x, y, gridWidth, gridHeight));
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var rtb = new RenderTargetBitmap(
|
var rtb = new RenderTargetBitmap(
|
||||||
width,
|
(int)width,
|
||||||
height,
|
(int)height,
|
||||||
96,
|
96,
|
||||||
96,
|
96,
|
||||||
PixelFormats.Pbgra32);
|
PixelFormats.Pbgra32);
|
||||||
@ -103,8 +162,11 @@ namespace BackgroundBuilder.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void SaveBitmap(RenderTargetBitmap bitmap, string path)
|
private static void SaveBitmap(RenderTargetBitmap bitmap, string path)
|
||||||
{
|
{
|
||||||
// Ensure directory exists
|
var directory = Path.GetDirectoryName(path);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
if (!Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory!);
|
||||||
|
}
|
||||||
|
|
||||||
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write);
|
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write);
|
||||||
var encoder = new PngBitmapEncoder();
|
var encoder = new PngBitmapEncoder();
|
||||||
|
|||||||
13
Services/MessageService.cs
Normal file
13
Services/MessageService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,9 +27,11 @@ namespace BackgroundBuilder.ViewModels
|
|||||||
|
|
||||||
public ObservableCollection<Contato> RawContatos { get; } = [];
|
public ObservableCollection<Contato> RawContatos { get; } = [];
|
||||||
public ObservableCollection<Contato> Comando { get; } = [];
|
public ObservableCollection<Contato> Comando { get; } = [];
|
||||||
public ObservableCollection<Contato> ContatosSemCMD { get; } = [];
|
|
||||||
public ObservableCollection<Contato> Aniversarios { get; } = [];
|
public ObservableCollection<Contato> Aniversarios { get; } = [];
|
||||||
|
|
||||||
|
public ObservableCollection<Contato> ContatosSemCMDFirstHalf { get; } = new();
|
||||||
|
public ObservableCollection<Contato> ContatosSemCMDSecondHalf { get; } = new();
|
||||||
|
|
||||||
|
|
||||||
private Contato? _selectedContato;
|
private Contato? _selectedContato;
|
||||||
public Contato? SelectedContato{ get => _selectedContato; set { _selectedContato = value; OnPropertyChanged(); DeleteCommand.RaiseCanExecuteChanged(); } }
|
public Contato? SelectedContato{ get => _selectedContato; set { _selectedContato = value; OnPropertyChanged(); DeleteCommand.RaiseCanExecuteChanged(); } }
|
||||||
@ -212,18 +214,30 @@ namespace BackgroundBuilder.ViewModels
|
|||||||
private void ApplyFilters()
|
private void ApplyFilters()
|
||||||
{
|
{
|
||||||
Comando.Clear();
|
Comando.Clear();
|
||||||
ContatosSemCMD.Clear();
|
|
||||||
Aniversarios.Clear();
|
Aniversarios.Clear();
|
||||||
|
|
||||||
foreach (var item in RawContatos.Where(x => x.IsComando))
|
foreach (var item in RawContatos.Where(x => x.IsComando))
|
||||||
Comando.Add(item);
|
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))
|
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);
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,110 +1,39 @@
|
|||||||
<Window x:Class="BackgroundBuilder.Views.MainWindow"
|
<Window x:Class="BackgroundBuilder.Views.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:BackgroundBuilder.Converters"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:BackgroundBuilder.Converters"
|
||||||
Title="BackgroundBuilder" WindowStartupLocation="CenterScreen" WindowState="Maximized" MinHeight="950" MinWidth="1230">
|
Title="BackgroundBuilder" WindowStartupLocation="CenterScreen" WindowState="Maximized" MinHeight="950" MinWidth="1290" MaxHeight="1048" MaxWidth="2250">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<converters:DateNoDotConverter x:Key="DateNoDotConverter" />
|
<converters:DateNoDotConverter x:Key="DateNoDotConverter"/>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
<Grid x:Name="RootGrid" Margin="20">
|
<Grid x:Name="RootGrid" Margin="20">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
<Border Grid.Row="0" BorderBrush="Black" BorderThickness="1"/>
|
||||||
<!-- Row 0: Background selection -->
|
<Border Grid.Row="1" BorderBrush="Black" BorderThickness="1,0,1,1"/>
|
||||||
<Grid Grid.Row="0">
|
<!-- Row 0: three side-by-side DataGrids -->
|
||||||
|
<Grid Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<!-- Row 2: Image-->
|
<Border Grid.Column="0" BorderBrush="Black" BorderThickness="1"/>
|
||||||
<Rectangle Grid.Column="0"
|
<!-- Column 0: DataGrid for RawContatos -->
|
||||||
Fill="Gainsboro"
|
|
||||||
Height="90"
|
|
||||||
Width="160"
|
|
||||||
>
|
|
||||||
</Rectangle>
|
|
||||||
<Image Source="{Binding BackgroundImage}" Height="90" Stretch="Uniform" Grid.Column="0"/>
|
|
||||||
<Button Content="Selecionar Imagem de Fundo…"
|
|
||||||
Command="{Binding SelectImageCommand}"
|
|
||||||
Grid.Column="1"
|
|
||||||
Padding="20,3,20,3"
|
|
||||||
Height="30"
|
|
||||||
Margin="5"/>
|
|
||||||
<TextBox Text="{Binding CaminhoBGImage}"
|
|
||||||
Grid.Column="2"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Padding="20,3,20,3"
|
|
||||||
Height="30"
|
|
||||||
Margin="5"/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Row 1: three side-by-side DataGrids -->
|
|
||||||
<Grid Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Bottom">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
|
|
||||||
<Grid Grid.Column="0" x:Name="MainGrid" HorizontalAlignment="Right" VerticalAlignment="Bottom">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<!-- Comando -->
|
|
||||||
<DataGrid Grid.Column="0"
|
|
||||||
ItemsSource="{Binding Comando}"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
CanUserAddRows="False">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="Ação" Binding="{Binding Nome}"/>
|
|
||||||
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal}"/>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
<!-- ContatosSemCMD -->
|
|
||||||
<DataGrid Grid.Column="1"
|
|
||||||
ItemsSource="{Binding ContatosSemCMD}"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
CanUserAddRows="False">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="Nome" Binding="{Binding Nome}"/>
|
|
||||||
<DataGridTextColumn Header="Email" Binding="{Binding Email}"/>
|
|
||||||
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal}"/>
|
|
||||||
<DataGridTextColumn Header="Área" Binding="{Binding Area}"/>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
<!-- Aniversarios -->
|
|
||||||
<DataGrid Grid.Column="2"
|
|
||||||
ItemsSource="{Binding Aniversarios}"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
CanUserAddRows="False">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="Nome" Binding="{Binding Nome}"/>
|
|
||||||
<DataGridTextColumn Header="Aniversário"
|
|
||||||
Binding="{Binding Aniversario,Converter={StaticResource DateNoDotConverter}}"/>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Row 3: DataGrid for RawContatos -->
|
|
||||||
<DataGrid x:Name="RawGrid"
|
<DataGrid x:Name="RawGrid"
|
||||||
Grid.Column="2"
|
Grid.Column="0"
|
||||||
ItemsSource="{Binding RawContatos}"
|
ItemsSource="{Binding RawContatos}"
|
||||||
SelectedItem="{Binding SelectedContato}"
|
SelectedItem="{Binding SelectedContato}"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
IsReadOnly="False"
|
IsReadOnly="False"
|
||||||
IsHitTestVisible ="True"
|
IsHitTestVisible ="True"
|
||||||
FontSize="11"
|
FontSize="11"
|
||||||
|
BorderBrush="Black"
|
||||||
|
BorderThickness="1"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Left"
|
||||||
Height="800"
|
Height="800"
|
||||||
|
Panel.ZIndex="1"
|
||||||
|
SelectionChanged="ListView_SelectionChanged"
|
||||||
AlternatingRowBackground="DarkGray">
|
AlternatingRowBackground="DarkGray">
|
||||||
<DataGrid.ColumnHeaderStyle>
|
<DataGrid.ColumnHeaderStyle>
|
||||||
<Style TargetType="DataGridColumnHeader">
|
<Style TargetType="DataGridColumnHeader">
|
||||||
@ -115,6 +44,7 @@
|
|||||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
</Style>
|
</Style>
|
||||||
</DataGrid.ColumnHeaderStyle>
|
</DataGrid.ColumnHeaderStyle>
|
||||||
|
|
||||||
<!-- your existing columns here -->
|
<!-- your existing columns here -->
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal, UpdateSourceTrigger=PropertyChanged}" />
|
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
@ -132,52 +62,139 @@
|
|||||||
|
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- Column 1: DataGrid for MainGrid -->
|
||||||
|
<Grid Grid.Column="0" x:Name="MainGrid" HorizontalAlignment="Right" VerticalAlignment="Bottom">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Comando -->
|
||||||
|
<DataGrid Grid.Column="0"
|
||||||
|
ItemsSource="{Binding Comando}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Ação" Binding="{Binding Nome}"/>
|
||||||
|
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal}"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<!-- ContatosSemCMD (First Half) -->
|
||||||
|
<DataGrid Grid.Column="1"
|
||||||
|
x:Name="ContatosSemCMDGrid1"
|
||||||
|
ItemsSource="{Binding ContatosSemCMDFirstHalf}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Nome" Binding="{Binding Nome}"/>
|
||||||
|
<DataGridTextColumn Header="Email" Binding="{Binding Email}"/>
|
||||||
|
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal}"/>
|
||||||
|
<DataGridTextColumn Header="Área" Binding="{Binding Area}"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<!-- ContatosSemCMD (Second Half) -->
|
||||||
|
<DataGrid Grid.Column="2"
|
||||||
|
x:Name="ContatosSemCMDGrid2"
|
||||||
|
ItemsSource="{Binding ContatosSemCMDSecondHalf}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Nome" Binding="{Binding Nome}"/>
|
||||||
|
<DataGridTextColumn Header="Email" Binding="{Binding Email}"/>
|
||||||
|
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal}"/>
|
||||||
|
<DataGridTextColumn Header="Área" Binding="{Binding Area}"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<!-- Aniversarios -->
|
||||||
|
<DataGrid Grid.Column="3"
|
||||||
|
ItemsSource="{Binding Aniversarios}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Nome" Binding="{Binding Nome}"/>
|
||||||
|
<DataGridTextColumn Header="Aniversário"
|
||||||
|
Binding="{Binding Aniversario,Converter={StaticResource DateNoDotConverter}}"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Row 3: Buttons for actions -->
|
<!-- Row 1: Buttons for actions -->
|
||||||
<Grid Grid.Row="3"
|
<Grid Grid.Row="1"
|
||||||
Margin="5">
|
Margin="5">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
<!-- Row 2: Image-->
|
||||||
|
<Rectangle Grid.Column="0"
|
||||||
|
Fill="Gainsboro"
|
||||||
|
Height="90"
|
||||||
|
Width="160"
|
||||||
|
>
|
||||||
|
</Rectangle>
|
||||||
|
<Image Source="{Binding BackgroundImage}" Height="90" Stretch="Uniform" Grid.Column="0"/>
|
||||||
|
<Button Content="Selecionar Imagem de Fundo…"
|
||||||
|
Command="{Binding SelectImageCommand}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="20,3,20,3"
|
||||||
|
Height="40"
|
||||||
|
Margin="5"/>
|
||||||
|
<TextBox Text="{Binding CaminhoBGImage}"
|
||||||
|
Grid.Column="2"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Padding="20,3,20,3"
|
||||||
|
Height="40"
|
||||||
|
Margin="5"/>
|
||||||
<Button Content="Recarregar Contatos"
|
<Button Content="Recarregar Contatos"
|
||||||
Command="{Binding RefreshCommand}"
|
Command="{Binding RefreshCommand}"
|
||||||
Grid.Column="0"
|
Grid.Column="3"
|
||||||
FontWeight="Medium"
|
FontWeight="Medium"
|
||||||
Padding="5"
|
Padding="5"
|
||||||
|
Height="40"
|
||||||
Margin="5"/>
|
Margin="5"/>
|
||||||
<Button Content="+ Nova linha"
|
<Button Content="+ Nova linha"
|
||||||
Command="{Binding AddCommand}"
|
Command="{Binding AddCommand}"
|
||||||
Grid.Column = "1"
|
Grid.Column = "4"
|
||||||
Foreground="Green"
|
Foreground="Green"
|
||||||
FontWeight="Medium"
|
FontWeight="Medium"
|
||||||
Padding="5"
|
Padding="5"
|
||||||
|
Height="40"
|
||||||
Margin="5"/>
|
Margin="5"/>
|
||||||
<Button Content="- Deletar selecionada"
|
<Button Content="- Deletar selecionada"
|
||||||
Command="{Binding DeleteCommand}"
|
Command="{Binding DeleteCommand}"
|
||||||
Grid.Column = "2"
|
Grid.Column = "5"
|
||||||
Foreground="Red"
|
Foreground="Red"
|
||||||
FontWeight="Medium"
|
FontWeight="Medium"
|
||||||
Padding="5"
|
Padding="5"
|
||||||
|
Height="40"
|
||||||
Margin="5"/>
|
Margin="5"/>
|
||||||
<Button Content="Salvar dados"
|
<Button Content="Salvar dados"
|
||||||
Command="{Binding UpdateCommand}"
|
Command="{Binding UpdateCommand}"
|
||||||
CommandParameter="RawGrid"
|
CommandParameter="RawGrid"
|
||||||
Grid.Column = "3"
|
Grid.Column = "6"
|
||||||
FontWeight="Medium"
|
FontWeight="Medium"
|
||||||
Padding="5"
|
Padding="5"
|
||||||
|
Height="40"
|
||||||
Margin="5"/>
|
Margin="5"/>
|
||||||
<Button Content="Criar Imagem -->"
|
<Button Content="Criar Imagem -->"
|
||||||
Command="{Binding ExportImageCommand}"
|
Command="{Binding ExportImageCommand}"
|
||||||
CommandParameter="MainGrid"
|
CommandParameter="MainGrid"
|
||||||
Grid.Column = "4"
|
Grid.Column = "7"
|
||||||
Foreground="Blue"
|
Foreground="Blue"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Padding="5"
|
Padding="5"
|
||||||
|
Height="40"
|
||||||
Margin="5"/>
|
Margin="5"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -25,5 +25,13 @@ namespace BackgroundBuilder.Views
|
|||||||
// Load contatos on window load
|
// Load contatos on window load
|
||||||
Loaded += async (_, __) => await _vm.LoadRawAsync();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"ContatosDb": "Host=192.168.10.248;Username=postgres;Password=gds21;Database=Smart Energia"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
149
resume.md
149
resume.md
@ -1,5 +1,7 @@
|
|||||||
DateNoDotConverter.cs:
|
# Resume
|
||||||
[
|
|
||||||
|
DateNoDotConverter.cs:
|
||||||
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Converters\DateNoDotConverter.cs
|
//BackgroundBuilder\Converters\DateNoDotConverter.cs
|
||||||
using System;
|
using System;
|
||||||
@ -29,9 +31,9 @@ namespace BackgroundBuilder.Converters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
Contato.cs:
|
Contato.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Models\Contato.cs
|
//BackgroundBuilder\Models\Contato.cs
|
||||||
using System;
|
using System;
|
||||||
@ -49,10 +51,9 @@ namespace BackgroundBuilder.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
IContatoRepository.cs:
|
IContatoRepository.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Repositories\IContatoRepository.cs
|
//BackgroundBuilder\Repositories\IContatoRepository.cs
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -69,10 +70,9 @@ namespace BackgroundBuilder.Repositories
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
PostgresContatoRepository.cs:
|
PostgresContatoRepository.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Repositories\PostgresContatoRepository.cs
|
//BackgroundBuilder\Repositories\PostgresContatoRepository.cs
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -147,10 +147,9 @@ namespace BackgroundBuilder.Repositories
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
DatabaseService.cs:
|
DatabaseService.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Services\DatabaseService.cs
|
//BackgroundBuilder\Services\DatabaseService.cs
|
||||||
using System;
|
using System;
|
||||||
@ -173,10 +172,9 @@ namespace BackgroundBuilder.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
IImageService.cs:
|
IImageService.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Services\IImageService.cs
|
//BackgroundBuilder\Services\IImageService.cs
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -206,10 +204,9 @@ namespace BackgroundBuilder.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
ImageService.cs:
|
ImageService.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Services\ImageService.cs
|
//BackgroundBuilder\Services\ImageService.cs
|
||||||
using System;
|
using System;
|
||||||
@ -328,10 +325,9 @@ namespace BackgroundBuilder.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
ITaskbarService.cs:
|
ITaskbarService.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Services\ITaskbarService.cs
|
//BackgroundBuilder\Services\ITaskbarService.cs
|
||||||
namespace BackgroundBuilder.Services
|
namespace BackgroundBuilder.Services
|
||||||
@ -348,10 +344,9 @@ namespace BackgroundBuilder.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
TaskbarService.cs:
|
TaskbarService.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Services\TaskbarService.cs
|
//BackgroundBuilder\Services\TaskbarService.cs
|
||||||
using System;
|
using System;
|
||||||
@ -409,10 +404,9 @@ namespace BackgroundBuilder.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
ObservableObject.cs:
|
ObservableObject.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Utils\ObservableObject.cs
|
//BackgroundBuilder\Utils\ObservableObject.cs
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@ -428,10 +422,9 @@ namespace BackgroundBuilder.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
RelayCommand.cs:
|
RelayCommand.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Utils\RelayCommand.cs
|
//BackgroundBuilder\Utils\RelayCommand.cs
|
||||||
using System;
|
using System;
|
||||||
@ -453,10 +446,9 @@ namespace BackgroundBuilder.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
MainWindowViewModel.cs:
|
MainWindowViewModel.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\ViewModels\MainWindowViewModel.cs
|
//BackgroundBuilder\ViewModels\MainWindowViewModel.cs
|
||||||
using System;
|
using System;
|
||||||
@ -689,15 +681,14 @@ namespace BackgroundBuilder.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
MainWindow.xaml
|
MainWindow.xaml
|
||||||
[
|
|
||||||
```xml
|
```xml
|
||||||
<!--BackgroundBuilder\Views\MainWindow.xaml-->
|
<!--BackgroundBuilder\Views\MainWindow.xaml-->
|
||||||
<Window x:Class="BackgroundBuilder.Views.MainWindow"
|
<Window x:Class="BackgroundBuilder.Views.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:BackgroundBuilder.Converters"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:BackgroundBuilder.Converters"
|
||||||
Title="BackgroundBuilder" WindowStartupLocation="CenterScreen" WindowState="Maximized" MinHeight="950" MinWidth="1230">
|
Title="BackgroundBuilder" WindowStartupLocation="CenterScreen" WindowState="Maximized" MinHeight="950" MinWidth="1230">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<converters:DateNoDotConverter x:Key="DateNoDotConverter" />
|
<converters:DateNoDotConverter x:Key="DateNoDotConverter" />
|
||||||
@ -880,10 +871,9 @@ MainWindow.xaml
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
MainWindow.xaml.cs:
|
MainWindow.xaml.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\Views\MainWindow.xaml.cs
|
//BackgroundBuilder\Views\MainWindow.xaml.cs
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@ -916,10 +906,9 @@ namespace BackgroundBuilder.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
App.xaml
|
App.xaml
|
||||||
[
|
|
||||||
```xml
|
```xml
|
||||||
<!--BackgroundBuilder\App.xaml-->
|
<!--BackgroundBuilder\App.xaml-->
|
||||||
<Application x:Class="BackgroundBuilder.App"
|
<Application x:Class="BackgroundBuilder.App"
|
||||||
@ -976,10 +965,9 @@ App.xaml
|
|||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
App.xaml.cs:
|
App.xaml.cs:
|
||||||
[
|
|
||||||
```cs
|
```cs
|
||||||
//BackgroundBuilder\App.xaml.cs
|
//BackgroundBuilder\App.xaml.cs
|
||||||
using System;
|
using System;
|
||||||
@ -1037,10 +1025,9 @@ namespace BackgroundBuilder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
appsettings.json:
|
appsettings.json:
|
||||||
[
|
|
||||||
```json
|
```json
|
||||||
//BackgroundBuilder\appsettings.json
|
//BackgroundBuilder\appsettings.json
|
||||||
{
|
{
|
||||||
@ -1049,56 +1036,72 @@ appsettings.json:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
]
|
|
||||||
|
|
||||||
README.md:
|
README.md:
|
||||||
[
|
|
||||||
```md
|
```md
|
||||||
# BackgroundBuilder
|
# 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
|
> 🖥️ An MVVM WPF application (.NET 8) providing an Excel-like editor for the `contatos` table in PostgreSQL
|
||||||
- PostgreSQL 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 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.
|
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.
|
Saves the current DataGrid overlaid on the background as a single PNG.
|
||||||
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
|
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
|
||||||
|
|
||||||
---
|
---
|
||||||
```
|
|
||||||
]
|
|
||||||
Loading…
x
Reference in New Issue
Block a user