Quero que o usuário seja capaz de colocar a célula no modo de edição e destacar a linha na qual a célula está contida com um único clique. Por padrão, é um clique duplo.
Como faço para substituir ou implementar isso?
Quero que o usuário seja capaz de colocar a célula no modo de edição e destacar a linha na qual a célula está contida com um único clique. Por padrão, é um clique duplo.
Como faço para substituir ou implementar isso?
Respostas:
Aqui está como resolvi esse problema:
<DataGrid DataGridCell.Selected="DataGridCell_Selected"
ItemsSource="{Binding Source={StaticResource itemView}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Nom" Binding="{Binding Path=Name}"/>
<DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"/>
</DataGrid.Columns>
</DataGrid>
Este DataGrid está vinculado a um CollectionViewSource (contendo objetos Person fictícios ).
A mágica acontece lá: DataGridCell.Selected = "DataGridCell_Selected" .
Simplesmente conecto o Evento Selecionado da célula DataGrid e chamo BeginEdit () no DataGrid.
Aqui está o código por trás do manipulador de eventos:
private void DataGridCell_Selected(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
}
}
SelectionUnit
propriedade no DataGrid como Cell
.
grd.BeginEdit(e)
, quero que o TextBox dessa célula tenha o foco. Como eu posso fazer isso? Eu tentei chamar FindName("txtBox")
o DataGridCell e o DataGrid, mas ele retornou null para mim.
A resposta de Micael Bergeron foi um bom começo para eu encontrar uma solução que está funcionando para mim. Para permitir a edição com um único clique também para células na mesma linha que já estão no modo de edição, tive que ajustar um pouco. Usar o SelectionUnit Cell não era uma opção para mim.
Em vez de usar o evento DataGridCell.Selected, que só é disparado pela primeira vez em que uma célula da linha é clicada, usei o evento DataGridCell.GotFocus.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Se você fizer isso, você terá sempre a célula correta focada e no modo de edição, mas nenhum controle na célula estará focado, isso eu resolvi assim
private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
if (control != null)
{
control.Focus();
}
}
}
private T GetFirstChildByType<T>(DependencyObject prop) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(prop); i++)
{
DependencyObject child = VisualTreeHelper.GetChild((prop), i) as DependencyObject;
if (child == null)
continue;
T castedProp = child as T;
if (castedProp != null)
return castedProp;
castedProp = GetFirstChildByType<T>(child);
if (castedProp != null)
return castedProp;
}
return null;
}
De: http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing
XAML:
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type dg:DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
CÓDIGO POR TRÁS:
//
// SINGLE CLICK EDITING
//
private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
{
if (!cell.IsFocused)
{
cell.Focus();
}
DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
if (dataGrid != null)
{
if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
{
if (!cell.IsSelected)
cell.IsSelected = true;
}
else
{
DataGridRow row = FindVisualParent<DataGridRow>(cell);
if (row != null && !row.IsSelected)
{
row.IsSelected = true;
}
}
}
}
}
static T FindVisualParent<T>(UIElement element) where T : UIElement
{
UIElement parent = element;
while (parent != null)
{
T correctlyTyped = parent as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
A solução de http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing funcionou muito bem para mim, mas eu a habilitei para cada DataGrid usando um estilo definido em um ResourceDictionary. Para usar manipuladores em dicionários de recursos, você precisa adicionar um arquivo code-behind a ele. Veja como você faz:
Este é um Dicionário de recursos DataGridStyles.xaml :
<ResourceDictionary x:Class="YourNamespace.DataGridStyles"
x:ClassModifier="public"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="DataGrid">
<!-- Your DataGrid style definition goes here -->
<!-- Cell style -->
<Setter Property="CellStyle">
<Setter.Value>
<Style TargetType="DataGridCell">
<!-- Your DataGrid Cell style definition goes here -->
<!-- Single Click Editing -->
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="DataGridCell_PreviewMouseLeftButtonDown" />
</Style>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Observe o atributo x: Class no elemento raiz. Crie um arquivo de classe. Neste exemplo, seria DataGridStyles.xaml.cs . Coloque este código dentro de:
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
namespace YourNamespace
{
partial class DataGridStyles : ResourceDictionary
{
public DataGridStyles()
{
InitializeComponent();
}
// The code from the myermian's answer goes here.
}
eu prefiro assim com base na sugestão de Dušan Knežević. você clica em e pronto))
<DataGrid.Resources>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsReadOnly"
Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="IsEditing"
Value="True" />
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
Resolvi isso adicionando um gatilho que define a propriedade IsEditing de DataGridCell como True quando o mouse está sobre ele. Isso resolveu a maioria dos meus problemas. Também funciona com caixas de combinação.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Procuro editar célula em um único clique no MVVM e esta é uma outra maneira de fazer isso.
Adicionando comportamento no xaml
<UserControl xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:myBehavior="clr-namespace:My.Namespace.To.Behavior">
<DataGrid>
<i:Interaction.Behaviors>
<myBehavior:EditCellOnSingleClickBehavior/>
</i:Interaction.Behaviors>
</DataGrid>
</UserControl>
A classe EditCellOnSingleClickBehavior extend System.Windows.Interactivity.Behavior;
public class EditCellOnSingleClick : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.LoadingRow += this.OnLoadingRow;
this.AssociatedObject.UnloadingRow += this.OnUnloading;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.LoadingRow -= this.OnLoadingRow;
this.AssociatedObject.UnloadingRow -= this.OnUnloading;
}
private void OnLoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.GotFocus += this.OnGotFocus;
}
private void OnUnloading(object sender, DataGridRowEventArgs e)
{
e.Row.GotFocus -= this.OnGotFocus;
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
this.AssociatedObject.BeginEdit(e);
}
}
Voila!
Existem dois problemas com a resposta do usuário2134678. Um é muito pequeno e não tem efeito funcional. O outro é bastante significativo.
O primeiro problema é que o GotFocus está realmente sendo chamado contra o DataGrid, não o DataGridCell na prática. O qualificador DataGridCell no XAML é redundante.
O principal problema que encontrei com a resposta é que o comportamento da tecla Enter está quebrado. Enter deve mover você para a próxima célula abaixo da célula atual no comportamento normal do DataGrid. No entanto, o que realmente acontece nos bastidores é que o evento GotFocus será chamado duas vezes. Uma vez a célula atual perdendo o foco e outra vez a nova célula ganhando foco. Mas, enquanto BeginEdit for chamado na primeira célula, a próxima célula nunca será ativada. O resultado é que você tem edição com um clique, mas qualquer um que não esteja literalmente clicando na grade ficará incomodado, e um designer de interface de usuário não deve presumir que todos os usuários estão usando mouses. (Os usuários de teclado podem contornar isso usando Tab, mas isso ainda significa que eles estão se esforçando para não precisar fazer isso.)
Então, a solução para esse problema? Manipule o evento KeyDown para a célula e se a tecla for a tecla Enter, defina um sinalizador que impeça o BeginEdit de disparar na primeira célula. Agora a tecla Enter se comporta como deveria.
Para começar, adicione o seguinte estilo ao seu DataGrid:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Aplique esse estilo à propriedade "CellStyle" nas colunas para as quais deseja habilitar um clique.
Então, no código atrás, você tem o seguinte em seu manipulador GotFocus (observe que estou usando VB aqui porque é isso que nosso cliente de "solicitação de grade de dados com um clique" queria como linguagem de desenvolvimento):
Private _endEditing As Boolean = False
Private Sub DataGrid_GotFocus(ByVal sender As Object, ByVal e As RoutedEventArgs)
If Me._endEditing Then
Me._endEditing = False
Return
End If
Dim cell = TryCast(e.OriginalSource, DataGridCell)
If cell Is Nothing Then
Return
End If
If cell.IsReadOnly Then
Return
End If
DirectCast(sender, DataGrid).BeginEdit(e)
.
.
.
Em seguida, você adiciona seu manipulador para o evento KeyDown:
Private Sub DataGridCell_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.Key = Key.Enter Then
Me._endEditing = True
End If
End Sub
Agora você tem um DataGrid que não mudou nenhum comportamento fundamental da implementação pronta para uso e ainda oferece suporte à edição com um único clique.
Sei que estou um pouco atrasado para a festa, mas tive o mesmo problema e encontrei uma solução diferente:
public class DataGridTextBoxColumn : DataGridBoundColumn
{
public DataGridTextBoxColumn():base()
{
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
throw new NotImplementedException("Should not be used.");
}
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var control = new TextBox();
control.Style = (Style)Application.Current.TryFindResource("textBoxStyle");
control.FontSize = 14;
control.VerticalContentAlignment = VerticalAlignment.Center;
BindingOperations.SetBinding(control, TextBox.TextProperty, Binding);
control.IsReadOnly = IsReadOnly;
return control;
}
}
<DataGrid Grid.Row="1" x:Name="exportData" Margin="15" VerticalAlignment="Stretch" ItemsSource="{Binding CSVExportData}" Style="{StaticResource dataGridStyle}">
<DataGrid.Columns >
<local:DataGridTextBoxColumn Header="Sample ID" Binding="{Binding SampleID}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Analysis Date" Binding="{Binding Date}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Test" Binding="{Binding Test}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Comment" Binding="{Binding Comment}"></local:DataGridTextBoxColumn>
</DataGrid.Columns>
</DataGrid>
Como você pode ver, escrevi meu próprio DataGridTextColumn herdando tudo do DataGridBoundColumn. Substituindo o Método GenerateElement e retornando um controle Textbox ali mesmo, o Método para gerar o Elemento de Edição nunca é chamado. Em um projeto diferente, usei isso para implementar uma coluna Datepicker, então isso deve funcionar para caixas de seleção e comboboxes também.
Isso não parece afetar o comportamento do restante dos datagrids ... Pelo menos, não notei nenhum efeito colateral nem recebi feedback negativo até agora.
Uma solução simples se você concordar que sua célula permaneça uma caixa de texto (sem distinção entre o modo de edição e não edição). Desta forma, a edição com um único clique funciona imediatamente. Isso funciona com outros elementos como combobox e botões também. Caso contrário, use a solução abaixo da atualização.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Tentei tudo o que encontrei aqui e no google e até tentei criar minhas próprias versões. Mas cada resposta / solução funcionou principalmente para colunas de caixa de texto, mas não funcionou com todos os outros elementos (caixas de seleção, caixas de combinação, colunas de botões), ou mesmo quebrou essas outras colunas de elemento ou teve alguns outros efeitos colaterais. Obrigado microsoft por fazer o datagrid se comportar daquela maneira feia e nos forçar a criar esses hacks. Por isso decidi fazer uma versão que pode ser aplicada com um estilo a uma coluna de caixa de texto diretamente sem afetar as outras colunas.
Usei esta solução e a resposta de @my e modifiquei-os para um comportamento vinculado. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Adicione este estilo. Isso BasedOn
é importante quando você usa alguns estilos sofisticados para seu datagrid e não quer perdê-los.
<Window.Resources>
<Style x:Key="SingleClickEditStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="local:DataGridTextBoxSingleClickEditBehavior.Enable" Value="True" />
</Style>
</Window.Resources>
Aplique o estilo CellStyle
a cada um de seus DataGridTextColumns
assim:
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
E agora adicione esta classe ao mesmo namespace que seu MainViewModel (ou um namespace diferente. Mas então você precisará usar um outro prefixo de namespace que não seja local
). Bem-vindo ao feio mundo do código clichê de comportamentos anexados.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace YourMainViewModelNameSpace
{
public static class DataGridTextBoxSingleClickEditBehavior
{
public static readonly DependencyProperty EnableProperty = DependencyProperty.RegisterAttached(
"Enable",
typeof(bool),
typeof(DataGridTextBoxSingleClickEditBehavior),
new FrameworkPropertyMetadata(false, OnEnableChanged));
public static bool GetEnable(FrameworkElement frameworkElement)
{
return (bool) frameworkElement.GetValue(EnableProperty);
}
public static void SetEnable(FrameworkElement frameworkElement, bool value)
{
frameworkElement.SetValue(EnableProperty, value);
}
private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DataGridCell dataGridCell)
dataGridCell.PreviewMouseLeftButtonDown += DataGridCell_PreviewMouseLeftButtonDown;
}
private static void DataGridCell_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
EditCell(sender as DataGridCell, e);
}
private static void EditCell(DataGridCell dataGridCell, RoutedEventArgs e)
{
if (dataGridCell == null || dataGridCell.IsEditing || dataGridCell.IsReadOnly)
return;
if (dataGridCell.IsFocused == false)
dataGridCell.Focus();
var dataGrid = FindVisualParent<DataGrid>(dataGridCell);
dataGrid?.BeginEdit(e);
}
private static T FindVisualParent<T>(UIElement element) where T : UIElement
{
var parent = VisualTreeHelper.GetParent(element) as UIElement;
while (parent != null)
{
if (parent is T parentWithCorrectType)
return parentWithCorrectType;
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
}
}
<DataGridComboBoxColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="cal:Message.Attach"
Value="[Event MouseLeftButtonUp] = [Action ReachThisMethod($source)]"/>
</Style>
</DataGridComboBoxColumn.CellStyle>
public void ReachThisMethod(object sender)
{
((System.Windows.Controls.DataGridCell)(sender)).IsEditing = true;
}