WPF如何动态创建文本框并在按钮点击时找到该文本框?

18

我正在使用以下代码动态创建一个TextBox和一个Button:

Button btnClickMe = new Button();
btnClickMe.Content = "Click Me";
btnClickMe.Name = "btnClickMe";
btnClickMe.Click += new RoutedEventHandler(this.CallMeClick);

someStackPanel.Childern.Add(btnClickMe);

TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";

someStackPanel.Childern.Add(txtNumber);

我将click事件挂钩到“Click Me”按钮上。单击该按钮会触发click事件,但我无法找到我动态输入的TextBox。

这是我的click事件:

protected void ClickMeClick(object sender, RoutedEventArgs e)
{
    // Find the phone number
    TextBox txtNumber = this.someStackPanel.FindName("txtNumber") as TextBox;

    if (txtNumber != null)
    {
        string message = string.Format("The number is {0}", txtNumber.Text);

        MessageBox.Show(message);    
    }
    else
    {
        MessageBox.Show("Textbox is null");
    }
}

如何找到名为txtNumberTextBox


你是不是想要这个:this.someStackPanel.FindName("txtNumber")? - foson
谢谢,我修复了。它仍然不起作用。 - David Basarab
这里有很多错别字: (1) s/Childern/Children/ (2) 按钮被路由到 CallMeClick 但是代码显示 ClickMeClick (3) 按钮被放置在 someStackPanel 中,但后来在 pnlCallMe 中搜索显然,这不是你正在运行的代码。 - hughdbrown
不,这不是我正在运行的代码。我拿出了我正在运行的代码并进行了更改,使其更加通用,并仅显示了我问题所需的部分。 - David Basarab
5个回答

17

Josh G提供了解决这个代码问题的线索:使用RegisterName()。

以下是三个好处:

  1. 不使用成员变量保存对动态创建的TextBox的引用。
  2. 编译通过。
  3. 代码完整。

using System;
using System.Windows;
using System.Windows.Controls;

namespace AddControlsDynamically
{
    public partial class Window1 : Window
    {
        public void Window_Loaded(object sender, RoutedEventArgs e)
        {
            GenerateControls();
        }
        public void GenerateControls()
        {
            Button btnClickMe = new Button();
            btnClickMe.Content = "Click Me";
            btnClickMe.Name = "btnClickMe";
            btnClickMe.Click += new RoutedEventHandler(this.CallMeClick);
            someStackPanel.Children.Add(btnClickMe);
            TextBox txtNumber = new TextBox();
            txtNumber.Name = "txtNumber";
            txtNumber.Text = "1776";
            someStackPanel.Children.Add(txtNumber);
            someStackPanel.RegisterName(txtNumber.Name, txtNumber);
        }
        protected void CallMeClick(object sender, RoutedEventArgs e)
        {
            TextBox txtNumber = (TextBox) this.someStackPanel.FindName("txtNumber");
            string message = string.Format("The number is {0}", txtNumber.Text);
            MessageBox.Show(message);
        }
    }
}

这个很好用。我将其改为被接受的答案,因为它实现了我最初想要的功能。另一个也很好用,但我不想创建文本框并存储它们。 - David Basarab

7
另一种方法是在实例化时将相关的TextBox设置为Button Tag
btnClickMe.Tag = txtNumber;

这样,您就可以在事件处理程序中检索它。

protected void ClickMeClick(object sender, RoutedEventArgs e)
{
    Button btnClickMe = sender as Button;
    if (btnClickMe != null)
    {
        TextBox txtNumber = btnClickMe.Tag as TextBox;
        // ...
    }
}

5
您可以通过注册文本框的名称来使原始点击处理程序正常工作:
someStackPanel.RegisterName(txtNumber.Name, txtNumber);

这样可以让你调用StackPanel上的FindName方法并找到TextBox。

4

如果您想通过控件的可视树进行全面搜索,可以使用VisualTreeHelper类。

使用以下代码遍历控件的所有可视子元素:

for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parentObj); i++)
{
    DependencyObject child = VisualTreeHelper.GetChild(parent, i);

    if (child is TextBox)
        // Do something
}

如果您想深入查找树结构,您需要递归执行此循环操作,如下所示:
public delegate void TextBoxOperation(TextBox box);

public bool SearchChildren(DependencyObject parent, TextBoxOperation op)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(parent, i);

        TextBox box = child as TextBox;

        if (box != null)
        {
            op.Invoke(box);
            return true;
        }

        bool found = SearchChildren(child, op);

        if (found)
            return true;
    }
}

3
有没有办法将TextBox控件作为类中的字段而不是生成器方法内的变量?
public class MyWindow : Window
{
    private TextBox txtNumber;

    public void Window_Loaded()
    {
        GenerateControls();
    }

    public void GenerateControls()
    {
        Button btnClickMe = new Button();
        btnClickMe.Content = "Click Me";
        btnClickMe.Name = "btnClickMe";
        btnClickMe.Click += new RoutedEventHandler(this.CallMeClick);
        someStackPanel.Childern.Add(btnClickMe);
        txtNumber = new TextBox();
        txtNumber.Name = "txtNumber";
        txtNumber.Text = "1776";
        someStackPanel.Childern.Add(txtNumber);
    }

    protected void ClickMeClick(object sender, RoutedEventArgs e)
    {    
        // Find the phone number    
        string message = string.Format("The number is {0}", txtNumber.Text);        
        MessageBox.Show(message);
    }
}

这将起作用。但是没有找到它的方式吗?如果我有10个文本框会发生什么?解决方案是创建一个 List<Textbox> 然后使用 Linq 查找它们吗? - David Basarab
我接受了这个答案,因为它确实有效,如果我有10个文本框,我可以创建一个列表并在列表中找到它们。谢谢。 - David Basarab
代码无法编译:Childern、CallMeClick 未定义等。 - hughdbrown
someStackPanel 必须在您的 Xaml 中设置。此代码基于 OP 问题中提供的示例。 - bendewey
List<Textbox> 应该工作正常,你仍然只是保留了对控件的本地引用。 - bendewey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接