Can we create a 1-D array of UserControl. ..? - c#

I just created a "Usercontrol" in WINFORMS- it just contains 1-Button with some style.
And i need to use the same as array(10) and load it to a form.
Ex:
Dim myButton() As Button = New ucSpecialButton(dataset4Category(i).Tables(0).Rows.Count - 1) {}
Here my usercontrol name is ucSpecialButton
can we create a ONE-Dimensional Array of a WINFORM usercontrol.?

With MAKKAM's words: Yes, you can. I guess you're actually uncertain about whether you can add a dynamic number of controls to a form, because in the designer you cannot define any arrays, you just drag and drop a certain number of controls on the form.
However, in fact Visual Studio simply generates some code in background that adds these controls to a collection. You can just as well write your own code to add an arbitrary number of UserControls to the collection dynamically. Just look at the forms' .designer.cs file to see how it works.
Taking MAKKAM's array controls it could look like this, e.g.:
public MyForm()
{
InitializeComponent(); // this is the call to the auto-generated code
// Here you could add you own code:
foreach (Control control in controls)
{
this.Controls.Add(control); // this is how to add a control to the form.
}
}

Yes, you can.
Control[] controls = new Control[10];
So, what's the problem?

I've just noticed that you edited your question. If I got it right, the only thing you're missing now is (I'm a C# guy, might be that there are some flaws in the following VB.NET code):
for i = 0 to dataset4Category(i).Tables(0).Rows.Count - 1
myButton(i) = New ucSpecialButton();
// ... specific button properties ...
next
For, the code you pasted in your question doesn't create the buttons yet, it only allocates memory for the array:
Dim myButton() As Button = New ucSpecialButton(
dataset4Category(i).Tables(0).Rows.Count - 1) {}
New in this place means to create a new array for the references, not to create new objects. ucSpecialButton(...) in this place is not the constructor for an object. Instead it only denotes the type of object you want to prepare the array for. You can IMHO just as well write New Button(...).
By the way: IMHO it should be
`New ucSpecialButton(dataset4Category(i).Tables(0).Rows.Count)`
Without the - 1. In the for loop however, the - 1 is correct (an array of size 10 goes from 0..9).

Related

Recreate a group of Controls

Let's say that I have a panel with like... 3 controls in it. I may end up adding more controls to it or changing the positioning within that panel. When the program starts, I will programmatically HIDE the control. Eventually, the user can click a button that will create a duplicate of the original panel to populate an area on the form. The button should have the option for another click eventually, meaning that multiple instances of these can come about to populate this area. Remember that these controls may have text labels within them that can be individually set or altered later on, programmatically. I am assuming that to do this program, I need to make a List of controls, maybe a List of panels? I'm not exactly sure how to do this considering the fact that I need multiple controls duplicated multiple times.
Is there a nice, simple way to do this? I really don't want to do the duplication with any kind of 3rd-party package.
You will have to do it in code and therefore it'll be as nice as you can code ;-)
Seriously the course most often taken is to
create a UserControl which is a class related to a form, with the layout you want..
..and add more and more instances of it..
..often to a FlowLayoutPanel, often with AutoScroll
This is pretty nice and simple imo.
Here is a short walk-though..:
first we start, as usual, by picking a nice name for the UserObject class, maybe 'DataPanel' or 'BookItem'..
Next we create it: Go to the project explorer and right-click, choosing Add-New UserControl and give it the class name you chose. I'll use 'BookItem'.
Now you can see the Designer showing you a small empty control.
Look closer: You can also see that in the project explorer ther is now not only the new 'BookItem.cs' file but also the complementary 'BookItem.Designer.cs' and even a 'BookItem.resx' file; so this works very much like creating a new Form..
Let's add a few controls from the toolbox, I chose to add a PictureBox, four Labels and a NumericUpDown.
Have a look at the BookItem.Designer.cs file: Here you can see the very things you see in a Form.Desginer.cs file: All settings and all declarations for all controls you add to the layout. Note especially the declarations (at the bottom of the file): Just like for a Form, all controls by default are declared as private!
We can now work on the layout and script the controls. We also can add functions and properties to the UC, just like a Form.
Please note: Anything you need to access from outside, read from your form or its methods must be public! So if you want to access the NUpDown, let call it 'nud_quantity' you have a choice
You can change its declaration in the BookItem.Designer.cs from private to public or in the Designer by changing the Modifiers property
Or you can write a public function in the UC to get/set its value
Chosing between those two ways is a matter of taste; if other developers will work with the UC class, it will probably be better to put close control over what you expose by writing access methods.
After you have compiled the project you can see the new UC in the Toolbox.
You can now either add it from the Toolbox or
you can add it in code like any control you create dynamically.
Let's look at an example:
Imagine a simple order system in a bookstore: The customer has done a search on the books in our store and is presented with a list of books in a DataGridView 'dgv_bookList', readonly, multiselect. To the right there is a FlowLayoutPanel 'flp_cart' represeting a shopping cart. And we have a command button 'cb_addItems' to add selected books to the cart.
The Button might be scripted like this:
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);
}
}
This will add one BookItem for each selected row in the DGV.
A few things to note on the above code:
I pass a DataGridViewRow into the constructor of the UC so it can directly set its labels! This means that, in addition to the parameterless contructor the desginer has built for us, we need to write a second contructor, maybe like this:
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();
}
Instead you could write a public setData(DataGridViewRow bookData) function.
Also note how stupid my labels are named! You can do better than that, I hope!
Also note how I access 'label1' and modify its Text from a Button in the Form; to do that I had to change its declaration in the Desginer.cs file:
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;
Often preferrable: An access function, maybe like this:
public int quantity() { return (int) numericUpDown1.Value; }
Or, of course a Property:
public int quantity { get { return (int)numericUpDown1.Value; } }
Also note, that I set the Name of the BookData item to some variant of the 1st data item, my book id. This might as well, or better, happen in the constructor; and there should be a check to prevent adding the same item twice..
All in all one can say, that using UserControls is very much like working with Forms, including all the usual ways or tricks for inter-form communication: keep references, expose members, create properties and functions..
One final Note: Like with forms or subclassed controls there is one catch: By placing them in the designer, you assign the designer the responsiblity to display your UC during design time.
This is normally just fine; however it is also possible to introduce subtle mistakes which make it impossible for the designer to display the control. You need to correct these problems before the designer will be able to show a control or any form that contains it. Let have a look at a simple example of such a problem:
Let's script the Paint event of the PictureBox 'pb_cover' in the UC:
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);
}
}
And let's modify the code in the Add button:
BookItem book = new BookItem (row);
book.label1.Text = "#00" + book.label1.Text;
book.myBrush = Brushes.OliveDrab;
flp_cart.Controls.Add(book);
Now, if you run the program all will be fine. Even if you try to look at the UC in the designer there may or may not be problems. But once you try to open a Form on which the UC was placed, the Desginer will crash and tell you that it can't work, since the Brush is null. Here the remedy is simple: add a default value to the Brush declaration and all is well. Other situations may need a little more thinking..
I don't even run into the problem btw, since I have not placed an instance of BookItem on the Form; they are only created in the Add Button..
I hope that gets you started!

How to add link Labels at run time in Windows form

I have been making a Windows Form Application in C# using the Visual C# 2008 IDE.
There are basically two forms in my application. One is created at Runtime and it's layout is undefined and the second one's predefined.
Now, I have been adding form elements using the toolbox provided and don't have any idea how to add them using written code(not using toolbox). I want to add n number of Labels to the second form which is undefined. n can be anything(decided at runtime, depending on the user's input). Can anybody tell me what is the efficient way to do this?
Just a quick example of a "dynamic control" being created at run-time and added to the form:
Label lbl = new Label();
lbl.Text = "Hello World!";
lbl.Location = new Point(100, 25);
this.Controls.Add(lbl);
You can replace "this" with the container to add it to, like "panel1" for instance. For containers that have their own layout engine, like a FlowLayoutPanel, then you wouldn't need to specify a Location().
Create a new LinkLabel(), set its properties (in particular, text and position), then add it to the Controls collection of your form or any panel.
You may also want to add event handlers, and store them somewhere (probably in a List<T>) so you can change or remove them later.
Create one in the designer, configure it's properties as you wish. Then go to the designer file, which name is like Form1.Desiner.cs, copy the code related to your LinkLabel (find everything with text search) and paste it where you wish :)

Load form's controls to panel in C#

I want to load Form's controls to a panel in C# so the panel will show the same components as the form. I have tried this code:
foreach (Control control in (new Form2()).Controls)
{
panels[panelsCounter].Controls.Add(control);
}
But the problem is that when I'm running the program it loads only the type of control that I've added last (For example if I've been added a label and than I've added a button to the form it shows only a button, but if I add another label, it shows both of the labels, but not the button).
Please help me.
This is a classic bug, you are modifying the collection while you are iterating it. The side-effect is that only ever other control will be moved to the panel. You'll need to do this carefully, iterate the collection backwards to avoid the problem:
var formObj = new Form2(); //???
for (int ix = formObj.Controls.Count-1; ix >= 0; --ix) {
panels[panelsCounter].Controls.Add(formObj.Controls[ix]);
}
Controls are not designed to be displayed multiple times. You cannot add controls to multiple forms, or add the same control to a form multiple times. They simply weren't designed to support it.
You could go through each control and create a new control of the same type, and even copy over the values of their properties (or at least what's publicly accessible to you), effectively cloning them, but it's important that it be a different control that you add to the new panel.

Array of Components in Forms Designer

Is there any good way in Windows Forms Designer to have an array (or other collection) of similar components defined? You can check "GenerateMember", which will give you a unique named member in code to reference, but what if I want that generated member to be one of a list of some sort, so that I can iterate through them easily? The only way I can think of is to create a new list on load, and the manually add all the already-generated members to that list, which is tedious and redundant. I thought about editing the code that generates these components, but it's inside that auto-generated "Do not modify" section, and I'm afraid any change I make there will be overwritten any time I make other changes to the form. Does the Form Designer just not have this functionality?
If I understand your question correctly, you can simply cast "this" (the form) to a "Control", and then examine it's "ControlCollection" property- a collection containing the form's controls. Eg.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var myControlList = ((Control)this).Controls;
}
}
Unless I am missing something...
You could put them inside a Panel and then iterate through its controls.

c# wpf how to reference a dynamically added control

Just starting out with C#, so this may be overly simple that I keep overlooking it...
In the main window I have a stackpanel, named targetDocArea, that will hold controls.
Based on user input, controls appear in the panel like so:
var htmlView = new System.Windows.Controls.WebBrowser();
htmlView.MinHeight = 200;
htmlView.Height = deskHeight - 225;
htmlView.Name = "targetDocControl";
htmlView.Navigate(dlg.FileName);
this.targetDocArea.Children.Add(htmlView);
Now I have another function that needs to interact with that control - and that's where I'm a bit lost. I would think there would be some index I could use to reference the children of the panel or directly using the name.
I've been reading about "this.registerName" but I'm not sure if that is correct way to approach this problem.
Any guidance would be greatly appreciated - and I wouldn't mind changing from a stackpanel to something more suited that would allow this interactivity.
Thanks.
You should store the control in a field in your class, like this:
private WebBrowser htmlView;
You can then use this field in any function.
If you need to store a number of copies of the control, you can use a List<WebBrowser> field, like this:
private List<WebBrowser> htmlViews = new List<WebBrowser>();
//Elsewhere:
htmlViews.Add(something);

Categories

Resources