C# - 动态创建控件并访问它们

时间:2012-04-03 10:00:17

标签: c# dynamic-controls

我正在使用标签功能创建一个简单的记事本类型的应用程序。我正在运行时创建TabControl,它的TabPages和RichTextBoxes。我让它们在类范围内实例化。还有一个名为New的MenuStrip项,通过单击可以添加更多标签页。

TabControl tbcEditor = new TabControl();
TabPage tbPage = new TabPage();
RichTextBox rtb = new RichTextBox();

private void frmTextEditor_Load(object sender, EventArgs e)
{
     Controls.Add(tbcEditor);
     tbcEditor.Dock = DockStyle.Fill;
     tbcEditor.TabPages.Add(tbPage);
     tbPage.Controls.Add(rtb);
     rtb.Dock = DockStyle.Fill;
}

private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
     //TabPage tbPage = new TabPage();
     //RichTextBox rtb = new RichTextBox();

     tbPage.Controls.Add(rtb);
     rtb.Dock = DockStyle.Fill;
     tbcEditor.TabPages.Add(tbPage);
}

我面临的问题有点难以解释。我会尽力的。当表单加载时,一切都按预期工作。 TabControl使用添加了RichTextBox的TabPage创建。但是,如果我单击“新建”按钮添加另一个页面,它就会变得疯狂。创建了一个新的TabPage但没有添加RichTextBox。也没有错误。如果我取消注释掉那两行(在MenuItem点击事件下),它创建2个TabPage和RichTextBox实例,一切都按我的意思运行。

现在我的第一个问题是为什么我必须再次创建只有这两种类型(TabPage,RichTextBox)但不是TabControl的新实例?正如您在最后一行中所看到的,我可以再次使用tbcEditor。但不是tbPagertb

当然,我可以继续在当地范围内再次声明它们,但另一个问题就出现了。如果我想说,添加复制,粘贴功能,我应该这样做,对吗?

Clipboard.SetDataObject(rtb.SelectedText);

但我无法访问rtb,因为它被声明为本地。

我对此感到非常困惑,所以对于如何克服这两个问题的任何建议和想法都将不胜感激。

谢谢。

4 个答案:

答案 0 :(得分:2)

If I un-comment out those 2 lines(under MenuItem click event), which creates 2 instances of TabPage and RichTextBox, everything works as I want.

当您取消注释这些行时,您将再次将富文本框和标签页的相同实例添加到容器面板中,这是毫无意义的。而是添加新控件foreach tabpage。 (我希望这是要求)

Now my first question is why do I have to make new instances of only those 2 types(TabPage, RichTextBox) again but not TabControl?

TabControl是父控件,它将TabPages作为子控件。您可以在一个TabControl下有多个选项卡。因此,除了已添加的tbcEditor之外,您无需创建TabControls。我们不会多次添加容器控件(除非是它的要求)。我们需要更多表格吗?不,只有一种形式可以保持所有儿童控制权。类似地,只有一个TabControl可以容纳TabPages的集合。只有当你想要子选项卡预设新选项卡时我才需要更多的TabControls,我认为这不是必需的。

But I can't access rtb since it is declared as local.

这没什么大不了的。你可以用两种方式做到:

1)通过循环搜索适当的控件。 SelectedTab属性提供了您想要的内容。

 private void someEvent(object sender, EventArgs e)
 {           
        foreach (Control c in tbcEditor.SelectedTab.Controls)
        {
            if (c is RichTextBox)
            {
                Clipboard.SetDataObject(((RichTextBox)c).SelectedText);
                break; //assuming u have just one main rtb there
            }
        }
 }

2)在创建时将每个rtb标记到tabPage,然后您可以获取所选标签页的标记元素以获取富文本框。我会采用这种方法。

修改(通常请对您的代码进行以下更改):

 TabControl tbcEditor = new TabControl();

 private void frmTextEditor_Load(object sender, EventArgs e)
 {
      Controls.Add(tbcEditor);
      tbcEditor.Dock = DockStyle.Fill;
      AddMyControlsOnNewTab();
 }

private void AddMyControlsOnNewTab()
{
    TabPage tbPage = new TabPage();
    RichTextBox rtb = new RichTextBox();
    tbPage.Tag = rtb; //just one extra bit of line.

    tbcEditor.TabPages.Add(tbPage);
    tbPage.Controls.Add(rtb);
    rtb.Dock = DockStyle.Fill;
}

private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
    AddMyControlsOnNewTab();
}

现在,您可以这样称呼它:

 private void someEvent(object sender, EventArgs e)
 {           
        RichTextBox rtb= (RichTextBox)tbcEditor.SelectedTab.Tag;
        Clipboard.SetDataObject(rtb.SelectedText);
        //or even better in just a line,
        //Clipboard.SetDataObject(((RichTextBox)tbcEditor.SelectedTab.Tag).SelectedText);
 }

这里你需要考虑的是你首先得到的控件是哪一个,哪个是你得不到的控件。你会得到TabPage但不是RichTextBox。所以你必须将RichTextBox标记为TabPage。你必须抛出它,因为Tag是object类型,所以你必须指定它是哪种类型的对象。最后,这种方法的优点是您无需遍历列表,因此其性能更高。并且你可以在TabPage中有更多的RichTextBox(如果你只想从一组RichTextBox中复制文本,每个TabPage中有一个)。

答案 1 :(得分:0)

评论的行正在做他们想要做的事情。该代码不会将Richtextbox与Tabpage相关联。

TabPage tbPage = new TabPage(); // Creates a new tabpage

RichTextBox rtb = new RichTextBox(); // Creates a new RichtextBox control.

TabControl是一个容器,所以一个实例就可以了。

另见 - http://sujay-ghosh.blogspot.in/2009/03/addingremoving-dynamically-created.html,与tabcontrols无关,但如何动态创建控件。

希望这有帮助。

答案 2 :(得分:0)

代码

tbPage.Controls.Add(rtb); 
rtb.Dock = DockStyle.Fill; 
tbcEditor.TabPages.Add(tbPage); 

获取现有文本框,将其添加到exisitng标签页,然后将现有标签页添加到编辑器中。由于这已经完成,没有任何反应。

当您添加这两行时,您可以创建文本框的新实例和新标签页,这正是您想要的。您的后一个问题来了,因为新声明的变量rtb隐藏了在类中声明的变量 - 在另一个方法中,您只能访问类中声明的onde(除非将控件从选项卡中取出)

要解决无法访问正确文本框的问题,可以将它们保存在列表(*)(或其他一些集合)中,并参考与当前活动选项卡关联的文本框。为此,您必须创建一个事件侦听器,以查看当前激活的选项卡。

(*)而不是只有一个

答案 3 :(得分:0)

确定您需要创建RichTextBox rathere的新实例,而不是尝试将相同的实例添加到每个选项卡。

 TabControl tbcEditor = new TabControl();
 //Get rid off this line --- TabPage tbPage = new TabPage();
 //Get rid off this line --- RichTextBox rtb = new RichTextBox();

 List<TabPage> _tabs = new  List<TabPage>();
 List<RichTextBox> _tbx = new  List<RichTextBox>();

 private void frmTextEditor_Load(object sender, EventArgs e) 
 {
      Controls.Add(tbcEditor);
      tbcEditor.Dock = DockStyle.Fill;

       AddNewTab();
 }

 private void newToolStripMenuItem_Click(object sender, EventArgs e) 
 {   
       AddNewTab();
 } 

 private void AddNewTab()
 {
    //TabPage 
        var tbPage = new TabPage();
        _tabs.Add(tbPage);

        //RichTextBox 
        var rtb = new RichTextBox();
        _tbx.Add(rtb);

        tbPage.Controls.Add(rtb);
        rtb.Dock = DockStyle.Fill;
        tbcEditor.TabPages.Add(tbPage); 
 }

这只是将tab和rtb添加到可以通过索引访问的集合中(也可以使用Dictionary进行命名访问等)。当然还有其他方法,包括只需命名组件并在需要时循环使用等等。