Compare commits

..

11 Commits

Author SHA1 Message Date
62a3830716 Melhorias de usabilidade e aparência no ListView
Adicionadas funcionalidades de manipulação de eventos de clique do mouse
(`PreviewMouseLeftButtonDown` e `PreviewMouseRightButtonDown`) e suporte
para copiar informações do `ListView` com atalhos de teclado (`Ctrl+C`) e
menu de contexto ("Copiar coluna"). Implementado com `InputBindings`,
`CommandBindings` e `ContextMenu`.

Alterada a exibição das colunas do `GridView` para usar `TextBox` somente
leitura, permitindo seleção de texto, com customizações visuais como
cor de texto (`Foreground="#FF042271"`), fundo transparente e sem bordas.

Adicionado estilo personalizado para itens do `ListView`, incluindo
captura de eventos de tecla pressionada (`KeyDown`) e remoção do evento
de clique duplo do mouse (`MouseDoubleClick`).

Atualizadas colunas adicionais (`Unidade`, `Instalação`, `CNPJ`, `Razão
Social`) para seguir o mesmo padrão de somente leitura e aparência.

Essas mudanças melhoram a experiência do usuário, tornando a interface
mais prática e visualmente consistente.
2025-10-17 11:41:12 -03:00
2f1c28e482 Melhorias na usabilidade e interação com o GridView
Substituído duplo-clique por botão para abrir pastas,
adicionada nova coluna com ícone de pasta e handler
`OpenFolderButton_Click`. Células agora usam `TextBox`
readonly para permitir seleção de texto. Melhorado o
comando de cópia (`Ctrl+C`) com tratamento de exceções
e mensagens de erro detalhadas.

Refatorado hit-test para suportar a nova estrutura de
células e adicionado fallback para capturar texto de
outras fontes. Ajustado evento `KeyDown` para abrir
pastas com validações adicionais. Melhorias gerais na
robustez e mensagens informativas ao usuário.
2025-09-30 17:23:44 -03:00
6b791aa3d5 Adiciona suporte para copiar texto de células no ListView
- Adicionado `ContextMenu` e `KeyBinding` para copiar texto.
- Implementado suporte ao comando `Ctrl+C` com `CopyCommand_Executed`.
- Melhorada interação com `ListView` (cliques e seleção).
- Criados métodos auxiliares para hit-test e navegação visual.
- Implementados fallbacks para cópia de texto em casos específicos.
- Adicionada variável `_lastClickedCellText` para armazenar o texto.
- Importados namespaces adicionais para suportar as mudanças.
2025-09-30 16:23:20 -03:00
981074871a Refatora lógica de filtros e manipulação de diretórios
Adiciona campo de busca de unidades no `MainWindow.xaml`, vinculado ao ViewModel com atualização em tempo real.

Ajusta métodos de manipulação de diretórios (`MouseDoubleClick` e `EnterKeyDown`) para abrir o diretório pai de `unidade.Caminho_NFs`, com validações e tratamento de exceções aprimorados.

Simplifica setters das propriedades `SearchEmpresaText` e `SearchUnidadeText` no ViewModel, garantindo atualização consistente dos filtros e removendo verificações redundantes.

Inclui namespace `System.IO` para manipulação de caminhos no ViewModel.
2025-08-20 14:50:51 -03:00
50c5e390ba Melhorias no layout e usabilidade de MainWindow
- Associado o evento de carregamento da janela ao método `Window_Loaded` em MainWindow.xaml.
- Ajustada a estrutura de `<Grid.RowDefinitions>` para melhorar o layout.
- Adicionado `<TextBox>` `txtEmpresaSearch` vinculado ao ViewModel para busca de empresas.
- Definida altura fixa de `213` no `<ListView>` para consistência visual.
- Implementado o método `Window_Loaded` em MainWindow.xaml.cs para definir foco inicial no campo de busca.
2025-08-19 11:02:52 -03:00
674af18617 fix: Arrumar imagem na tela inicial 2025-08-04 14:12:56 -03:00
4ed4048865 feat: - Limitação na exibição de empresas
- Texto indicativo nas caixas de pesquisa
2025-08-04 14:07:22 -03:00
e9df36ea36 feat: Exibir apenas Unidades gerenciadas. 2025-08-01 16:16:11 -03:00
5149d429ab feat!: Funcionalidades adicionadas para atualizações automáticas de versão e rolagem dos itens de Empresa e Unidade. 2025-08-01 16:13:32 -03:00
22d308a397 Adiciona suporte a MVVM e integração com banco Access
- Atualizado `.gitignore` para ignorar `FodyWeavers.xsd` e `.history`.
- Adicionado suporte a MVVM com `MainWindowViewModel` e comandos.
- Criados conversores `BoolToVisibilityConverter` e `StringToVisibilityConverter`.
- Implementado `AccessService` para acesso ao banco de dados Access.
- Adicionado layout e lógica de interface no `MainWindow.xaml` e `.cs`.
- Incluída dependência `System.Data.OleDb` no projeto.
- Criados `ClienteSmart` e `IClienteRepository` para modelagem de dados.
2025-07-31 18:00:20 -03:00
f592470406 feat: Migração de app console para WPF 2025-07-31 17:11:05 -03:00
17 changed files with 1026 additions and 199 deletions

63
.gitattributes vendored
View File

@ -1,63 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

6
.gitignore vendored
View File

@ -1,4 +1,4 @@
## Ignore Visual Studio temporary files, build results, and
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
@ -360,4 +360,6 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
.history

12
App.xaml Normal file
View File

@ -0,0 +1,12 @@
<Application x:Class="BD_empresa.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BD_empresa"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<local:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
</ResourceDictionary>
</Application.Resources>
</Application>

14
App.xaml.cs Normal file
View File

@ -0,0 +1,14 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace BD_empresa
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

10
AssemblyInfo.cs Normal file
View 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)
)]

View File

@ -1,15 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0-windows7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ApplicationIcon>marca.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.OleDb" Version="9.0.3" />
<None Remove="marca.png" />
</ItemGroup>
<ItemGroup>
<Content Include="marca.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Data.OleDb" Version="9.0.7" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.OleDb" />
</ItemGroup>
<ItemGroup>
<Resource Include="marca.png" />
</ItemGroup>
</Project>

View File

@ -1,9 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
VisualStudioVersion = 17.12.35527.113 d17.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BD_empresa", "BD_empresa.csproj", "{AF7C4BDC-57FB-4277-8D9D-4F863B11538E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BD_empresa", "BD_empresa.csproj", "{8A5C3F7B-3694-45AA-BAF0-5A3A7C2F23D4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,15 +11,12 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AF7C4BDC-57FB-4277-8D9D-4F863B11538E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF7C4BDC-57FB-4277-8D9D-4F863B11538E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF7C4BDC-57FB-4277-8D9D-4F863B11538E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF7C4BDC-57FB-4277-8D9D-4F863B11538E}.Release|Any CPU.Build.0 = Release|Any CPU
{8A5C3F7B-3694-45AA-BAF0-5A3A7C2F23D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A5C3F7B-3694-45AA-BAF0-5A3A7C2F23D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A5C3F7B-3694-45AA-BAF0-5A3A7C2F23D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A5C3F7B-3694-45AA-BAF0-5A3A7C2F23D4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {142A299C-80F0-4D68-B366-3891F4D5C9BB}
EndGlobalSection
EndGlobal

33
Converters.cs Normal file
View File

@ -0,0 +1,33 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace BD_empresa
{
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool b && b)
return Visibility.Visible;
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value is Visibility v && v == Visibility.Visible);
}
}
public class StringToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.IsNullOrWhiteSpace(value as string) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

45
Data/AccessService.cs Normal file
View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Linq;
using System.Threading.Tasks;
namespace BD_empresa.Data
{
public class AccessService(string connectionString) : IClienteRepository
{
private readonly string _connectionString = connectionString;
public async Task<List<UnidadeSmart>> GetAllClientesAsync()
{
return await Task.Run(() =>
{
var unidades = new List<UnidadeSmart>();
using (var connection = new OleDbConnection(_connectionString))
{
connection.Open();
string sql = "SELECT Cod_Smart_cliente, Cod_Smart_unidade, Gestao, Cliente, Unidade, CNPJ_CPF, Codigo_Instalacao, Razao_Social, Caminho_NFs FROM Dados_cadastrais WHERE Dados_cadastrais.Unidade_gerenciada";
using var cmd = new OleDbCommand(sql, connection);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
unidades.Add(new UnidadeSmart
{
Cod_Smart_cliente = long.Parse(reader["Cod_Smart_cliente"].ToString() ?? "0"),
Cod_Smart_unidade = long.Parse(reader["Cod_Smart_unidade"].ToString() ?? "0"),
Gestao = reader["Gestao"].ToString(),
Cliente = reader["Cliente"].ToString(),
Unidade = reader["Unidade"].ToString(),
CNPJ_CPF = reader["CNPJ_CPF"].ToString(),
Codigo_Instalacao = reader["Codigo_Instalacao"].ToString(),
Razao_Social = reader["Razao_Social"].ToString(),
Caminho_NFs = reader["Caminho_NFs"].ToString()
});
}
}
return unidades;
});
}
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BD_empresa.Data
{
public interface IClienteRepository
{
Task<List<UnidadeSmart>> GetAllClientesAsync();
}
}

23
Data/UnidadeSmart.cs Normal file
View File

@ -0,0 +1,23 @@
using System.ComponentModel;
namespace BD_empresa.Data
{
public class UnidadeSmart : INotifyPropertyChanged
{
public long Cod_Smart_cliente { get; set; }
public long Cod_Smart_unidade { get; set; }
public string? Gestao { get; set; }
public string? Cliente { get; set; }
public string? Unidade { get; set; }
public string? CNPJ_CPF { get; set; }
public string? Codigo_Instalacao { get; set; }
public string? Razao_Social { get; set; }
public string? Caminho_NFs { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

269
MainWindow.xaml Normal file
View File

@ -0,0 +1,269 @@
<Window x:Class="BD_empresa.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BD_empresa"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="Pesquisar Unidades" Height="650" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Margin="10" Grid.Row="0" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Source="/marca.png"
Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
Height="50"
RenderOptions.BitmapScalingMode="HighQuality" />
<TextBlock Text="Pesquisar Empresa:" Margin="0,0,10,0" VerticalAlignment="Center" Grid.Column="1"/>
<Button Content="Recarregar dados do BD"
Margin="10"
Grid.Column="3"
Command="{Binding RefreshCommand}"
VerticalAlignment="Center" />
<TextBox
x:Name="txtEmpresaSearch"
Margin="0,0,10,0"
VerticalAlignment="Center"
Grid.Column="2"
Text="{Binding SearchEmpresaText, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<TextBlock Text="{Binding ErrorMessage}"
Foreground="Red"
Margin="10,5,10,0"
Grid.Row="1"
Visibility="{Binding ErrorMessage, Converter={StaticResource StringToVisibilityConverter}}" />
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Empresas:" Margin="10,0,10,0" FontWeight="Bold" Grid.Row="0"/>
<ListView ItemsSource="{Binding Clientes}" Height="213" Margin="10" SelectedItem="{Binding SelectedCliente, Mode=TwoWay}" Grid.Row="1"
PreviewMouseLeftButtonDown="UnidadesListView_PreviewMouseLeftButtonDown"
PreviewMouseRightButtonDown="UnidadesListView_PreviewMouseRightButtonDown">
<ListView.InputBindings>
<KeyBinding Key="C" Modifiers="Control" Command="ApplicationCommands.Copy"/>
</ListView.InputBindings>
<ListView.CommandBindings>
<CommandBinding Command="ApplicationCommands.Copy" Executed="CopyCommand_Executed"/>
</ListView.CommandBindings>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Copiar coluna" Click="CopyMenu_Click"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<!-- Demais colunas (TextBox readonly para seleção) -->
<GridViewColumn Header="Gestão" Width="70">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Gestao}"
IsReadOnly="True"
Foreground="#FF042271"
BorderThickness="0"
Background="Transparent"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Cursor="IBeam"
Focusable="True"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!-- Demais colunas (TextBox readonly para seleção) -->
<GridViewColumn Header="Cliente" Width="400">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Cliente}"
IsReadOnly="True"
Foreground="#FF042271"
BorderThickness="0"
Background="Transparent"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Cursor="IBeam"
Focusable="True"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<!-- removido MouseDoubleClick -->
<!--<EventSetter Event="MouseDoubleClick" Handler="UnidadeListView_MouseDoubleClick" />-->
<EventSetter Event="KeyDown" Handler="UnidadeListView_EnterKeyDown" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<Grid Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}"
Background="#80FFFFFF">
<ProgressBar IsIndeterminate="True" Height="30" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</Grid>
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Margin="10" Grid.Row="0" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Pesquisar Unidade:" Margin="0,0,10,0" Grid.Row="1"/>
<TextBox x:Name="txtUnidadeSearch"
Margin="0,0,10,0"
VerticalAlignment="Center"
Grid.Column="2"
Text="{Binding SearchUnidadeText, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<!-- texto atualizado: remove menção ao duplo-clique -->
<TextBlock Text="Unidades da empresa selecionada (Clique no ícone da pasta para abrir o caminho):" Margin="10,0,10,0" FontWeight="Bold" Grid.Row="1"/>
<ListView ItemsSource="{Binding UnidadesSelecionadas}" Margin="10" Grid.Row="2"
x:Name="UnidadesListView"
PreviewMouseLeftButtonDown="UnidadesListView_PreviewMouseLeftButtonDown"
PreviewMouseRightButtonDown="UnidadesListView_PreviewMouseRightButtonDown">
<ListView.InputBindings>
<KeyBinding Key="C" Modifiers="Control" Command="ApplicationCommands.Copy"/>
</ListView.InputBindings>
<ListView.CommandBindings>
<CommandBinding Command="ApplicationCommands.Copy" Executed="CopyCommand_Executed"/>
</ListView.CommandBindings>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Copiar coluna" Click="CopyMenu_Click"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<!-- Coluna do ícone de pasta (pequena) -->
<GridViewColumn Width="36" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<!-- Botão com ícone (emoji) — simples e confiável.
Pode trocar por uma imagem se preferir (Image/Path). -->
<Button Click="OpenFolderButton_Click"
ToolTip="Abrir pasta da unidade"
Padding="2"
Margin="2"
BorderThickness="0"
Background="Transparent"
Cursor="Hand"
Foreground="#FF042271"
HorizontalAlignment="Center"
VerticalAlignment="Center"
DataContext="{Binding}">
<TextBlock Text="📁" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!-- Demais colunas (TextBox readonly para seleção) -->
<GridViewColumn Header="Unidade" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Unidade}"
IsReadOnly="True"
Foreground="#FF042271"
BorderThickness="0"
Background="Transparent"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Cursor="IBeam"
Focusable="True"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Instalação" Width="120">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Codigo_Instalacao}"
IsReadOnly="True"
Foreground="#FF042271"
BorderThickness="0"
Background="Transparent"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Cursor="IBeam"
Focusable="True"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="CNPJ" Width="120">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding CNPJ_CPF}"
IsReadOnly="True"
Foreground="#FF042271"
BorderThickness="0"
Background="Transparent"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Cursor="IBeam"
Focusable="True"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Razão Social" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Razao_Social}"
IsReadOnly="True"
Foreground="#FF042271"
BorderThickness="0"
Background="Transparent"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Cursor="IBeam"
Focusable="True"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<!-- removido MouseDoubleClick -->
<!--<EventSetter Event="MouseDoubleClick" Handler="UnidadeListView_MouseDoubleClick" />-->
<EventSetter Event="KeyDown" Handler="UnidadeListView_EnterKeyDown" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
</Grid>
</Window>

398
MainWindow.xaml.cs Normal file
View File

@ -0,0 +1,398 @@
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Reflection;
using System.Windows.Controls.Primitives;
namespace BD_empresa
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private string? _lastClickedCellText;
public MainWindow()
{
InitializeComponent();
// Caminho do banco de dados Access
string accessDbPath = "X:\\Middle\\Informativo Setorial\\Modelo Word\\BD1_dados cadastrais e faturas.accdb";
var accessService = new Data.AccessService($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={accessDbPath};Jet OLEDB:Database Password=gds21");
DataContext = new ViewModels.MainWindowViewModel(accessService);
}
/// <summary>
/// Captura texto quando clica com o botão esquerdo
/// </summary>
private void UnidadesListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is ListView lv)
{
var pt = e.GetPosition(lv);
RecordCellTextFromPoint(lv, pt);
}
}
/// <summary>
/// Seleciona o item clicado com o botão direito e captura o texto da célula clicada
/// </summary>
private void UnidadesListView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is ListView lv)
{
var pt = e.GetPosition(lv);
var hit = VisualTreeHelper.HitTest(lv, pt);
var dep = hit?.VisualHit;
if (dep == null) return;
// Seleciona o item clicado
var lvi = FindAncestor<ListViewItem>(dep);
if (lvi != null) lvi.IsSelected = true;
// Se certo elemento TextBox foi clicado, foca ele (para permitir seleção/Ctrl+C)
var tbx = FindAncestor<TextBox>(dep) ?? FindDescendant<TextBox>(dep);
tbx?.Focus();
RecordCellTextFromPoint(lv, pt);
}
}
private void UnidadesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_lastClickedCellText = null;
}
/// <summary>
/// Executado quando o usuário pressiona Ctrl+C
/// </summary>
private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// sender normalmente é o ListView (porque definimos o CommandBinding nele).
var lv = sender as ListView ?? UnidadesListView;
CopyTextFromListView(lv);
}
/// <summary>
/// Executado pelo MenuItem do ContextMenu
/// </summary>
private void CopyMenu_Click(object sender, RoutedEventArgs e)
{
CopyTextFromListView(UnidadesListView);
}
/// <summary>
/// Copia para o clipboard com base no último texto clicado ou em fallback (primeira coluna)
/// </summary>
private void CopyTextFromListView(ListView? listView)
{
if (listView == null) return;
// 1) se o usuário clicou previamente em uma célula (direito/esquerdo), usamos esse texto
if (!string.IsNullOrEmpty(_lastClickedCellText))
{
try
{
Clipboard.SetText(_lastClickedCellText);
}
catch (Exception ex)
{
MessageBox.Show($"Erro ao copiar para o clipboard: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
return;
}
// 2) fallback: usa a primeira coluna (se houver) do item selecionado
if (listView.SelectedItem is Data.UnidadeSmart unidade)
{
var gv = listView.View as GridView;
if (gv?.Columns.Count > 0)
{
var firstCol = gv.Columns[0];
// tenta obter o DisplayMemberBinding.Path
if (firstCol.DisplayMemberBinding is System.Windows.Data.Binding b && !string.IsNullOrEmpty(b.Path?.Path))
{
var prop = unidade.GetType().GetProperty(b.Path.Path, BindingFlags.Public | BindingFlags.Instance);
if (prop != null)
{
var value = prop.GetValue(unidade)?.ToString() ?? string.Empty;
try
{
Clipboard.SetText(value);
}
catch (Exception ex)
{
MessageBox.Show($"Erro ao copiar para o clipboard: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
return;
}
}
// se não tiver DisplayMemberBinding (ou falhar), tenta buscar visualmente o TextBlock do ListViewItem
var lvi = listView.ItemContainerGenerator.ContainerFromItem(unidade) as ListViewItem;
var tb = FindDescendant<TextBlock>(lvi);
if (tb != null)
{
try
{
Clipboard.SetText(tb.Text);
}
catch (Exception ex)
{
MessageBox.Show($"Erro ao copiar para o clipboard: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
return;
}
}
// 3) fallback final: ToString do objeto
try
{
Clipboard.SetText(unidade?.ToString() ?? string.Empty);
}
catch (Exception ex)
{
MessageBox.Show($"Erro ao copiar para o clipboard: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// Faz hit-test em point e tenta achar o TextBlock responsável pela célula clicada;
/// grava em _lastClickedCellText.
/// </summary>
private void RecordCellTextFromPoint(ListView listView, Point point)
{
_lastClickedCellText = null;
var hit = VisualTreeHelper.HitTest(listView, point);
var dep = hit?.VisualHit;
if (dep == null) return;
// 1) Procura TextBox (nossa célula agora é TextBox readonly)
var tbx = FindAncestor<TextBox>(dep) ?? FindDescendant<TextBox>(dep);
if (tbx != null)
{
// se houver seleção, prioriza a seleção
if (!string.IsNullOrEmpty(tbx.SelectedText))
_lastClickedCellText = tbx.SelectedText;
else
_lastClickedCellText = tbx.Text;
return;
}
// 2) Procura TextBlock (fallback)
var tblock = FindAncestor<TextBlock>(dep) ?? FindDescendant<TextBlock>(dep);
if (tblock != null)
{
_lastClickedCellText = tblock.Text;
return;
}
// 3) Fallback: procura container e, dentro dele, tenta TextBox primeiro, depois TextBlock
DependencyObject? container = FindAncestor<ContentPresenter>(dep);
container ??= FindAncestor<GridViewRowPresenter>(dep);
if (container != null)
{
var innerTbx = FindDescendant<TextBox>(container);
if (innerTbx != null)
{
_lastClickedCellText = !string.IsNullOrEmpty(innerTbx.SelectedText) ? innerTbx.SelectedText : innerTbx.Text;
return;
}
var innerTblock = FindDescendant<TextBlock>(container);
if (innerTblock != null)
{
_lastClickedCellText = innerTblock.Text;
return;
}
}
// 4) último recurso: se um ListViewItem foi selecionado, tenta pegar a primeira coluna via binding (fallback anterior)
if (listView.SelectedItem is Data.UnidadeSmart unidade)
{
var gv = listView.View as GridView;
if (gv?.Columns.Count > 0)
{
var firstCol = gv.Columns[0];
if (firstCol.DisplayMemberBinding is System.Windows.Data.Binding b && !string.IsNullOrEmpty(b.Path?.Path))
{
var prop = unidade.GetType().GetProperty(b.Path.Path, BindingFlags.Public | BindingFlags.Instance);
if (prop != null)
{
var value = prop.GetValue(unidade)?.ToString() ?? string.Empty;
_lastClickedCellText = value;
}
}
}
}
}
// Helpers (coloque-os se já não existirem):
private static T? FindAncestor<T>(DependencyObject? current) where T : DependencyObject
{
while (current != null)
{
if (current is T typed) return typed;
current = VisualTreeHelper.GetParent(current);
}
return null;
}
private static T? FindDescendant<T>(DependencyObject? root) where T : DependencyObject
{
if (root == null) return null;
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
var node = queue.Dequeue();
var childrenCount = VisualTreeHelper.GetChildrenCount(node);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(node, i);
if (child is T found) return found;
queue.Enqueue(child);
}
}
return null;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
txtEmpresaSearch.Focus();
}
private void UnidadeListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (sender is ListViewItem listViewItem && listViewItem.Content is Data.UnidadeSmart unidade && !string.IsNullOrWhiteSpace(unidade.Caminho_NFs))
{
string? parentDirectory = System.IO.Path.GetDirectoryName(unidade.Caminho_NFs);
if (!string.IsNullOrWhiteSpace(parentDirectory))
{
try
{
System.Diagnostics.Process.Start("explorer.exe", parentDirectory);
}
catch (System.Exception ex)
{
MessageBox.Show($"Não foi possível abrir a pasta: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
private void UnidadeListView_EnterKeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.Key != Key.Enter) { return; }
// DataContext do Button será a UnidadeSmart relacionada à linha
var lvi = sender as ListViewItem;
if (lvi?.Content is not Data.UnidadeSmart unidade)
{
MessageBox.Show("Não foi possível identificar a unidade.", "Erro", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var caminho = unidade.Caminho_NFs;
if (string.IsNullOrWhiteSpace(caminho))
{
MessageBox.Show("Não há caminho definido para essa unidade.", "Aviso", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
string folderToOpen;
// Se o caminho for um arquivo (provavelmente um arquivo NFe), abre o diretório pai.
if (System.IO.File.Exists(caminho))
{
folderToOpen = System.IO.Path.GetDirectoryName(caminho) ?? caminho;
}
else
{
// tenta obter diretório do caminho (caso usuário tenha colocado um arquivo inexistente ou caminho parcial)
var parent = System.IO.Path.GetDirectoryName(caminho);
if (!string.IsNullOrWhiteSpace(parent) && System.IO.Directory.Exists(parent))
folderToOpen = parent;
else
{
MessageBox.Show($"O caminho informado não existe: {caminho}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
// Abre o Explorer na pasta encontrada (UseShellExecute = true para abrir paths corretamente)
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = $"\"{folderToOpen}\"",
UseShellExecute = true
};
System.Diagnostics.Process.Start(psi);
}
catch (System.Exception ex)
{
MessageBox.Show($"Não foi possível abrir a pasta: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void OpenFolderButton_Click(object sender, RoutedEventArgs e)
{
try
{
// DataContext do Button será a UnidadeSmart relacionada à linha
var btn = sender as Button;
if (btn?.DataContext is not Data.UnidadeSmart unidade)
{
MessageBox.Show("Não foi possível identificar a unidade.", "Erro", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var caminho = unidade.Caminho_NFs;
if (string.IsNullOrWhiteSpace(caminho))
{
MessageBox.Show("Não há caminho definido para essa unidade.", "Aviso", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
string folderToOpen;
// Se o caminho for um arquivo (provavelmente um arquivo NFe), abre o diretório pai.
if (System.IO.File.Exists(caminho))
{
folderToOpen = System.IO.Path.GetDirectoryName(caminho) ?? caminho;
}
else
{
// tenta obter diretório do caminho (caso usuário tenha colocado um arquivo inexistente ou caminho parcial)
var parent = System.IO.Path.GetDirectoryName(caminho);
if (!string.IsNullOrWhiteSpace(parent) && System.IO.Directory.Exists(parent))
folderToOpen = parent;
else
{
MessageBox.Show($"O caminho informado não existe: {caminho}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
// Abre o Explorer na pasta encontrada (UseShellExecute = true para abrir paths corretamente)
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = $"\"{folderToOpen}\"",
UseShellExecute = true
};
System.Diagnostics.Process.Start(psi);
}
catch (System.Exception ex)
{
MessageBox.Show($"Não foi possível abrir a pasta: {ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

View File

@ -1,119 +0,0 @@
using System.Data.OleDb;
using System.Diagnostics;
class Download_BD
{
static void Main()
{
string nome_entrada;
string strQRY;
string[] charsToRemove = new string[] { "@", ",", ".", ";", "'", "-", "/" };
nome_entrada = "aaa";
while (nome_entrada.Length != 0)
{
Console.WriteLine("Digite 'c' - caso que queira pesquisar por CNPJ ou 'u' - caso que queira pesquisar por UC e pressione Enter");
Console.WriteLine("Para pesquisar por Razao Social/Nome, escreva o nome da empresa a ser procurada (min: 4 digitos) e pressione Enter - deixe em branco para sair:");
nome_entrada = Console.ReadLine();
switch (nome_entrada.ToUpper())
{
case "U":
Console.WriteLine("Digite a UC a ser procurada (completa) e pressione Enter:");
nome_entrada = Console.ReadLine();
if (nome_entrada.Length >= 4)
{
strQRY = "SELECT DISTINCT Gestao, Cliente, razao_social, Caminho_NFs FROM Dados_cadastrais WHERE Codigo_Instalacao = " + "\"" + nome_entrada + "\"";
strQRY += " ORDER BY gestao, cliente, razao_social";
Acessar_BD(strQRY);
}
break;
case "C":
Console.WriteLine("Digite o CNPJ a ser procurado (14 dig) e pressione Enter:");
nome_entrada = Console.ReadLine();
foreach (var c in charsToRemove)
{
nome_entrada = nome_entrada.Replace(c, string.Empty);
}
while (nome_entrada.Length < 14)
{
nome_entrada = "0" + nome_entrada;
}
strQRY = "SELECT DISTINCT Gestao, Cliente, razao_social, Caminho_NFs FROM Dados_cadastrais WHERE CNPJ_CPF = " + "\"" + nome_entrada + "\"";
strQRY += " ORDER BY gestao, cliente, razao_social";
//Console.WriteLine(strQRY);
Acessar_BD(strQRY);
break;
default:
if (nome_entrada.Length >= 4)
{
strQRY = "SELECT DISTINCT Gestao, Cliente, razao_social, Caminho_NFs FROM Dados_cadastrais WHERE Cliente ALike " + "\"" + "%" + nome_entrada + "%" + "\"";
strQRY += " UNION " + " SELECT DISTINCT Gestao, Cliente, razao_social, Caminho_NFs FROM Dados_cadastrais WHERE Razao_social ALike " + "\"" + "%" + nome_entrada + "%" + "\"";
strQRY += " ORDER BY gestao, cliente, razao_social";
Acessar_BD(strQRY);
}
break;
}
}
Console.WriteLine("Programa encerrado.");
return;
}
static void Acessar_BD(string strSQL)
{
int cont_result;
string prt_str;
int n_pasta;
bool teste_par;
string teste_cli = "";
List<string> caminhos = new List<string>();
OleDbConnection conn = new("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb;Jet OLEDB:Database Password=gds21");
conn.Open();
OleDbCommand tbEmpresas = new(strSQL, conn);
OleDbDataReader reader = tbEmpresas.ExecuteReader();
prt_str = "\n" + String.Format("{0,-4} | {1,-8} | {2,-25}| {3,-30}", "N", "Gestao", "Nome cliente", "Razao social");
cont_result = 0;
while (reader.Read() && cont_result < 11)
{
if (reader["Cliente"].ToString() != teste_cli)
{
teste_cli = reader["Cliente"].ToString();
cont_result++;
prt_str += "\n" + String.Format("{0,-4} | {1,-8} | {2,-25}| {3,-30}", cont_result, reader["gestao"].ToString(), reader["Cliente"].ToString(), reader["Razao_Social"].ToString());
caminhos.Add(reader["Caminho_NFs"].ToString());
}
}
prt_str += "\n\n";
conn.Close();
if (cont_result >= 10)
{
Console.WriteLine("\nForam encontrados mais de {0} resultados para o nome pesquisado, buscou-se por razao social e nome do cliente.\n", cont_result - 1);
Console.WriteLine("Especificar melhor o nome pesquisado para que os resultados possam sem exibidos.\n");
}
else if (cont_result > 0)
{
Console.WriteLine("\nForam encontrados {0} resultados para os parametros procurados.\n", cont_result);
Console.WriteLine(prt_str);
Console.WriteLine("Digite um numero da lista para acessar a pasta do cliente (só funcionará se possuir acesso):");
teste_par = Int32.TryParse(Console.ReadLine(), out n_pasta);
if (teste_par && n_pasta <= cont_result)
{
//Console.WriteLine(caminhos[n_pasta - 1]);
Process.Start("explorer.exe", caminhos[n_pasta - 1]);
}
else
{
Console.WriteLine("Numero invalido");
}
}
return;
}
}

View File

@ -0,0 +1,178 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;
using BD_empresa.Data;
namespace BD_empresa.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private readonly IClienteRepository _clienteRepository;
private string? _searchEmpresaText;
private string? _searchUnidadeText;
private bool _isLoading;
private string? _errorMessage;
private UnidadeSmart? _selectedCliente;
private List<UnidadeSmart> _allUnidades = [];
public ObservableCollection<UnidadeSmart> Clientes { get; } = [];
public ObservableCollection<UnidadeSmart> UnidadesSelecionadas { get; } = [];
public string? SearchEmpresaText
{
get => _searchEmpresaText;
set
{
if (_searchEmpresaText != value)
{
_searchEmpresaText = value;
SearchUnidadeText = null; // Limpa o filtro de unidades ao mudar a empresa
OnPropertyChanged();
FiltrarClientes();
AtualizarUnidadesSelecionadas();
}
}
}
public string? SearchUnidadeText
{
get => _searchUnidadeText;
set
{
_searchUnidadeText = value;
OnPropertyChanged();
AtualizarUnidadesSelecionadas();
}
}
public UnidadeSmart? SelectedCliente
{
get => _selectedCliente;
set
{
if (_selectedCliente != value)
{
_selectedCliente = value;
OnPropertyChanged();
AtualizarUnidadesSelecionadas();
}
}
}
public bool IsLoading
{
get => _isLoading;
set { _isLoading = value; OnPropertyChanged(); }
}
public string? ErrorMessage
{
get => _errorMessage;
set { _errorMessage = value; OnPropertyChanged(); }
}
public ICommand RefreshCommand { get; }
public MainWindowViewModel(IClienteRepository clienteRepository)
{
_clienteRepository = clienteRepository;
RefreshCommand = new RelayCommand(async _ => await RefreshAsync());
_ = RefreshAsync();
}
private void FiltrarClientes()
{
Clientes.Clear();
ErrorMessage = null;
var _search = SearchEmpresaText;
if (string.IsNullOrWhiteSpace(_search))
{
_search = string.Empty;
}
var filtrados = _allUnidades
.Where(c => (c.CNPJ_CPF?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (c.Codigo_Instalacao?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (c.Razao_Social?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (c.Unidade?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (c.Cliente?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false))
.GroupBy(c => c.Cod_Smart_cliente)
.Select(g => g.First())
.Take(9)
.ToList();
foreach (var cliente in filtrados)
Clientes.Add(cliente);
if (Clientes.Count == 0)
ErrorMessage = "Nenhum resultado encontrado.";
else
SelectedCliente = Clientes.First();
}
private void AtualizarUnidadesSelecionadas()
{
UnidadesSelecionadas.Clear();
ErrorMessage = null;
var _search = SearchUnidadeText;
if (string.IsNullOrWhiteSpace(_search))
{
_search = "";
}
var cliente = SelectedCliente;
if (Clientes.Count > 0)
{
cliente ??= Clientes.First();
var unidades = _allUnidades.Where(c => c.Cod_Smart_cliente == cliente.Cod_Smart_cliente)
.Where(c => (c.CNPJ_CPF?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (c.Codigo_Instalacao?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false)
|| (c.Unidade?.Contains(_search, StringComparison.OrdinalIgnoreCase) ?? false))
.ToList();
foreach (var unidade in unidades)
UnidadesSelecionadas.Add(unidade);
}
if (UnidadesSelecionadas.Count == 0)
ErrorMessage = "Nenhum resultado encontrado.";
}
public async Task RefreshAsync()
{
IsLoading = true;
ErrorMessage = null;
try
{
var results = await _clienteRepository.GetAllClientesAsync();
_allUnidades = [.. results.OrderBy(u => u.Gestao).ThenBy(c => c.Cliente).ThenBy(g => g.Unidade)];
FiltrarClientes();
AtualizarUnidadesSelecionadas();
}
catch (Exception ex)
{
ErrorMessage = $"Erro: {ex.Message}";
}
finally
{
IsLoading = false;
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// RelayCommand para ICommand
public class RelayCommand(Func<object?, Task> execute, Predicate<object?>? canExecute = null) : ICommand
{
private readonly Func<object?, Task> _execute = execute;
private readonly Predicate<object?>? _canExecute = canExecute;
public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true;
public async void Execute(object? parameter) => await _execute(parameter);
public event EventHandler? CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}

BIN
marca.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
marca.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB