datagrid-cell 获得焦点时自动编辑 WPF datagrid 内容
Automatic editing of WPF datagrid content when datagrid-cell gets focus
我在 WPF 中有一个带有 DataGridTextColum 和 DataGridTemplateColum.
的数据网格
<DataGridTextColumn Width="4*" IsReadOnly="True" x:Name="dataGridColumnDescription"
Header="Description" Binding="{Binding Description}">
</DataGridTextColumn>
<DataGridTemplateColumn CellStyle="{StaticResource CellEditing}" IsReadOnly="False" Width="*" Header="Value"
CellEditingTemplateSelector="{StaticResource myCellEditingTemplateSelectorValue}"
CellTemplateSelector="{StaticResource myCellTemplateSelectorValue}">
</DataGridTemplateColumn>
CellTemplateSelectors return 带有 Celltemplate resp 的 TextBlock 的 DataTemplate。用于 CellEditing 的文本框!
<DataTemplate x:Key="dGridStringValueTemplate">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=Value}"/>
</DataTemplate>
<DataTemplate x:Key="dGridStringValueTemplateEditing">
<TextBox TextAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" BorderThickness="1" Text="{Binding Path=Value, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>
现在我想在 DataGridCell 获得焦点时自动聚焦 TextBox。用户无需双击单元格即可编辑文本框内容。
我找到了这篇文章:
DataGrid Tips & Tricks: Single-Click Editing
我在哪里可以获得当前 DataGridCell,但我如何访问内容以使文本框获得编辑内容的焦点?
这是我的风格:
<Style x:Key="CellEditing" TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="myDataGridMain_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
这是我的事件处理程序:
private void myDataGridMain_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell cell = sender as DataGridCell; // cell ist not null
DataGridTemplateColumn col = cell.Column as DataGridTemplateColumn; //col is not null
DataTemplate template = col.CellTemplate; //this is null
}
如何获取带有该事件处理程序的文本框?
这似乎有效:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
我成功了,虽然不是最好的解决方案,但它确实有效...
当 Cell 获得焦点时,我将其设置为编辑模式。
private void myDataGridMain_OnFocus(object sender, RoutedEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell != null)
cell.IsEditing = true;
//var test = FindVisualChild<TextBlock>(cell);
}
在 Keydown 上我搜索视觉对象 child 并给出焦点。
private void myDataGridMain_KeyDown(object sender, KeyEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (e.Key == Key.Enter)
{ //give cell the focus
cell.Focus();
}
else
{
if ((cell != null))
{
TextBox textbox = FindVisualChild<TextBox>(cell);
if (textbox != null)
{ //TextBox has benn found
if ((textbox as TextBox).IsFocused == false)
{
(textbox as TextBox).SelectAll();
}
(textbox as TextBox).Focus();
}
CheckBox chkbox = FindVisualChild<CheckBox>(cell);
if (chkbox != null)
{ //Checkbox has been found
(chkbox as CheckBox).Focus();
}
ComboBox combbox = FindVisualChild<ComboBox>(cell);
if (combbox != null)
{ //ComboBox has been found
(combbox as ComboBox).Focus();
}
}
}
}
查找视觉对象 Child!
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
这种方法对我有用。它使用了 DataGrid
将始终在编辑开始时创建模板的新实例这一事实:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty}"
Loaded="TextBox_Loaded"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
在后面的代码中:
private void TextBox_Loaded(object sender, RoutedEventArgs e)
{
((TextBox)sender).Focus();
((TextBox)sender).SelectAll();
}
作为额外的好处,它还会选择单元格中的所有文本。不管你怎么进入编辑模式(双击,单击,按F2)都应该能用
创建从数据网格控件派生的新控件的简单答案
using System.Windows.Controls;
public class CustomDataGrid : DataGrid
{
protected override void OnSelectedCellsChanged(SelectedCellsChangedEventArgs e)
{
//to make sure cell is selected
var cells = e.AddedCells.FirstOrDefault();
if (cells != null)
{
this.BeginEdit();
}
base.OnSelectedCellsChanged(e);
}
}
Hisham's 建议对我来说非常有效,但我会改用 OnCurrentCellChanged
,因为当 SelectionUnit
为 CellOrRowHeader
时 OnSelectedCellsChanged
将不起作用。在后一种情况下 BeginEdit()
只会在选择移动到另一行中的单元格时触发。向左或向右走完全不会触发该事件。
此外,可能建议将 DependencyProperty 添加到自定义控件并在触发之前对其进行检查 BeginEdit()
,以在需要时防止此行为(如 XCeed 等其他 DataGrid 所做的那样)。但这不是批评 - 只是我经常做的事情。
protected override void OnCurrentCellChanged(EventArgs e)
{
// Make sure a cell is selected and only enter edit mode
// if this is the desired behavior
if (CurrentCell != null && EditTrigger == EditTriggers.CellsCurrent)
{
this.BeginEdit();
}
base.OnCurrentCellChanged(e);
}
我在 WPF 中有一个带有 DataGridTextColum 和 DataGridTemplateColum.
的数据网格<DataGridTextColumn Width="4*" IsReadOnly="True" x:Name="dataGridColumnDescription"
Header="Description" Binding="{Binding Description}">
</DataGridTextColumn>
<DataGridTemplateColumn CellStyle="{StaticResource CellEditing}" IsReadOnly="False" Width="*" Header="Value"
CellEditingTemplateSelector="{StaticResource myCellEditingTemplateSelectorValue}"
CellTemplateSelector="{StaticResource myCellTemplateSelectorValue}">
</DataGridTemplateColumn>
CellTemplateSelectors return 带有 Celltemplate resp 的 TextBlock 的 DataTemplate。用于 CellEditing 的文本框!
<DataTemplate x:Key="dGridStringValueTemplate">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=Value}"/>
</DataTemplate>
<DataTemplate x:Key="dGridStringValueTemplateEditing">
<TextBox TextAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" BorderThickness="1" Text="{Binding Path=Value, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>
现在我想在 DataGridCell 获得焦点时自动聚焦 TextBox。用户无需双击单元格即可编辑文本框内容。
我找到了这篇文章:
DataGrid Tips & Tricks: Single-Click Editing 我在哪里可以获得当前 DataGridCell,但我如何访问内容以使文本框获得编辑内容的焦点?
这是我的风格:
<Style x:Key="CellEditing" TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="myDataGridMain_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
这是我的事件处理程序:
private void myDataGridMain_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell cell = sender as DataGridCell; // cell ist not null
DataGridTemplateColumn col = cell.Column as DataGridTemplateColumn; //col is not null
DataTemplate template = col.CellTemplate; //this is null
}
如何获取带有该事件处理程序的文本框?
这似乎有效:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
我成功了,虽然不是最好的解决方案,但它确实有效... 当 Cell 获得焦点时,我将其设置为编辑模式。
private void myDataGridMain_OnFocus(object sender, RoutedEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell != null)
cell.IsEditing = true;
//var test = FindVisualChild<TextBlock>(cell);
}
在 Keydown 上我搜索视觉对象 child 并给出焦点。
private void myDataGridMain_KeyDown(object sender, KeyEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (e.Key == Key.Enter)
{ //give cell the focus
cell.Focus();
}
else
{
if ((cell != null))
{
TextBox textbox = FindVisualChild<TextBox>(cell);
if (textbox != null)
{ //TextBox has benn found
if ((textbox as TextBox).IsFocused == false)
{
(textbox as TextBox).SelectAll();
}
(textbox as TextBox).Focus();
}
CheckBox chkbox = FindVisualChild<CheckBox>(cell);
if (chkbox != null)
{ //Checkbox has been found
(chkbox as CheckBox).Focus();
}
ComboBox combbox = FindVisualChild<ComboBox>(cell);
if (combbox != null)
{ //ComboBox has been found
(combbox as ComboBox).Focus();
}
}
}
}
查找视觉对象 Child!
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
这种方法对我有用。它使用了 DataGrid
将始终在编辑开始时创建模板的新实例这一事实:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty}"
Loaded="TextBox_Loaded"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
在后面的代码中:
private void TextBox_Loaded(object sender, RoutedEventArgs e)
{
((TextBox)sender).Focus();
((TextBox)sender).SelectAll();
}
作为额外的好处,它还会选择单元格中的所有文本。不管你怎么进入编辑模式(双击,单击,按F2)都应该能用
创建从数据网格控件派生的新控件的简单答案
using System.Windows.Controls;
public class CustomDataGrid : DataGrid
{
protected override void OnSelectedCellsChanged(SelectedCellsChangedEventArgs e)
{
//to make sure cell is selected
var cells = e.AddedCells.FirstOrDefault();
if (cells != null)
{
this.BeginEdit();
}
base.OnSelectedCellsChanged(e);
}
}
Hisham's 建议对我来说非常有效,但我会改用 OnCurrentCellChanged
,因为当 SelectionUnit
为 CellOrRowHeader
时 OnSelectedCellsChanged
将不起作用。在后一种情况下 BeginEdit()
只会在选择移动到另一行中的单元格时触发。向左或向右走完全不会触发该事件。
此外,可能建议将 DependencyProperty 添加到自定义控件并在触发之前对其进行检查 BeginEdit()
,以在需要时防止此行为(如 XCeed 等其他 DataGrid 所做的那样)。但这不是批评 - 只是我经常做的事情。
protected override void OnCurrentCellChanged(EventArgs e)
{
// Make sure a cell is selected and only enter edit mode
// if this is the desired behavior
if (CurrentCell != null && EditTrigger == EditTriggers.CellsCurrent)
{
this.BeginEdit();
}
base.OnCurrentCellChanged(e);
}