From 6b791aa3d563b448b3d96a9fb0792b4fbdbdf46d Mon Sep 17 00:00:00 2001 From: Giuliano Paschoalino Date: Tue, 30 Sep 2025 16:23:20 -0300 Subject: [PATCH] =?UTF-8?q?Adiciona=20suporte=20para=20copiar=20texto=20de?= =?UTF-8?q?=20c=C3=A9lulas=20no=20ListView?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- MainWindow.xaml | 16 ++++- MainWindow.xaml.cs | 168 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/MainWindow.xaml b/MainWindow.xaml index 073968b..5b096a5 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -89,7 +89,21 @@ Text="{Binding SearchUnidadeText, UpdateSourceTrigger=PropertyChanged}" /> - + + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 1ecb6f8..d5d1bb2 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -8,6 +8,8 @@ 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 { @@ -16,6 +18,7 @@ namespace BD_empresa /// public partial class MainWindow : Window { + private string? _lastClickedCellText; public MainWindow() { InitializeComponent(); @@ -24,6 +27,171 @@ namespace BD_empresa var accessService = new Data.AccessService($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={accessDbPath};Jet OLEDB:Database Password=gds21"); DataContext = new ViewModels.MainWindowViewModel(accessService); } + /// + /// Captura texto quando clica com o botão esquerdo + /// + private void UnidadesListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (sender is ListView lv) + { + var pt = e.GetPosition(lv); + RecordCellTextFromPoint(lv, pt); + } + } + + /// + /// Seleciona o item clicado com o botão direito e captura o texto da célula clicada + /// + private void UnidadesListView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) + { + if (sender is ListView lv) + { + var pt = e.GetPosition(lv); + // Se clicou em um ListViewItem, seleciona-o (comportamento útil para context menu) + var hit = VisualTreeHelper.HitTest(lv, pt); + var dep = hit?.VisualHit; + var lvi = FindAncestor(dep); + if (lvi != null) + { + lvi.IsSelected = true; + } + + RecordCellTextFromPoint(lv, pt); + } + } + + /// + /// Executado quando o usuário pressiona Ctrl+C + /// + 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); + } + + /// + /// Executado pelo MenuItem do ContextMenu + /// + private void CopyMenu_Click(object sender, RoutedEventArgs e) + { + CopyTextFromListView(UnidadesListView); + } + + /// + /// Copia para o clipboard com base no último texto clicado ou em fallback (primeira coluna) + /// + 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)) + { + Clipboard.SetText(_lastClickedCellText); + 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; + Clipboard.SetText(value); + 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(lvi); + if (tb != null) + { + Clipboard.SetText(tb.Text); + return; + } + } + + // 3) fallback final: ToString do objeto + Clipboard.SetText(unidade?.ToString() ?? string.Empty); + } + } + + /// + /// Faz hit-test em point e tenta achar o TextBlock responsável pela célula clicada; + /// grava em _lastClickedCellText. + /// + private void RecordCellTextFromPoint(ListView listView, Point point) + { + _lastClickedCellText = null; + var hit = VisualTreeHelper.HitTest(listView, point); + var dep = hit?.VisualHit; + if (dep == null) return; + + // sobe a árvore procurando TextBlock (geralmente GridView cria TextBlock) + var tb = FindAncestor(dep); + if (tb != null) + { + _lastClickedCellText = tb.Text; + return; + } + + // às vezes o TextBlock está abaixo de um Border/ContentPresenter + DependencyObject? container = FindAncestor(dep); + container ??= FindAncestor(dep); + + if (container != null) + { + var tb2 = FindDescendant(container); + if (tb2 != null) + { + _lastClickedCellText = tb2.Text; + } + } + } + + /// + /// Encontra primeiro ancestral do tipo T. + /// + private static T? FindAncestor(DependencyObject? current) where T : DependencyObject + { + while (current != null) + { + if (current is T typed) return typed; + current = VisualTreeHelper.GetParent(current); + } + return null; + } + + /// + /// Encontra primeiro descendente do tipo T usando DFS (útil para achar TextBlock dentro de um visual container) + /// + private static T? FindDescendant(DependencyObject? root) where T : DependencyObject + { + if (root == null) return null; + var queue = new Queue(); + 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();