【问题标题】:Dynamically Generated TextBoxes动态生成的文本框
【发布时间】:2025-11-21 17:15:02
【问题描述】:

我需要一种动态创建多个文本框并访问它们的值的方法。

在我的表单中,用户输入一个 1 到 50 之间的数字,然后必须使用动态名称创建该数量的文本框,即成分 1、成分 2、成分 3、...成分 50 等。

我有一个 for 循环,它将使用该值创建多个文本框,但是如何将文本框值存储在字符串变量中?

这是当前为空的 for 循环

int i = Form1.ingredientCount;

for (i = 1; i < Form1.ingredientCount; i++)
{
    //create new text box
    //create new string that then holds value from text box
}

澄清:

用户在上一页输入一个数字。

然后这个数字决定了创建的文本框的数量和创建的字符串的数量。

文本框和字符串需要在 for 循环中具有唯一生成的 ID。

我还需要另一个文本框来显示每种成分的重量,尽管我自己可以弄清楚。

所以基本上我希望每个文本框和字符串都被命名为

"input" + i(其中 i 是增量器) 这样名字就会变成“input1”、“input2”、“input3”等等。

与包含文本框中数据的字符串相同。

【问题讨论】:

  • 如果成分应该是“酵母”、“鸡蛋”、“糖”之类的东西......只需从数组中取出它们
  • 我不确定你在这里问什么。我对文本框的来源以及需要它们位于全局变量中的意思感到困惑。 C# 有几个可以被视为“全局变量”的东西,但没有真正称为“全局变量”的概念,因此了解您要完成的工作会有所帮助。
  • 嗨,我会尝试更详细的解释首先用户输入一个数字然后这个数字用于for循环然后for循环需要生成一个新的文本框和一个唯一的字符串名称来存储用户随后在每个文本框中输入的内容。所以字符串基本上是读取文本框即string1 = TextBox1.ToString() string2 = TextBox2.ToString() ... string50 = TextBox50.ToString()
  • 这就是集合和数组的用途
  • 我从来没有理解过数组,希望有办法解决它。我很难理解数组的每一位是如何写入的,以及在需要时如何读取每一位。

标签: c# forms variables for-loop dynamic


【解决方案1】:

我以为我会编辑它,因为我似乎误解了这个问题

您可以将其绑定为如下形式:

public partial class Form1 : Form
{
    Recepie pancakes = new Recepie();
    IList<UniqueHolder> items = new List<UniqueHolder>();

    public Form1()
    {
        InitializeComponent();
        pancakes.Ingredients.Add(new Ingredient { Title = "Milk - 250 gr" });
        pancakes.Ingredients.Add(new Ingredient { Title = "Butter - 25 gr" });
        pancakes.Ingredients.Add(new Ingredient { Title = "Oil - 1 large spoon" });
        pancakes.Ingredients.Add(new Ingredient { Title = "Sugar - 100 gr" });
        pancakes.Ingredients.Add(new Ingredient { Title = "Flower - 200 gr" });
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {
        for (var i = 0; i < pancakes.Ingredients.Count; i++)
        {
            Ingredient ing = pancakes.Ingredients[i];
            TextBox tb = new TextBox { Location = new Point(10, i * 30), Size = new Size(200, 20), Text = ing.Title };
            UniqueHolder uh = new UniqueHolder { Ingredient = ing, TextBox = tb };
            this.Controls.Add(tb);
        }
    }
}

唯一的持有者对成分或文本框的变化进行数据绑定

public class UniqueHolder : IDisposable
{
    public Guid UniqueID { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is UniqueHolder)
        {

            return Guid.Equals(((UniqueHolder)obj).UniqueID, this.UniqueID);
        }
        return base.Equals(obj);
    }

    public override int GetHashCode()
    {
        return UniqueID.GetHashCode();
    }

    private TextBox textbox;
    public TextBox TextBox
    {
        get
        {
            return textbox;
        }
        set
        {
            if (object.Equals(textbox, value))
            {
                return;
            }
            if (textbox != null)
            {
                textbox.TextChanged -= OnTextChanged;
            }
            textbox = value;
            if (textbox != null)
            {
                textbox.TextChanged += OnTextChanged;
            }
        }
    }

    private Ingredient ingredient;
    public Ingredient Ingredient 
    {
        get
        {
            return ingredient;
        }
        set
        {
            if (object.Equals(ingredient, value))
            {
                return;
            }
            if (ingredient != null)
            {
                ingredient.PropertyChanged -= OnIngredientChanged;
            }
            ingredient = value;
            if (ingredient != null)
            {
                ingredient.PropertyChanged += OnIngredientChanged;
            }
        }
    }

    public UniqueHolder()
    {
        this.UniqueID = Guid.NewGuid();
    }

    protected virtual void OnIngredientChanged(object sender, PropertyChangedEventArgs e)
    {
        if (string.Equals(e.PropertyName, "Title", StringComparison.OrdinalIgnoreCase))
        {
            if (TextBox == null)
            {
                return;
            }
            TextBox.Text = Ingredient.Title;
        }
    }

    protected virtual void OnTextChanged(object sender, EventArgs e)
    {
        var tb = sender as TextBox;
        if (tb == null)
        {
            return;
        }
        if (Ingredient == null)
        {
            return;
        }
        Ingredient.Title = tb.Text;
    }

    public void Dispose()
    {
        Ingredient = null;
        TextBox = null;
    }
}

您可以使用这些成分回到接收器

public class Recepie : IDisposable
{
    private readonly IList<Ingredient> ingredients = new ObservableCollection<Ingredient>();
    public IList<Ingredient> Ingredients
    {
        get
        {
            return ingredients;
        }
    }

    protected virtual void OnIngredientsListChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems != null)
        {
            foreach (var item in e.OldItems)
            {
                var ing = item as Ingredient;
                if (ing == null)
                {
                    continue;
                }
                ing.Recepie = null;
            }
        }
        if (e.NewItems != null)
        {
            foreach (var item in e.NewItems)
            {
                var ing = item as Ingredient;
                if (ing == null)
                {
                    continue;
                }
                ing.Recepie = this;
            }
        }
    }

    public Recepie()
    {
        var obs = Ingredients as INotifyCollectionChanged;
        if (obs != null)
        {
            obs.CollectionChanged += OnIngredientsListChanged;
        }
    }

    public void Dispose()
    {
        int total = Ingredients.Count;
        for (int i = total; --i >= 0; )
        {
            Ingredients.RemoveAt(i);
        }
        var obs = Ingredients as INotifyCollectionChanged;
        if (obs != null)
        {
            obs.CollectionChanged -= OnIngredientsListChanged;
        }
    }
}

public class Ingredient : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private Recepie recepie;
    public virtual Recepie Recepie
    {
        get
        {
            return recepie;
        }
        set
        {
            if (object.Equals(recepie, value))
            {
                return;
            }
            recepie = value;
            RaisePropertyChanged("Recepie");
        }
    }

    private string title;
    public string Title
    {
        get
        {
            return title;
        }
        set
        {
            if (string.Equals(title, value))
            {
                return;
            }
            title = value;
            RaisePropertyChanged("Title");
        }
    }

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        var local = PropertyChanged;
        if (local != null)
        {
            local.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

【讨论】:

  • guid 创建随机字符串对吗?我需要能够稍后在程序中操作和访问这些字符串。对不起,我应该提到这一点。或者有什么方法可以让我以后轻松访问他们的名字?
  • 对,你没有理由不能将 Guids 保存在 Dictionary 之后才能访问文本框(我想知道为什么我在发布此内容后第二次被否决了...)
  • 投反对票的不是我,除非我有良好的声誉,否则我不能投反对票。您能否解释一下如何创建 Guid 然后将其添加到字典中?我需要在 for 循环之外创建字典吗?字典到底是做什么的?谢谢
  • 公会如何按要求“保存文本框中的 [the] 值”?
  • @stuartd:我只提到 Guid 是为了指出这是创建唯一 ID 的最简单方法,正如我认为他在第一次提出问题时所问的那样(老实说,我弄错了评论框对于答案框:))。但我尽我所能做出更好的实现,看来,我真的不需要 Guid...
【解决方案2】:

与其创建一个字符串来保存文本框中的值,不如将文本框保存在一个列表中,并在需要时从 Text 属性中获取值:这样就不需要连接 TextChanged 事件等。编辑:您不需要字符串变量来存储文本框文本,因为您现在可以在需要时通过列表和 Text 属性对其进行引用。

// a field 
List<TextBox> ingredientTextBoxes = new List<TextBox>();

private void Populate(int ingredientCount)
{
    for (i = 1; i < ingredientCount; i++)
    {
        // Assign a consecutive name so can order by it
        TextBox tb = new TextBox { Name = "IngredientTextbox" + i};
        ingredientTextBoxes.Add(tb);
    }
}

// when you want the ingredients
public List<string> GetIngredients
{
    List<string> ingredients = new List<string>();

    foreach (var tb in ingredientTextBoxes)
    {
        ingredients.Add(tb.text);
    }

    return ingredients;
}

// contains
private List<string> GetIngredientsMatching(string match)
{
    // Contains really should take a StringComparison parameter, but it doesn't
    return ingredientTextBoxes
                    .Where(t => t.Text.ToUpper().Contains(match.ToUpper()))
                    .Select(t => t.Text);
}

编辑:如果您要为每种成分设置多个值,则使用用户控件公开您想要的属性 - 总结数量、重量、描述(例如“一小撮”盐) - 以最方便的方式访问这些属性的代码。

【讨论】:

  • 谢谢,我认为这是我一直在寻找的完美解决方案。非常感谢,遗憾的是我还不能投票,否则我会。但还有一件事,我将如何读取该字符串?稍后在程序中我将需要使用 .Contains() 那么我将如何处理单个字符串呢?
  • @KieranWarren 文本始终可以直接从文本框控件获得,并且您将它们保存在列表中。见编辑。虽然您不能投票,但如果这是您问题的答案,您可以通过单击答案旁边的勾号来标记它。