【问题标题】:Recreate a group of Controls重新创建一组控件
【发布时间】:2014-12-26 07:37:49
【问题描述】:

假设我有一个面板,其中包含... 3 个控件。我最终可能会为其添加更多控件或更改该面板中的位置。当程序启动时,我将以编程方式隐藏控件。最终,用户可以单击一个按钮,该按钮将创建原始面板的副本以填充表单上的一个区域。该按钮最终应该具有再次单击的选项,这意味着这些实例的多个实例可能会填充该区域。请记住,这些控件中可能有文本标签,可以在以后以编程方式单独设置或更改。我假设要执行此程序,我需要制作一个控件列表,也许是面板列表?考虑到我需要多次复制多个控件这一事实,我不确定如何执行此操作。

有没有一种很好、简单的方法来做到这一点?我真的不想使用任何类型的 3rd-party 包进行复制。

【问题讨论】:

    标签: c# windows duplicates


    【解决方案1】:

    你必须用代码来做,因此它会和你能写的一样好;-)

    说真的,最常上的课程是

    • 创建一个UserControl,它是一个与表单相关的类,具有您想要的布局..
    • ..并添加越来越多的实例..
    • ..通常是FlowLayoutPanel,通常是AutoScroll

    这是非常漂亮和简单的 imo。

    这里是一个简短的步行虽然..:

    • 首先,像往常一样,我们首先为 UserObject 类选择一个好听的名称,可能是“DataPanel”或“BookItem”。
    • 接下来我们创建它:转到项目资源管理器并右键单击,选择Add-New UserControl 并为其指定您选择的类名。我将使用“BookItem”。
    • 现在您可以看到 Designer 向您显示一个小的空控件。
    • 仔细观察:您还可以看到,在项目资源管理器中,现在不仅有新的“BookItem.cs”文件,还有补充的“BookItem.Designer.cs”甚至“BookItem.resx”文件;所以这很像创建一个新表单..
    • 让我们从工具箱中添加一些控件,我选择添加一个PictureBox、四个Labels 和一个NumericUpDown
    • 查看BookItem.Designer.cs 文件:在这里您可以看到您在Form.Desginer.cs 文件中看到的内容:您添加到布局的所有控件的所有设置和所有声明。特别注意声明(在文件底部):就像表单一样,默认情况下所有控件都声明为私有!
    • 我们现在可以处理布局并编写控件脚本。我们还可以向 UC 添加函数和属性,就像 Form 一样。
    • 请注意:您需要从外部访问、从表单或其方法读取的任何内容都必须是公开的!因此,如果您想访问 NUpDown,请称其为“nud_quantity”,您可以选择
      • 您可以将其在 BookItem.Designer.cs 中的声明从 private 更改为 public 或在 Designer 中通过更改 Modifiers 属性来更改
      • 或者您可以在 UC 中编写一个公共函数来获取/设置其值
    • 在这两种方式之间进行选择是个人喜好问题;如果其他开发人员将使用 UC 类,最好通过编写访问方法来密切控制您公开的内容。

    • 编译项目后,您可以在工具箱中看到新的 UC。

    • 您现在可以从工具箱中添加它,或者
    • 您可以将其添加到代码中,就像您动态创建的任何控件一样。

    我们来看一个例子:

    想象一下书店中的简单订购系统:客户在我们的书店中搜索了书籍,并在DataGridView 'dgv_bookList' 中看到了书籍列表,只读,多选。右侧有一个 FlowLayoutPanel 'flp_cart' 代表购物车。我们有一个命令按钮“cb_addItems”来将选定的书籍添加到购物车。

    Button 的脚本可能是这样的:

    private void cb_addItems_Click(object sender, EventArgs e)
    {
        if (dgv_bookList.SelectedRows.Count <= 0) return;
    
        foreach (DataGridViewRow row in dgv_bookList.SelectedRows)
        {
            BookItem book = new BookItem (row);
            book.label1.Text = "#00" + book.label1.Text;
            book.Name = book.label1.Text;
            flp_cart.Controls.Add(book);
    
        }
    }
    

    这将为 DGV 中的每个选定行添加一个 BookItem。

    上面代码有几点需要注意:

    我将 DataGridViewRow 传递给 UC 的构造函数,以便它可以直接设置其标签!这意味着,除了设计师为我们构建的无参数构造器之外,我们还需要编写第二个构造器,可能是这样的:

    public bookItem()
    {
        InitializeComponent();
    }
    
    public bookItem(DataGridViewRow bookData)
    {
        InitializeComponent();
        label1.Text = bookData.Cells[0].FormattedValue.ToString();
        label2.Text = bookData.Cells[1].FormattedValue.ToString();
        label3.Text = bookData.Cells[2].FormattedValue.ToString();
        label4.Text = bookData.Cells[3].FormattedValue.ToString();
    }
    

    相反,您可以编写 public setData(DataGridViewRow bookData) 函数。

    还要注意我的标签被命名为多么愚蠢!我希望你能做得更好!

    还要注意我如何访问“label1”并从表单中的按钮修改其文本;为此,我必须更改其在 Desginer.cs 文件中的声明:

    private System.Windows.Forms.PictureBox pb_cover;
    public System.Windows.Forms.Label label1;          // <<----expose this label !
    private System.Windows.Forms.Label label2;
    private System.Windows.Forms.Label label3;
    private System.Windows.Forms.Label label4;
    private System.Windows.Forms.NumericUpDown numericUpDown1;
    

    通常更可取:一个访问函数,可能是这样的:

    public int quantity() { return (int) numericUpDown1.Value; } 
    

    或者,当然是一个属性:

    public int quantity {  get { return (int)numericUpDown1.Value;  }  }
    

    另外请注意,我将 BookData 项的名称设置为第一个数据项的某个变体,即我的图书 ID。这也可能或更好地发生在构造函数中;并且应该检查以防止两次添加相同的项目..

    总而言之,使用 UserControls 与使用 Forms 非常相似,包括所有常见的跨表单通信方式或技巧:保留引用、公开成员、创建属性和函数..

    最后一个注意事项:与表单或子类控件一样,有一个问题:通过将它们放置在设计器中,您可以指定设计器在设计期间显示您的 UC。

    这通常没问题;然而,也有可能引入细微的错误,使设计者无法显示控件。在设计器能够显示控件或包含它的任何窗体之前,您需要更正这些问题。让我们看一个此类问题的简单示例:

    让我们在 UC 中编写 PictureBox 'pb_cover' 的 Paint 事件:

    public Brush myBrush = null;
    
    private void pb_cover_Paint(object sender, PaintEventArgs e)
    {
        if (pb_cover.Image == null)
        {
            Size  s = pb_cover.ClientSize;
            e.Graphics.FillRectangle(myBrush, 0, 0, s.Width, s.Height);
            e.Graphics.DrawLine(Pens.Red, 0, 0, s.Width, s.Height);
            e.Graphics.DrawLine(Pens.Red, s.Height, 0, 0, s.Width);
        }
    }
    

    让我们修改添加按钮中的代码:

     BookItem book = new BookItem (row);
     book.label1.Text = "#00" + book.label1.Text;
     book.myBrush = Brushes.OliveDrab;
     flp_cart.Controls.Add(book);
    

    现在,如果您运行该程序,一切都会好起来的。即使您尝试在设计器中查看 UC,也可能会或可能不会出现问题。但是,一旦您尝试打开放置 UC 的表单,Desginer 就会崩溃并告诉您它无法工作,因为 Brush 为空。这里的补救措施很简单:为 Brush 声明添加一个默认值,一切都很好。其他情况可能需要多考虑。

    顺便说一句,我什至没有遇到问题,因为我没有在表单上放置 BookItem 的实例;它们仅在添加按钮中创建..

    我希望这能让你开始!

    【讨论】:

    • 您介意分享更多信息吗?我尝试制作一个 UserControl,然后使用 Add() 函数制作一个副本,但似乎 Add() 函数从原始面板中删除了控件并将其放入 UserControl。
    • 确实,你说得对。添加听起来不像,但实际上是一个“移动”。如果您已经制作了一个 UserControl,那么您可以像使用任何其他控件一样使用它。如果您想在代码中向表单添加另一个副本,您可以像任何其他对象一样通过声明它来动态创建它,也许作为该新类型的 alist 中的新元素并通过调用 new 来实例化它。我' 会在短时间内在答案中添加几行,好吗? (你怎么称呼你 UC 的?)
    • 我的问题是我不想以编程方式执行此操作。我想以一种可以使用我在 [设计] 模式下制作的面板的方式来完成它。这是因为这个大项目肯定需要用更多控件更新该面板。我的意思是,如果我最终必须这样做,我会这样做,但我更喜欢复制父面板并以此为基础创建一个 UserControls 列表。
    • 另外,您创建的任何代码/伪代码将不胜感激!
    • 您的案例显然需要 UserControl.. 请参阅上面的演练!
    猜你喜欢
    • 2018-08-01
    • 2016-03-31
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多