如何覆盖或实现这个功能?
这是我解决此问题的方法:
<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>
这个DataGrid绑定了一个CollectionViewSource(包含虚拟的Person对象)。
实现方法为:DataGridCell.Selected="DataGridCell_Selected"。
我只是简单地挂接DataGrid单元格的Selected事件,并在DataGrid上调用BeginEdit()。
以下是事件处理程序的代码:
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
属性设置为Cell
,就可以解决已选择行的问题。 - Matt Wincklergrd.BeginEdit(e)
之后,我希望该单元格中的TextBox获得焦点。 我该怎么做? 我尝试在DataGridCell和DataGrid上调用FindName(“txtBox”)
,但它对我返回null。 - user1214135<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
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;
}
来源: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>
代码后台:
//
// 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;
}
来自http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing的解决方案对我非常有用,但我使用了在ResourceDictionary中定义的Style为每个DataGrid启用它。要在资源字典中使用处理程序,您需要向其中添加一个代码后台文件。以下是操作步骤:
这是一个名为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>
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.
}
我通过添加一个触发器来解决了这个问题,当鼠标悬停在DataGridCell上时,它会将IsEditing属性设置为True。这解决了我的大部分问题,它也适用于组合框。
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
<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>
Adding behavior in 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>
The EditCellOnSingleClickBehavior class 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);
}
}
OnGotFocus
方法,否则,在单元格上点击 Enter 键会再次触发此方法而不是提交编辑:var row = sender as Row; if (!row.IsEditing) this.AssociatedObject.BeginEdit(e);
- lavantgarde这些答案中的几个启发了我,还有this blog post,但每个答案都有所欠缺。我将它们中最好的部分结合起来,想出了这个相当优雅的解决方案,似乎完美地实现了用户体验。
这使用了一些C# 9语法,在任何地方都可以正常工作,但您可能需要在项目文件中进行设置:
<LangVersion>9</LangVersion>
将以下类添加到您的项目中:
public static class WpfHelpers
{
internal static void DataGridPreviewMouseLeftButtonDownEvent(object sender, RoutedEventArgs e)
{
// The original source for this was inspired by https://softwaremechanik.wordpress.com/2013/10/02/how-to-make-all-wpf-datagrid-cells-have-a-single-click-to-edit/
DataGridCell? cell = e is MouseButtonEventArgs { OriginalSource: UIElement clickTarget } ? FindVisualParent<DataGridCell>(clickTarget) : null;
if (cell is { IsEditing: false, IsReadOnly: false })
{
if (!cell.IsFocused)
{
cell.Focus();
}
if (FindVisualParent<DataGrid>(cell) is DataGrid dataGrid)
{
if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
{
if (!cell.IsSelected)
{
cell.IsSelected = true;
}
}
else
{
if (FindVisualParent<DataGridRow>(cell) is DataGridRow { IsSelected: false } row)
{
row.IsSelected = true;
}
}
}
}
}
internal static T? GetFirstChildByType<T>(DependencyObject prop)
where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(prop);
for (int i = 0; i < count; i++)
{
if (VisualTreeHelper.GetChild(prop, i) is DependencyObject child)
{
T? typedChild = child as T ?? GetFirstChildByType<T>(child);
if (typedChild is object)
{
return typedChild;
}
}
}
return null;
}
private static T? FindVisualParent<T>(UIElement element)
where T : UIElement
{
UIElement? parent = element;
while (parent is object)
{
if (parent is T correctlyTyped)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
}
App.xaml.cs
文件中:protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
EventManager.RegisterClassHandler(
typeof(DataGrid),
DataGrid.PreviewMouseLeftButtonDownEvent,
new RoutedEventHandler(WpfHelpers.DataGridPreviewMouseLeftButtonDownEvent));
}
将以下代码添加到包含DataGrid
的页面的后台代码中:
private void TransactionDataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
WpfHelpers.GetFirstChildByType<Control>(e.EditingElement)?.Focus();
}
并将其链接起来(在XAML中):PreparingCellForEdit="TransactionDataGrid_PreparingCellForEdit"
<DataGrid.Resources>
<Style x:Key="ddlStyle" TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
并将样式应用于我想要的列
<DataGridComboBoxColumn CellStyle="{StaticResource ddlStyle}">
user2134678的答案存在两个问题。其中一个非常小,没有实际影响。另一个则比较重要。
第一个问题是在实践中,GotFocus实际上是针对DataGrid而不是DataGridCell进行调用的。XAML中的DataGridCell限定符是多余的。
我发现该答案的主要问题是Enter键的行为有问题。在正常的DataGrid行为中,按Enter应该将您移动到当前单元格下方的下一个单元格。然而,在幕后实际发生的是GotFocus事件将被调用两次。一次在当前单元格失去焦点时,另一次在新单元格获取焦点时。但只要在第一个单元格上调用BeginEdit,下一个单元格就永远不会被激活。结果是,您可以进行一键编辑,但任何没有真正点击网格的人都会感到不便,用户界面设计师不应该假设所有用户都在使用鼠标。(键盘用户可以通过使用Tab来解决这个问题,但仍然意味着他们需要跳过不必要的步骤。)
那么解决这个问题的方法是什么呢?为单元格处理KeyDown事件,如果按键是Enter键,则设置一个标志,阻止在第一个单元格上触发BeginEdit。现在,Enter键的行为就正常了。
首先,将以下样式添加到您的DataGrid中:<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
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)
.
.
.
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