J'ai un DataGrid avec la première colonne en tant que colonne de texte et la deuxième colonne en tant que colonne CheckBox. Ce que je veux, c'est si je coche la case. Il devrait être vérifié.
Mais, il faut deux clics pour être sélectionné. Pour le premier clic, la cellule est sélectionnée, pour le second, la case à cocher est cochée. Comment faire la case à cocher pour être coché/décoché en un seul clic.
J'utilise WPF 4.0. Les colonnes du DataGrid sont générées automatiquement.
Pour la case à cocher DataGrid en un seul clic, vous pouvez simplement mettre un contrôle de case à cocher régulier dans DataGridTemplateColumn
et définir UpdateSourceTrigger=PropertyChanged
.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
J'ai résolu ceci avec le style suivant:
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Il est bien sûr possible d’adapter cela à des colonnes spécifiques ...
Premièrement, je sais que la question est assez ancienne, mais je pensais toujours que j'essaierais d'y répondre.
J'ai eu le même problème il y a quelques jours et je suis tombé sur une solution étonnamment courte (voir ce blog ). En gros, tout ce que vous avez à faire est de remplacer la définition de DataGridCheckBoxColumn
dans votre code XAML par ce qui suit:
<DataGridTemplateColumn Header="MyCheckBoxColumnHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
L’avantage de cette solution est évident: il s’agit uniquement de XAML; ainsi, il vous évite de surcharger votre code-back avec une logique d’interface utilisateur supplémentaire et vous aide à conserver votre statut aux yeux des fanatiques de MVVM;).
Basé sur le blog référencé dans la réponse de Goblin, mais modifié pour fonctionner en .NET 4.0 et avec le mode de sélection de ligne.
Notez que cela accélère également l'édition de DataGridComboBoxColumn - en entrant en mode édition et en affichant une liste déroulante en un seul clic ou en entrée de texte.
XAML:
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
</Style>
Code-behind:
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;
}
Pour que réponse de Konstantin Salavatov travaille avec AutoGenerateColumns
, ajoutez un gestionnaire d'événements au DataGrid
de AutoGeneratingColumn
avec le code suivant:
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
};
}
Cela rendra toutes les colonnes de case à cocher générées automatiquement de DataGrid
modifiables en un seul clic.
J'ai essayé ces suggestions et beaucoup d'autres que j'ai trouvées sur d'autres sites, mais aucune d'entre elles n'a vraiment fonctionné pour moi. Au final, j'ai créé la solution suivante.
J'ai créé mon propre contrôle DataGrid-inherited et y ai simplement ajouté ce code:
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'est-ce que tout cela fait?
Eh bien, chaque fois que nous cliquons sur une cellule de notre DataGrid, nous voyons si la cellule contient un contrôle CheckBox. Si le fait , alors nous allons définir le focus sur ce CheckBox et basculer sa valeur .
Cela semble fonctionner pour moi et constitue une solution agréable et facilement réutilisable.
Il est décevant que nous ayons besoin d’écrire du code pour le faire. L'explication selon laquelle le premier clic de souris (sur la case à cocher d'un contrôle DataGrid) est "ignorée" car WPF l'utilise pour mettre la ligne en mode Edition peut sembler logique, mais dans la réalité, cela va à l'encontre du fonctionnement de chaque application réelle.
Si un utilisateur voit une case à cocher sur son écran, il devrait pouvoir cliquer dessus une fois pour la cocher/la décocher. Fin de l'histoire.
Il existe une solution beaucoup plus simple ici.
<DataGridTemplateColumn MinWidth="20" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Si vous utilisez DataGridCheckBoxColumn
pour l'implémenter, le premier clic est pour le focus, le deuxième clic est pour vérifier.
Mais utiliser DataGridTemplateColumn
pour l'implémenter nécessite un seul clic.
La différence entre DataGridComboboxBoxColumn
et la mise en œuvre par DataGridTemplateColumn
est également similaire.
J'ai résolu avec ceci:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Height="25">
<CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
La case à cocher active sur simple clic!
Base sur Jim Adorno réponse et commentaires sur son post, voici la solution avec 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>
Une autre solution simple consiste à ajouter ce style à votre DataGridColumn.Le corps de votre style peut être vide.
<DataGridCheckBoxColumn>
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox">
</Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
<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