我有一个DataGrid,第一列是文本列,第二列是复选框列。我的目标是,如果我点击复选框,它应该被选中。
但是,需要点击两次才能选中,在第一次单击时,单元格会被选中,第二次单击时复选框才会被选中。如何使复选框在一次单击时被勾选/取消勾选。
我使用的是WPF 4.0。 DataGrid中的列是自动生成的。
我有一个DataGrid,第一列是文本列,第二列是复选框列。我的目标是,如果我点击复选框,它应该被选中。
但是,需要点击两次才能选中,在第一次单击时,单元格会被选中,第二次单击时复选框才会被选中。如何使复选框在一次单击时被勾选/取消勾选。
我使用的是WPF 4.0。 DataGrid中的列是自动生成的。
对于单击DataGrid复选框,您可以在DataGridTemplateColumn
中放置常规复选框控件,并设置UpdateSourceTrigger=PropertyChanged
。
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
DataGridTemplateColumn.CellTemplate
和你自己的绑定就可以了!@weidian-huang的回答帮助我理解了这一点,谢谢! - Patrick我用如下样式解决了这个问题:
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
当然可以根据特定的列进一步进行调整...
首先,我知道这是一个相当古老的问题,但我仍然想尝试回答它。
几天前我也遇到了同样的问题,并找到了一个出人意料的简短解决方案(请参见此博客)。基本上,你只需要将 XAML 中的 DataGridCheckBoxColumn
定义替换为以下内容:
<DataGridTemplateColumn Header="MyCheckBoxColumnHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
这种解决方案的好处是显而易见的 - 它仅使用XAML,因此有效地避免了将额外的UI逻辑加到您的代码后面所带来的负担。DataGridCell.Selected="DataGridCell_Selected" SelectionUnit="Cell"
。 - Gillespie要使Konstantin Salavatov的答案与AutoGenerateColumns
一起使用,需要向DataGrid
的AutoGeneratingColumn
添加事件处理程序,并使用以下代码:
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
};
}
这会使所有DataGrid
自动生成的复选框列可通过"单击"进行编辑。
根据Goblin的回答所引用的博客,但修改后可在.NET 4.0中运行并带有行选择模式。
请注意,它还可以加快DataGridComboBoxColumn的编辑速度-通过单击或文本输入进入编辑模式并显示下拉列表。
XAML:
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
</Style>
代码后台:
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;
}
根据Jim Adorno在他的帖子中的回答和评论,这是使用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>
这里有一个简单得多的解决方案。
<DataGridTemplateColumn MinWidth="20" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
如果使用DataGridCheckBoxColumn
实现,第一次点击是聚焦,第二次点击是选中。
但是使用DataGridTemplateColumn
实现只需要一次点击。
使用DataGridComboboxBoxColumn
或者使用DataGridTemplateColumn
实现的区别也类似。
我尝试过这些建议,以及其他网站上的许多建议,但它们都没有完全适用于我。最终,我创建了以下解决方案。
我创建了自己的 DataGrid 继承控件,并简单地将以下代码添加到其中:
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;
}
}
这段代码的作用是什么?
好的,每次我们在DataGrid中点击任何单元格时,我们都会查看该单元格是否包含一个复选框控件。如果有,则将焦点设置为该复选框,并切换其值。
这对我来说似乎很有效,并且是一个不错、易于重用的解决方案。
但是,需要编写代码来实现这一点确实令人失望。虽然第一次鼠标单击(在 DataGrid 的复选框上)被忽略是因为 WPF 使用它来将行放入编辑模式的解释听起来很合理,但在现实世界中,这与每个真实应用程序的工作方式相违背。
如果用户在屏幕上看到一个复选框,他们应该可以单击一次来选择/取消选择它。就这样。
<DataGridCheckBoxColumn>
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox">
</Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Height="25">
<CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>