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

WPF开发:DataGrid单元格动态添加TextBox及相关操作技术咨询

嘿,针对你在WPF发票应用里遇到的DataGrid单元格TextBox相关需求,我分三个场景给你详细说明,都是实际项目里常用的方案:

1. 纯编程方式为DataGrid单元格添加TextBox

如果不想在XAML里硬编码列,完全可以通过C#代码动态生成带TextBox的模板列,核心是用DataGridTemplateColumn配合FrameworkElementFactory来构建模板:

// 假设你的DataGrid叫dataGrid,业务实体是InvoiceItem,包含Amount属性
var amountColumn = new DataGridTemplateColumn();
amountColumn.Header = "发票金额";

// 构建编辑模板(单元格进入编辑状态时显示TextBox)
var editingTemplateFactory = new FrameworkElementFactory(typeof(TextBox));
// 绑定到业务实体的Amount属性,双向绑定确保输入同步
editingTemplateFactory.SetBinding(TextBox.TextProperty, 
    new Binding("Amount") { Mode = BindingMode.TwoWay, UpdateSourceTrigger = PropertyChanged });
var editingTemplate = new DataTemplate();
editingTemplate.VisualTree = editingTemplateFactory;
amountColumn.CellEditingTemplate = editingTemplate;

// 可选:构建显示模板(单元格非编辑状态时显示TextBlock)
var displayTemplateFactory = new FrameworkElementFactory(typeof(TextBlock));
displayTemplateFactory.SetBinding(TextBlock.TextProperty, new Binding("Amount"));
displayTemplateFactory.SetValue(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Right);
var displayTemplate = new DataTemplate();
displayTemplate.VisualTree = displayTemplateFactory;
amountColumn.CellTemplate = displayTemplate;

// 将列添加到DataGrid
dataGrid.Columns.Add(amountColumn);
2. 使用自定义UserControl动态添加TextBox并获取输入

如果你的TextBox需要复杂样式或逻辑,用自定义UserControl会更易维护。先完成UserControl的编写:

UserControltest.xaml

<UserControl x:Class="InvoiceApp.UserControltest"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Padding="4">
        <TextBox x:Name="txtUserInput" 
                 Text="{Binding InputValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                 BorderBrush="#FF0078D7" />
    </Grid>
</UserControl>

UserControltest.xaml.cs

给UserControl定义依赖属性,这是WPF控件实现数据绑定的关键:

public partial class UserControltest : UserControl
{
    // 定义双向绑定的依赖属性
    public static readonly DependencyProperty InputValueProperty =
        DependencyProperty.Register(
            nameof(InputValue), 
            typeof(string), 
            typeof(UserControltest),
            new FrameworkPropertyMetadata(
                string.Empty, 
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public string InputValue
    {
        get => (string)GetValue(InputValueProperty);
        set => SetValue(InputValueProperty, value);
    }

    public UserControltest()
    {
        InitializeComponent();
        // 让UserControl的DataContext指向自身,方便内部绑定
        DataContext = this;
    }
}

动态添加到DataGrid

接下来用代码把这个UserControl作为单元格的编辑模板:

var customInputColumn = new DataGridTemplateColumn();
customInputColumn.Header = "备注信息";

var controlFactory = new FrameworkElementFactory(typeof(UserControltest));
// 绑定到业务实体的Remark属性
controlFactory.SetBinding(UserControltest.InputValueProperty, 
    new Binding("Remark") { Mode = BindingMode.TwoWay });
var customTemplate = new DataTemplate();
customTemplate.VisualTree = controlFactory;
customInputColumn.CellEditingTemplate = customTemplate;

// 同样可以设置CellTemplate用TextBlock显示备注
var displayFactory = new FrameworkElementFactory(typeof(TextBlock));
displayFactory.SetBinding(TextBlock.TextProperty, new Binding("Remark"));
var displayTemplate = new DataTemplate();
displayTemplate.VisualTree = displayFactory;
customInputColumn.CellTemplate = displayTemplate;

dataGrid.Columns.Add(customInputColumn);

获取输入数据

因为用了双向绑定,直接从你的业务实体里拿数据就好,不用手动操作控件:

// 遍历DataGrid的所有数据项
foreach (var item in dataGrid.Items.OfType<InvoiceItem>())
{
    var userInput = item.Remark;
    // 这里处理用户输入的内容
    Console.WriteLine($"用户输入的备注:{userInput}");
}
3. 查找CellEditingTemplate内的TextBox

如果确实需要直接操作模板里的TextBox(比如设置焦点、修改样式),可以用VisualTreeHelper遍历视觉树来查找。先写一个通用的辅助方法:

/// <summary>
/// 在视觉树中查找指定类型的子控件
/// </summary>
public static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    if (parent == null) return null;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is T typedChild)
        {
            return typedChild;
        }
        // 递归查找子控件
        var foundChild = FindVisualChild<T>(child);
        if (foundChild != null)
        {
            return foundChild;
        }
    }
    return null;
}

查找普通TextBox

可以在DataGrid的BeginningEdit事件里触发查找(此时编辑控件已经实例化):

private void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
    // 获取当前编辑的单元格元素
    var editingCell = e.EditingElement as FrameworkElement;
    if (editingCell == null) return;

    // 查找单元格内的TextBox
    var targetTextBox = FindVisualChild<TextBox>(editingCell);
    if (targetTextBox != null)
    {
        // 示例:设置焦点并选中所有文本
        targetTextBox.Focus();
        targetTextBox.SelectAll();
        var currentText = targetTextBox.Text;
    }
}

查找自定义UserControl内的TextBox

如果是UserControl里的TextBox,先找到UserControl再访问内部控件:

private void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
    var editingCell = e.EditingElement as FrameworkElement;
    if (editingCell == null) return;

    // 先找到自定义UserControl
    var customControl = FindVisualChild<UserControltest>(editingCell);
    if (customControl != null)
    {
        // 直接访问UserControl里命名的TextBox
        var innerTextBox = customControl.txtUserInput;
        innerTextBox.Foreground = Brushes.Red;
    }
}

小贴士:WPF推荐用数据绑定而非直接操作控件,这样更符合MVVM模式,也能避免视觉树查找的潜在问题(比如控件未实例化时返回null)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:36:13