Adicionar arquivos de projeto.
This commit is contained in:
parent
f5f3b40120
commit
b979de68c5
53
App.xaml
Normal file
53
App.xaml
Normal file
@ -0,0 +1,53 @@
|
||||
<Application x:Class="BackgroundBuilder.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Startup="OnStartup">
|
||||
<Application.Resources>
|
||||
<Style TargetType="DataGrid">
|
||||
<Setter Property="AutoGenerateColumns" Value="True"/>
|
||||
<Setter Property="CanUserAddRows" Value="False"/>
|
||||
<Setter Property="CanUserDeleteRows" Value="False"/>
|
||||
<Setter Property="CanUserReorderColumns" Value="False"/>
|
||||
<Setter Property="CanUserResizeColumns" Value="False"/>
|
||||
<Setter Property="CanUserSortColumns" Value="False"/>
|
||||
<Setter Property="GridLinesVisibility" Value="None"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderBrush" Value="Black"/>
|
||||
<Setter Property="IsReadOnly" Value="True"/>
|
||||
<Setter Property="FontSize" Value="16"/>
|
||||
<Setter Property="RowBackground" Value="GhostWhite"/>
|
||||
<Setter Property="AlternatingRowBackground" Value="#387f79"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontStyle" Value="Italic" />
|
||||
<Setter Property="FontWeight" Value="Medium" />
|
||||
<Setter Property="HeadersVisibility" Value="Column" />
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="IsHitTestVisible" Value="False"/>
|
||||
</Style>
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Background" Value="#387f79"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="FontWeight" Value="Medium" />
|
||||
<Setter Property="Margin" Value="10,0,10,0"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="Foreground" Value="Black"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="16"/>
|
||||
</Style>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
54
App.xaml.cs
Normal file
54
App.xaml.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using BackgroundBuilder.Repositories;
|
||||
using BackgroundBuilder.Services;
|
||||
using BackgroundBuilder.ViewModels;
|
||||
using BackgroundBuilder.Views;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace BackgroundBuilder
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
private readonly IHost _host;
|
||||
|
||||
public App()
|
||||
{
|
||||
_host = Host.CreateDefaultBuilder()
|
||||
.ConfigureAppConfiguration(cfg =>
|
||||
cfg.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true))
|
||||
.ConfigureServices((_, services) =>
|
||||
{
|
||||
// Database & repository
|
||||
services.AddSingleton<DatabaseService>();
|
||||
services.AddScoped<IContatoRepository, PostgresContatoRepository>();
|
||||
|
||||
// New image service
|
||||
services.AddSingleton<IImageService, ImageService>();
|
||||
|
||||
// New taskbar service
|
||||
services.AddSingleton<ITaskbarService, TaskbarService>();
|
||||
|
||||
// VM & View
|
||||
services.AddTransient<MainWindowViewModel>();
|
||||
services.AddTransient<MainWindow>();
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
private async void OnStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
await _host.StartAsync();
|
||||
var window = _host.Services.GetRequiredService<MainWindow>();
|
||||
window.Show();
|
||||
}
|
||||
|
||||
protected override async void OnExit(ExitEventArgs e)
|
||||
{
|
||||
using (_host) { await _host.StopAsync(); }
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
AssemblyInfo.cs
Normal file
10
AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
29
BackgroundBuilder.csproj
Normal file
29
BackgroundBuilder.csproj
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows7.0</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>BackgroundBuilder</RootNamespace>
|
||||
<AssemblyName>BackgroundBuilder</AssemblyName>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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" />
|
||||
<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" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="10.0.0-preview.3.25173.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
22
BackgroundBuilder.sln
Normal file
22
BackgroundBuilder.sln
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35527.113 d17.12
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackgroundBuilder", "BackgroundBuilder.csproj", "{4AC89EE0-00B5-47D2-924B-FD65DD09E67B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4AC89EE0-00B5-47D2-924B-FD65DD09E67B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4AC89EE0-00B5-47D2-924B-FD65DD09E67B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4AC89EE0-00B5-47D2-924B-FD65DD09E67B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4AC89EE0-00B5-47D2-924B-FD65DD09E67B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
26
Converters/DateNoDotConverter.cs
Normal file
26
Converters/DateNoDotConverter.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace BackgroundBuilder.Converters
|
||||
{
|
||||
public class DateNoDotConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is DateTime dt)
|
||||
{
|
||||
// get "MMM" then TrimEnd('.'):
|
||||
string month = culture
|
||||
.DateTimeFormat
|
||||
.GetAbbreviatedMonthName(dt.Month)
|
||||
.TrimEnd('.');
|
||||
return $"{dt:dd}/{month}";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
14
Models/Contato.cs
Normal file
14
Models/Contato.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace BackgroundBuilder.Models
|
||||
{
|
||||
public class Contato
|
||||
{
|
||||
public string Ramal { get; set; } = string.Empty;
|
||||
public string? Nome { get; set; } = string.Empty;
|
||||
public string? Email { get; set; }
|
||||
public string? Area { get; set; }
|
||||
public DateTime? Aniversario { get; set; }
|
||||
public bool IsComando { get; set; }
|
||||
}
|
||||
}
|
||||
46
README.md
Normal file
46
README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# BackgroundBuilder
|
||||
|
||||
An MVVM WPF application (.NET 6) providing an Excel-like editor for the `contatos` table in PostgreSQL.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 6 SDK
|
||||
- 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
|
||||
|
||||
- **Select Background…**
|
||||
Opens a file picker—choose any image (PNG, JPG, BMP). That image becomes your canvas.
|
||||
|
||||
- **Create Image…**
|
||||
Saves the current DataGrid overlaid on the background as a single PNG.
|
||||
Uses WPF’s `RenderTargetBitmap` and `PngBitmapEncoder` under the hood.
|
||||
---
|
||||
13
Repositories/IContatoRepository.cs
Normal file
13
Repositories/IContatoRepository.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BackgroundBuilder.Models;
|
||||
|
||||
namespace BackgroundBuilder.Repositories
|
||||
{
|
||||
public interface IContatoRepository
|
||||
{
|
||||
Task<IEnumerable<Contato>> GetAllAsync();
|
||||
Task InsertUpdateAsync(Contato c);
|
||||
Task DeleteAsync(string ramal);
|
||||
}
|
||||
}
|
||||
71
Repositories/PostgresContatoRepository.cs
Normal file
71
Repositories/PostgresContatoRepository.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Npgsql;
|
||||
using BackgroundBuilder.Models;
|
||||
using BackgroundBuilder.Services;
|
||||
|
||||
namespace BackgroundBuilder.Repositories
|
||||
{
|
||||
public class PostgresContatoRepository(DatabaseService db) : IContatoRepository
|
||||
{
|
||||
private readonly DatabaseService _db = db;
|
||||
|
||||
public async Task<IEnumerable<Contato>> GetAllAsync()
|
||||
{
|
||||
using var conn = _db.CreateConnection();
|
||||
const string sql = @"
|
||||
SELECT
|
||||
ramal AS Ramal,
|
||||
nome AS Nome,
|
||||
email AS Email,
|
||||
area AS Area,
|
||||
aniversario AS Aniversario,
|
||||
""isComando"" AS IsComando
|
||||
FROM contatos;";
|
||||
return await conn.QueryAsync<Contato>(sql);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Contato c)
|
||||
{
|
||||
using var conn = _db.CreateConnection();
|
||||
const string sql = @"
|
||||
INSERT INTO contatos
|
||||
(ramal, nome, email, area, aniversario, ""isComando"")
|
||||
VALUES
|
||||
(@Ramal, @Nome, @Email, @Area, @Aniversario, @IsComando);";
|
||||
await conn.ExecuteAsync(sql, c);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string ramal)
|
||||
{
|
||||
using var conn = _db.CreateConnection();
|
||||
const string sql = "DELETE FROM contatos WHERE ramal=@Ramal;";
|
||||
await conn.ExecuteAsync(sql, new { Ramal = ramal });
|
||||
}
|
||||
|
||||
public async Task InsertUpdateAsync(Contato c)
|
||||
{
|
||||
using var conn = _db.CreateConnection();
|
||||
string Ramal = $"'{c.Ramal}'";
|
||||
string Nome = c.Nome is null ? "null" : $"'{c.Nome}'";
|
||||
string Email = c.Email is null ? "null" : $"'{c.Email}'";
|
||||
string Area = c.Area == "" ? "null" : $"'{c.Area}'";
|
||||
string Aniversario = c.Aniversario is null ? "null" : $"'{c.Aniversario:yyyy-MM-dd}'";
|
||||
string IsComando = c.IsComando ? "true" : "false";
|
||||
|
||||
string sql = @$"
|
||||
INSERT INTO contatos
|
||||
(ramal, nome, email, area, aniversario, ""isComando"")
|
||||
VALUES
|
||||
({Ramal}, {Nome}, {Email}, {Area}, {Aniversario:YYYY-MM-DD}, {IsComando})
|
||||
ON CONFLICT (ramal) DO UPDATE SET
|
||||
nome = EXCLUDED.nome,
|
||||
email = EXCLUDED.email,
|
||||
area = EXCLUDED.area,
|
||||
aniversario = EXCLUDED.aniversario,
|
||||
""isComando"" = EXCLUDED.""isComando"";";
|
||||
await conn.ExecuteAsync(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Services/DatabaseService.cs
Normal file
19
Services/DatabaseService.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Npgsql;
|
||||
|
||||
namespace BackgroundBuilder.Services
|
||||
{
|
||||
public class DatabaseService(IConfiguration config)
|
||||
{
|
||||
private readonly string _connString = config.GetConnectionString("ContatosDb")
|
||||
?? throw new InvalidOperationException("Missing connection string 'ContatosDb'.");
|
||||
|
||||
public NpgsqlConnection CreateConnection()
|
||||
{
|
||||
var conn = new NpgsqlConnection(_connString);
|
||||
conn.Open();
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Services/IImageService.cs
Normal file
26
Services/IImageService.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace BackgroundBuilder.Services
|
||||
{
|
||||
public interface IImageService
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads an image from disk.
|
||||
/// </summary>
|
||||
Task<BitmapImage> LoadAsync(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the background + overlay and writes two PNGs:
|
||||
/// • primaryPath: composite of background+overlay
|
||||
/// • overlayPath (optional): overlay alone
|
||||
/// Returns the actual paths written.
|
||||
/// </summary>
|
||||
Task<(string primaryPath, string? overlayPath)> SaveAsync(
|
||||
FrameworkElement overlay,
|
||||
BitmapImage background,
|
||||
string primaryPath,
|
||||
string? overlayPath = null);
|
||||
}
|
||||
}
|
||||
13
Services/ITaskbarService.cs
Normal file
13
Services/ITaskbarService.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace BackgroundBuilder.Services
|
||||
{
|
||||
///
|
||||
/// Defines methods for querying the Windows taskbar size and orientation.
|
||||
///
|
||||
public interface ITaskbarService
|
||||
{
|
||||
/// Gets the current thickness (height if horizontal, width if vertical) of the taskbar in pixels.
|
||||
int GetTaskbarSize();
|
||||
/// <summary>Returns true when the taskbar is horizontal, false when vertical.</summary>
|
||||
bool IsHorizontal();
|
||||
}
|
||||
}
|
||||
115
Services/ImageService.cs
Normal file
115
Services/ImageService.cs
Normal file
@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace BackgroundBuilder.Services
|
||||
{
|
||||
public class ImageService : IImageService
|
||||
{
|
||||
|
||||
// no longer injected — use a private constant
|
||||
private static readonly Thickness OverlayOffset = new(10, 58, 10, 58);
|
||||
|
||||
public async Task<BitmapImage> LoadAsync(string path)
|
||||
{
|
||||
// Load image from disk into BitmapImage
|
||||
var bmp = new BitmapImage();
|
||||
using var stream = File.OpenRead(path);
|
||||
bmp.BeginInit();
|
||||
bmp.UriSource = new Uri(path, UriKind.Absolute);
|
||||
bmp.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bmp.EndInit();
|
||||
// no real async work—return completed task
|
||||
return await Task.FromResult(bmp);
|
||||
}
|
||||
|
||||
public async Task<(string primaryPath, string? overlayPath)> 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))
|
||||
{
|
||||
var overlayBmp = RenderComposite(overlay, null, new Thickness(0));
|
||||
SaveBitmap(overlayBmp, overlayPath!);
|
||||
savedOverlayPath = overlayPath;
|
||||
}
|
||||
|
||||
return await Task.FromResult((primaryPath, savedOverlayPath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a <paramref name="background"/> plus the <paramref name="mainGrid"/>
|
||||
/// at bottom‐right offset by <paramref name="offset"/>. If background is null,
|
||||
/// only the mainGrid is rendered at (0,0).
|
||||
/// </summary>
|
||||
private static RenderTargetBitmap RenderComposite(
|
||||
FrameworkElement mainGrid,
|
||||
BitmapImage? background,
|
||||
Thickness offset)
|
||||
{
|
||||
// Determine canvas size
|
||||
int width = background?.PixelWidth ?? (int)mainGrid.ActualWidth;
|
||||
int height = background?.PixelHeight ?? (int)mainGrid.ActualHeight;
|
||||
|
||||
var dv = new DrawingVisual();
|
||||
using (var ctx = dv.RenderOpen())
|
||||
{
|
||||
// Draw background if provided
|
||||
if (background != null)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
var rtb = new RenderTargetBitmap(
|
||||
width,
|
||||
height,
|
||||
96,
|
||||
96,
|
||||
PixelFormats.Pbgra32);
|
||||
|
||||
rtb.Render(dv);
|
||||
return rtb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the given bitmap as PNG and writes it to disk.
|
||||
/// </summary>
|
||||
private static void SaveBitmap(RenderTargetBitmap bitmap, string path)
|
||||
{
|
||||
// Ensure directory exists
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
|
||||
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write);
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(bitmap));
|
||||
encoder.Save(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Services/TaskbarService.cs
Normal file
54
Services/TaskbarService.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BackgroundBuilder.Services
|
||||
{
|
||||
///
|
||||
/// Implementation of using P/Invoke (SHAppBarMessage).
|
||||
///
|
||||
public class TaskbarService : ITaskbarService
|
||||
{
|
||||
#region WinAPI
|
||||
[DllImport("shell32.dll")]
|
||||
private static extern IntPtr SHAppBarMessage(uint msg, ref APPBARDATA data);
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct APPBARDATA
|
||||
{
|
||||
public uint cbSize;
|
||||
public IntPtr hWnd;
|
||||
public uint uCallbackMessage;
|
||||
public uint uEdge;
|
||||
public RECT rc;
|
||||
public IntPtr lParam;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct RECT { public int left, top, right, bottom; }
|
||||
|
||||
private const uint ABM_GETTASKBARPOS = 5;
|
||||
#endregion
|
||||
|
||||
public int GetTaskbarSize()
|
||||
{
|
||||
var data = new APPBARDATA { cbSize = (uint)Marshal.SizeOf<APPBARDATA>() };
|
||||
if (SHAppBarMessage(ABM_GETTASKBARPOS, ref data) == IntPtr.Zero)
|
||||
throw new InvalidOperationException("Failed to retrieve taskbar position.");
|
||||
|
||||
int thickness = IsHorizontal()
|
||||
? Math.Abs(data.rc.bottom - data.rc.top)
|
||||
: Math.Abs(data.rc.right - data.rc.left);
|
||||
|
||||
return thickness;
|
||||
}
|
||||
|
||||
public bool IsHorizontal()
|
||||
{
|
||||
var data = new APPBARDATA { cbSize = (uint)Marshal.SizeOf<APPBARDATA>() };
|
||||
SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
|
||||
int height = Math.Abs(data.rc.bottom - data.rc.top);
|
||||
int width = Math.Abs(data.rc.right - data.rc.left);
|
||||
|
||||
return width > height;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Utils/ObservableObject.cs
Normal file
12
Utils/ObservableObject.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace BackgroundBuilder.Utils
|
||||
{
|
||||
public abstract class ObservableObject : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
protected void OnPropertyChanged([CallerMemberName] string? propName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
|
||||
}
|
||||
}
|
||||
18
Utils/RelayCommand.cs
Normal file
18
Utils/RelayCommand.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace BackgroundBuilder.Utils
|
||||
{
|
||||
public class RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null) : ICommand
|
||||
{
|
||||
private readonly Action<object?> _execute = execute;
|
||||
private readonly Func<object?, bool>? _canExecute = canExecute;
|
||||
|
||||
public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true;
|
||||
public void Execute(object? parameter) => _execute(parameter);
|
||||
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
public void RaiseCanExecuteChanged()
|
||||
=> CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
229
ViewModels/MainWindowViewModel.cs
Normal file
229
ViewModels/MainWindowViewModel.cs
Normal file
@ -0,0 +1,229 @@
|
||||
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 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> ContatosSemCMD { get; } = [];
|
||||
public ObservableCollection<Contato> Aniversarios { 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 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);
|
||||
}
|
||||
|
||||
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(".", "_1."));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
184
Views/MainWindow.xaml
Normal file
184
Views/MainWindow.xaml
Normal file
@ -0,0 +1,184 @@
|
||||
<Window x:Class="BackgroundBuilder.Views.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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">
|
||||
<Window.Resources>
|
||||
<converters:DateNoDotConverter x:Key="DateNoDotConverter" />
|
||||
</Window.Resources>
|
||||
<Grid x:Name="RootGrid" Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Row 0: Background selection -->
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</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="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"
|
||||
Grid.Column="2"
|
||||
ItemsSource="{Binding RawContatos}"
|
||||
SelectedItem="{Binding SelectedContato}"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="False"
|
||||
IsHitTestVisible ="True"
|
||||
FontSize="11"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Center"
|
||||
Height="800"
|
||||
AlternatingRowBackground="DarkGray">
|
||||
<DataGrid.ColumnHeaderStyle>
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Background" Value="Gray"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
</DataGrid.ColumnHeaderStyle>
|
||||
<!-- your existing columns here -->
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Ramal" Binding="{Binding Ramal, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<DataGridTextColumn Header="Nome" Binding="{Binding Nome, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<DataGridTextColumn Header="Email" Binding="{Binding Email, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<DataGridTextColumn Header="Área" Binding="{Binding Area, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<DataGridTemplateColumn Header="Aniversário">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<DatePicker SelectedDate="{Binding Aniversario, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridCheckBoxColumn Header="É Comando?" Binding="{Binding IsComando}" />
|
||||
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
|
||||
<!-- Row 3: Buttons for actions -->
|
||||
<Grid Grid.Row="3"
|
||||
Margin="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Content="Recarregar Contatos"
|
||||
Command="{Binding RefreshCommand}"
|
||||
Grid.Column="0"
|
||||
FontWeight="Medium"
|
||||
Padding="5"
|
||||
Margin="5"/>
|
||||
<Button Content="+ Nova linha"
|
||||
Command="{Binding AddCommand}"
|
||||
Grid.Column = "1"
|
||||
Foreground="Green"
|
||||
FontWeight="Medium"
|
||||
Padding="5"
|
||||
Margin="5"/>
|
||||
<Button Content="- Deletar selecionada"
|
||||
Command="{Binding DeleteCommand}"
|
||||
Grid.Column = "2"
|
||||
Foreground="Red"
|
||||
FontWeight="Medium"
|
||||
Padding="5"
|
||||
Margin="5"/>
|
||||
<Button Content="Salvar dados"
|
||||
Command="{Binding UpdateCommand}"
|
||||
CommandParameter="RawGrid"
|
||||
Grid.Column = "3"
|
||||
FontWeight="Medium"
|
||||
Padding="5"
|
||||
Margin="5"/>
|
||||
<Button Content="Criar Imagem -->"
|
||||
Command="{Binding ExportImageCommand}"
|
||||
CommandParameter="MainGrid"
|
||||
Grid.Column = "4"
|
||||
Foreground="Blue"
|
||||
FontWeight="Bold"
|
||||
Padding="5"
|
||||
Margin="5"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
29
Views/MainWindow.xaml.cs
Normal file
29
Views/MainWindow.xaml.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using BackgroundBuilder.Models;
|
||||
using BackgroundBuilder.ViewModels;
|
||||
|
||||
namespace BackgroundBuilder.Views
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private readonly MainWindowViewModel _vm;
|
||||
|
||||
public MainWindow(MainWindowViewModel vm)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.Language = System.Windows.Markup.XmlLanguage.GetLanguage(System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag);
|
||||
|
||||
_vm = vm;
|
||||
DataContext = vm;
|
||||
|
||||
// Point the VM’s OverlayElement to our DataGrid
|
||||
_vm.OverlayElement = MainGrid;
|
||||
|
||||
// Load contatos on window load
|
||||
Loaded += async (_, __) => await _vm.LoadRawAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
5
appsettings.json
Normal file
5
appsettings.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"ContatosDb": "Host=192.168.10.248;Username=postgres;Password=gds21;Database=Smart Energia"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user