You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF DataGrid:如何单击(或触摸)选中DataGridCell内的控件

解决WPF DataGridTemplateColumn单击直接进入TextBox编辑状态的问题

你的思路方向是对的,但核心问题在于设置IsEditing = true后,CellEditingTemplate中的TextBox还未完成加载,此时直接查找控件会失败,导致需要二次点击才能获取焦点。以下是两种可行的解决方案:

方案一:利用TextBox的Loaded事件自动获取焦点

直接在CellEditingTemplate的TextBox上添加Loaded事件,控件加载完成后自动获取焦点:

修改XAML代码

<DataGridTemplateColumn Header="Comment" Width="Auto">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <Grid>
                <TextBox Name="InputTextBox" 
                         Text="{st:WpfBinding Path=Comment}" 
                         Loaded="InputTextBox_Loaded" />
            </Grid>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Grid>
                <TextBlock Text="{Binding Path=Comment}" 
                           PreviewMouseDown="UIElement_OnPreviewMouseDown" />
            </Grid>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

后台事件处理

private void InputTextBox_Loaded(object sender, RoutedEventArgs e)
{
    if (sender is TextBox textBox)
    {
        textBox.Focus();
        // 可选:将光标定位到文本末尾,提升编辑体验
        textBox.Select(textBox.Text.Length, 0);
    }
}

private void UIElement_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    var targetCell = GetDataGridCell(sender, e);
    if (targetCell != null)
    {
        targetCell.IsEditing = true;
        e.Handled = true; // 阻止默认事件,避免DataGrid的后续处理干扰
    }
}

private DataGridCell GetDataGridCell(object sender, MouseEventArgs e)
{
    var hit = VisualTreeHelper.HitTest((Visual)sender, e.GetPosition((IInputElement)sender));
    DependencyObject cell = VisualTreeHelper.GetParent(hit.VisualHit);

    while (cell != null && !(cell is DataGridCell))
    {
        cell = VisualTreeHelper.GetParent(cell);
    }

    return cell as DataGridCell;
}

方案二:通过Dispatcher延迟查找TextBox

在设置IsEditing = true后,利用Dispatcher以Render优先级延迟执行查找操作,确保CellEditingTemplate已加载完成:

修改后台事件处理代码

private void UIElement_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    var targetCell = GetDataGridCell(sender, e);
    if (targetCell != null)
    {
        targetCell.IsEditing = true;
        // 延迟执行,等待CellEditingTemplate加载
        Dispatcher.BeginInvoke(new Action(() =>
        {
            var textBox = FindVisualChild<TextBox>(targetCell);
            if (textBox != null)
            {
                textBox.Focus();
                textBox.Select(textBox.Text.Length, 0);
            }
        }), System.Windows.Threading.DispatcherPriority.Render);
        e.Handled = true;
    }
}

// 通用可视化树子控件查找方法
private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is T typedChild)
            return typedChild;
        
        var result = FindVisualChild<T>(child);
        if (result != null)
            return result;
    }
    return null;
}

// 保留你原有的GetDataGridCell方法
private DataGridCell GetDataGridCell(object sender, MouseEventArgs e)
{
    var hit = VisualTreeHelper.HitTest((Visual)sender, e.GetPosition((IInputElement)sender));
    DependencyObject cell = VisualTreeHelper.GetParent(hit.VisualHit);

    while (cell != null && !(cell is DataGridCell))
    {
        cell = VisualTreeHelper.GetParent(cell);
    }

    return cell as DataGridCell;
}

关键注意事项

  1. 确保DataGrid的IsReadOnly属性为false,同时目标列的IsReadOnly也需设为false(默认就是false,若手动修改过需检查)。
  2. 设置e.Handled = true是必要的,否则DataGrid的默认鼠标事件处理会干扰焦点设置逻辑。

内容的提问来源于stack exchange,提问作者Nico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:05:41