Quiero que el usuario pueda poner la celda en modo de edición y resaltar la fila en la que está contenida la celda con un solo clic. Por defecto, esto es doble clic.
¿Cómo anulo o implemento esto?
Quiero que el usuario pueda poner la celda en modo de edición y resaltar la fila en la que está contenida la celda con un solo clic. Por defecto, esto es doble clic.
¿Cómo anulo o implemento esto?
Respuestas:
Así es como resolví este 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 CollectionViewSource (que contiene objetos Person ficticios ).
La magia sucede allí: DataGridCell.Selected = "DataGridCell_Selected" .
Simplemente engancho el evento seleccionado de la celda DataGrid y llamo a BeginEdit () en el DataGrid.
Aquí está el código subyacente para el controlador 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
propiedad en DataGrid en Cell
.
grd.BeginEdit(e)
, quiero que el TextBox en esa celda tenga el foco. ¿Cómo puedo hacer eso? Intenté llamar FindName("txtBox")
tanto a DataGridCell como a DataGrid, pero me devuelve nulo.
La respuesta de Micael Bergeron fue un buen comienzo para encontrar una solución que me funcione. Para permitir la edición con un solo clic también para las celdas en la misma fila que ya está en modo de edición, tuve que ajustarlo un poco. Usar SelectionUnit Cell no fue una opción para mí.
En lugar de usar el evento DataGridCell.Selected, que solo se activa por primera vez, se hace clic en la celda de una fila, utilicé el evento DataGridCell.GotFocus.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Si lo hace, siempre tendrá la celda correcta enfocada y en modo de edición, pero no se enfocará ningún control en la celda, esto lo resolví así
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 DETRÁ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;
}
La solución de http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing funcionó muy bien para mí, pero la habilité para cada DataGrid usando un estilo definido en un ResourceDictionary. Para utilizar controladores en los diccionarios de recursos, debe agregarle un archivo de código subyacente. Así es como lo haces:
Este es un diccionario 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>
Tenga en cuenta el atributo x: Class en el elemento raíz. Crea un archivo de clase. En este ejemplo, sería DataGridStyles.xaml.cs . Pon este código dentro:
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.
}
Prefiero esta forma según la sugerencia de Dušan Knežević. haces clic en y eso es todo))
<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>
Lo resolví agregando un disparador que establece la propiedad IsEditing de DataGridCell en True cuando el mouse está sobre él. Resolvió la mayoría de mis problemas. También funciona con cuadros combinados.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Estoy buscando editar la celda con un solo clic en MVVM y esta es otra forma de hacerlo.
Agregar comportamiento en 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>
La clase EditCellOnSingleClickBehavior extiende 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!
Hay dos problemas con la respuesta de user2134678. Uno es muy pequeño y no tiene ningún efecto funcional. El otro es bastante significativo.
El primer problema es que GotFocus en realidad se llama contra DataGrid, no contra DataGridCell en la práctica. El calificador DataGridCell en XAML es redundante.
El principal problema que encontré con la respuesta es que el comportamiento de la tecla Enter está roto. Enter debería moverlo a la siguiente celda debajo de la celda actual en el comportamiento normal de DataGrid. Sin embargo, lo que realmente sucede detrás de escena es que el evento GotFocus se llamará dos veces. Una vez que la celda actual pierde el enfoque, y una vez que la nueva celda gana el foco. Pero mientras se llame a BeginEdit en esa primera celda, la siguiente celda nunca se activará. El resultado es que tiene la edición con un solo clic, pero cualquiera que no esté literalmente haciendo clic en la cuadrícula tendrá inconvenientes, y un diseñador de interfaz de usuario no debe asumir que todos los usuarios están usando ratones. (Los usuarios de teclado pueden sortearlo usando Tab, pero eso aún significa que están saltando obstáculos que no deberían necesitar).
Entonces, ¿la solución a este problema? Maneje el evento KeyDown para la celda y si la clave es la tecla Enter, establezca un indicador que impida que BeginEdit se active en la primera celda. Ahora la tecla Enter se comporta como debería.
Para empezar, agregue el siguiente estilo a su DataGrid:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Aplique ese estilo a la propiedad "CellStyle" de las columnas para las que desea habilitar un clic.
Luego, en el código detrás, tiene lo siguiente en su controlador GotFocus (tenga en cuenta que estoy usando VB aquí porque eso es lo que nuestro cliente de "solicitud de cuadrícula de datos con un solo clic" quería como lenguaje de desarrollo):
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)
.
.
.
Luego agrega su controlador para el 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
Ahora tiene un DataGrid que no ha cambiado ningún comportamiento fundamental de la implementación lista para usar y, sin embargo, admite la edición con un solo clic.
Sé que llegué un poco tarde a la fiesta, pero tuve el mismo problema y se me ocurrió una solución 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 puede ver, escribí mi propia DataGridTextColumn heredando todo lo relacionado con DataGridBoundColumn. Al anular el método GenerateElement y devolver un control Textbox allí mismo, nunca se llama al método para generar el elemento de edición. En un proyecto diferente, utilicé esto para implementar una columna Datepicker, por lo que también debería funcionar para casillas de verificación y cuadros combinados.
Esto no parece afectar el comportamiento del resto de las cuadrículas de datos. Al menos no he notado ningún efecto secundario ni he recibido comentarios negativos hasta ahora.
Una solución simple si está de acuerdo con que su celda siga siendo un cuadro de texto (sin distinguir entre el modo de edición y el de no editar). De esta manera, la edición con un solo clic funciona de inmediato. Esto también funciona con otros elementos como cuadro combinado y botones. De lo contrario, use la solución debajo de la actualización.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Probé todo lo que encontré aquí y en Google e incluso intenté crear mis propias versiones. Pero cada respuesta / solución funcionó principalmente para columnas de cuadro de texto, pero no funcionó con todos los demás elementos (casillas de verificación, cuadros combinados, columnas de botones), o incluso rompió esas otras columnas de elementos o tuvo algunos otros efectos secundarios. Gracias microsoft por hacer que datagrid se comporte de esa manera tan fea y nos obligue a crear esos trucos. Por eso, decidí hacer una versión que se pueda aplicar con un estilo a una columna de cuadro de texto directamente sin afectar a otras columnas.
Usé esta solución y la respuesta de @ my y las modifiqué para que fueran un comportamiento adjunto. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Agrega este estilo. El BasedOn
es importante cuando se utiliza algunos estilos de lujo para su cuadrícula de datos y usted no desea perder.
<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>
Aplica el estilo con CellStyle
a cada uno de tus DataGridTextColumns
así:
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
Y ahora agregue esta clase al mismo espacio de nombres que su MainViewModel (o un espacio de nombres diferente. Pero luego deberá usar un prefijo de espacio de nombres diferente local
). Bienvenido al feo mundo de códigos repetitivos de comportamientos adjuntos.
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;
}