examples ejemplo custom columns wpf datagrid wpfdatagrid

ejemplo - wpf datagrid details



¿Cómo se realiza la selección de la casilla de verificación con un solo clic en WPF DataGrid? (11)

Tengo un DataGrid con la primera columna como columna de texto y una segunda columna como columna CheckBox. Lo que quiero es que haga clic en la casilla de verificación. Debería ser verificado.

Sin embargo, se necesitan dos clics para ser seleccionado, para el primer clic se selecciona la celda, para los segundos clics se marca la casilla de verificación. Cómo hacer que la casilla de verificación se marque / desmarque con un solo clic.

Estoy usando WPF 4.0. Las columnas en el DataGrid son Autogeneradas.


Aquí hay una solución mucho más simple.

<DataGridTemplateColumn MinWidth="20" > <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Grid> <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/> </Grid> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>

Si usa DataGridCheckBoxColumn para implementar, primero haga clic en enfocar, segundo clic en verificar.

Pero el uso de DataGridTemplateColumn para implementar necesita un solo clic.

La diferencia de usar DataGridComboboxBoxColumn y la implementación de DataGridTemplateColumn también es similar.


Basado en el blog al que se hace referencia en la respuesta de Goblin, pero modificado para trabajar en .NET 4.0 y con el modo de selección de filas.

Tenga en cuenta que también acelera la edición de DataGridComboBoxColumn, ingresando al modo de edición y mostrando el menú desplegable con un solo clic o entrada de texto.

XAML:

<Style TargetType="{x:Type DataGridCell}"> <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" /> <EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" /> </Style>

Código detrás:

private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DataGridCell cell = sender as DataGridCell; GridColumnFastEdit(cell, e); } private void DataGridCell_PreviewTextInput(object sender, TextCompositionEventArgs e) { DataGridCell cell = sender as DataGridCell; GridColumnFastEdit(cell, e); } private static void GridColumnFastEdit(DataGridCell cell, RoutedEventArgs e) { if (cell == null || cell.IsEditing || cell.IsReadOnly) return; DataGrid dataGrid = FindVisualParent<DataGrid>(cell); if (dataGrid == null) return; if (!cell.IsFocused) { cell.Focus(); } if (cell.Content is CheckBox) { 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; } } } else { ComboBox cb = cell.Content as ComboBox; if (cb != null) { //DataGrid dataGrid = FindVisualParent<DataGrid>(cell); dataGrid.BeginEdit(e); cell.Dispatcher.Invoke( DispatcherPriority.Background, new Action(delegate { })); cb.IsDropDownOpen = true; } } } private 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; }


Basado en la respuesta de Jim Adorno y comentarios en su publicación, esta es una solución con MultiTrigger :

<Style TargetType="DataGridCell"> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsReadOnly" Value="False" /> <Condition Property="IsMouseOver" Value="True" /> </MultiTrigger.Conditions> <Setter Property="IsEditing" Value="True" /> </MultiTrigger> </Style.Triggers> </Style>


En primer lugar, sé que esta es una pregunta bastante antigua, pero aún así pensé en intentar responderla.

Tuve el mismo problema hace un par de días y encontré una solución sorprendentemente corta (vea este blog ). Básicamente, todo lo que necesita hacer es reemplazar la definición de DataGridCheckBoxColumn en su XAML con lo siguiente:

<DataGridTemplateColumn Header="MyCheckBoxColumnHeader"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>

La ventaja de esta solución es obvia: es solo XAML; por lo tanto, evita que cargue su código de retorno con lógica de UI adicional y lo ayuda a mantener su estado a los ojos de los fanáticos de MVVM;).


Intenté estas sugerencias, y muchas otras que encontré en otros sitios, pero ninguna de ellas me funcionó. Al final, creé la siguiente solución.

Creé mi propio control heredado de DataGrid y simplemente le agregué este código:

public class DataGridWithNavigation : Microsoft.Windows.Controls.DataGrid { public DataGridWithNavigation() { EventManager.RegisterClassHandler(typeof(DataGridCell), DataGridCell.PreviewMouseLeftButtonDownEvent, new RoutedEventHandler(this.OnPreviewMouseLeftButtonDown)); } private void OnPreviewMouseLeftButtonDown(object sender, RoutedEventArgs e) { DataGridCell cell = sender as DataGridCell; if (cell != null && !cell.IsEditing && !cell.IsReadOnly) { DependencyObject obj = FindFirstControlInChildren(cell, "CheckBox"); if (obj != null) { System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)obj; cb.Focus(); cb.IsChecked = !cb.IsChecked; } } } public DependencyObject FindFirstControlInChildren(DependencyObject obj, string controlType) { if (obj == null) return null; // Get a list of all occurrences of a particular type of control (eg "CheckBox") IEnumerable<DependencyObject> ctrls = FindInVisualTreeDown(obj, controlType); if (ctrls.Count() == 0) return null; return ctrls.First(); } public IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, string type) { if (obj != null) { if (obj.GetType().ToString().EndsWith(type)) { yield return obj; } for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type)) { if (child != null) { yield return child; } } } } yield break; } }

¿Qué hace todo esto?

Bueno, cada vez que hacemos clic en cualquier celda de nuestro DataGrid, vemos si la celda contiene un control CheckBox dentro de ella. Si lo hace , entonces estableceremos el foco en ese CheckBox y alternar su valor .

Esto parece funcionar para mí, y es una solución agradable, fácilmente reutilizable.

Sin embargo, es decepcionante que necesitemos escribir un código para hacer esto. La explicación de que el primer clic del mouse (en un CheckGox de DataGrid) se "ignora" como WPF lo usa para poner la fila en el modo de edición puede sonar lógico, pero en el mundo real, va en contra de la forma en que funciona cada aplicación real.

Si un usuario ve una casilla de verificación en su pantalla, debería poder hacer clic una vez para marcarla / desmarcarla. Fin de la historia.


Lo resolví con el siguiente Estilo:

<Style TargetType="DataGridCell"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="IsEditing" Value="True" /> </Trigger> </Style.Triggers> </Style>

Por supuesto, es posible adaptar esto aún más para columnas específicas ...


Lo resolví con esto:

<DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Viewbox Height="25"> <CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/> </Viewbox> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>

La casilla de verificación activa en un solo clic!


Otra solución simple es agregar este estilo a su DataGridColumn. El cuerpo de su estilo puede estar vacío.

<DataGridCheckBoxColumn> <DataGridCheckBoxColumn.ElementStyle> <Style TargetType="CheckBox"> </Style> </DataGridCheckBoxColumn.ElementStyle> </DataGridCheckBoxColumn>


Para que la respuesta de Konstantin Salavatov funcione con AutoGenerateColumns , agregue un controlador de eventos al AutoGeneratingColumn DataGrid con el siguiente código:

if (e.Column is DataGridCheckBoxColumn && !e.Column.IsReadOnly) { var checkboxFactory = new FrameworkElementFactory(typeof(CheckBox)); checkboxFactory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center); checkboxFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center); checkboxFactory.SetBinding(ToggleButton.IsCheckedProperty, new Binding(e.PropertyName) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); e.Column = new DataGridTemplateColumn { Header = e.Column.Header, CellTemplate = new DataTemplate { VisualTree = checkboxFactory }, SortMemberPath = e.Column.SortMemberPath }; }

Esto hará que todas las columnas de casilla de verificación generadas automáticamente de DataGrid sean editables con "un solo clic".


Para la casilla de verificación DataGrid con un solo clic, puede poner el control de casilla regular dentro de DataGridTemplateColumn y establecer UpdateSourceTrigger=PropertyChanged .

<DataGridTemplateColumn.CellTemplate> <DataTemplate> <CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate>


<Style x:Key="StilCelula" TargetType="DataGridCell"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="IsEditing" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource CheckBoxColumnToEditingConvertor}}" /> </Trigger> </Style.Triggers> <Style>

Imports System.Globalization Public Class CheckBoxColumnToEditingConvertor Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert Try Return TypeOf TryCast(value, DataGridCell).Column Is DataGridCheckBoxColumn Catch ex As Exception Return Visibility.Collapsed End Try End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class