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.
This commit is contained in:
parent
981074871a
commit
6b791aa3d5
@ -89,7 +89,21 @@
|
|||||||
Text="{Binding SearchUnidadeText, UpdateSourceTrigger=PropertyChanged}" />
|
Text="{Binding SearchUnidadeText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock Text="Unidades da empresa selecionada (Duplo-clique para abrir pasta):" Margin="10,0,10,0" FontWeight="Bold" Grid.Row="1"/>
|
<TextBlock Text="Unidades da empresa selecionada (Duplo-clique para abrir pasta):" Margin="10,0,10,0" FontWeight="Bold" Grid.Row="1"/>
|
||||||
<ListView ItemsSource="{Binding UnidadesSelecionadas}" Margin="10" Grid.Row="2">
|
<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>
|
<ListView.View>
|
||||||
<GridView>
|
<GridView>
|
||||||
<GridViewColumn Header="Unidade" DisplayMemberBinding="{Binding Unidade}" Width="200" />
|
<GridViewColumn Header="Unidade" DisplayMemberBinding="{Binding Unidade}" Width="200" />
|
||||||
|
|||||||
@ -8,6 +8,8 @@ using System.Windows.Media;
|
|||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
|
||||||
namespace BD_empresa
|
namespace BD_empresa
|
||||||
{
|
{
|
||||||
@ -16,6 +18,7 @@ namespace BD_empresa
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
private string? _lastClickedCellText;
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
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");
|
var accessService = new Data.AccessService($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={accessDbPath};Jet OLEDB:Database Password=gds21");
|
||||||
DataContext = new ViewModels.MainWindowViewModel(accessService);
|
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);
|
||||||
|
// 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<ListViewItem>(dep);
|
||||||
|
if (lvi != null)
|
||||||
|
{
|
||||||
|
lvi.IsSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordCellTextFromPoint(lv, pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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))
|
||||||
|
{
|
||||||
|
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<TextBlock>(lvi);
|
||||||
|
if (tb != null)
|
||||||
|
{
|
||||||
|
Clipboard.SetText(tb.Text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) fallback final: ToString do objeto
|
||||||
|
Clipboard.SetText(unidade?.ToString() ?? string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
|
||||||
|
// sobe a árvore procurando TextBlock (geralmente GridView cria TextBlock)
|
||||||
|
var tb = FindAncestor<TextBlock>(dep);
|
||||||
|
if (tb != null)
|
||||||
|
{
|
||||||
|
_lastClickedCellText = tb.Text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// às vezes o TextBlock está abaixo de um Border/ContentPresenter
|
||||||
|
DependencyObject? container = FindAncestor<ContentPresenter>(dep);
|
||||||
|
container ??= FindAncestor<GridViewRowPresenter>(dep);
|
||||||
|
|
||||||
|
if (container != null)
|
||||||
|
{
|
||||||
|
var tb2 = FindDescendant<TextBlock>(container);
|
||||||
|
if (tb2 != null)
|
||||||
|
{
|
||||||
|
_lastClickedCellText = tb2.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encontra primeiro ancestral do tipo T.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encontra primeiro descendente do tipo T usando DFS (útil para achar TextBlock dentro de um visual container)
|
||||||
|
/// </summary>
|
||||||
|
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)
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
txtEmpresaSearch.Focus();
|
txtEmpresaSearch.Focus();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user