How to duplicate a custom control (panel) programmatically - winforms

I am using C# in VS 2010. I created a custom panel and would like to add this custom panel 9 times so I created a loop to add a copy of the panel 9 times at equal distance from each other. Each panel will have its own text and image. All I'm getting though is a single panel. Any insight would be appreciated
public partial class Form1 : Form
{
int index = 0;
List<CutePanel.CustomPanel> MenuItems = new List<CutePanel.CustomPanel>();
public Form1()
{
InitializeComponent();
for (int i = 0; i < 9; i++)
{
this.cpTest.BackColor = System.Drawing.SystemColors.ActiveCaption;
this.cpTest.LabelText = "My super click text";
this.cpTest.Location = new System.Drawing.Point(12, 12+(64*i));
this.cpTest.Name = "cpTest";
this.cpTest.Size = new System.Drawing.Size(344, 58);
this.cpTest.SuperClick = null;
this.cpTest.TabIndex = 6;
}
cpTest.MouseClick += new MouseEventHandler(cpTest_MouseClick);
cpTest.SuperClick += new EventHandler(cpTest_SuperClick);
cpTest.LabelText = "This is my text.";
MenuItems.Add(cpTest);
}
void cpTest_SuperClick(object sender, EventArgs e)
{
tcTest.SelectedIndex = index++ % 2;
}
void cpTest_MouseClick(object sender, MouseEventArgs e)
{
tcTest.SelectedIndex = index++ % 2;
}
private void customPanel3_MouseClick(object sender, MouseEventArgs e)
{
tcTest.SelectedIndex = index++ % 2;
}
}
Thanks.

You have to make a distinction between your panel class and panel objects, also called instances of this class. Think of the class as a template that serves in creating objects. These objects are created with the new keyword:
for (int i = 0; i < 9; i++)
{
var cp = new CutePanel.CustomPanel();
cp.BackColor = System.Drawing.SystemColors.ActiveCaption;
cp.LabelText = "My super click text";
cp.Location = new System.Drawing.Point(12, 12+(64*i));
cp.Name = "cpTest" + i;
cp.Size = new System.Drawing.Size(344, 58);
cp.SuperClick = null;
cp.TabIndex = 6;
cp.MouseClick += new MouseEventHandler(cpTest_MouseClick);
cp.SuperClick += new EventHandler(cpTest_SuperClick);
cp.LabelText = "This is my text.";
MenuItems.Add(cp);
}
You can also assign it values from the existing panel:
cp.BackColor = cpTest.BackColor;
cp.Size = cpTest.Size;
...
An elegant way of making a duplicate is to include a Clone method in your panel class
public class CustomPanel
{
...
public CustomPanel Clone()
{
var cp = (CustomPanel)this.MemberwiseClone();
cp.Parent = null; // It has not yet been added to the form.
return cp;
}
}
Then
for (int i = 0; i < 9; i++)
{
CustomPanel cp = cpTest.Clone();
// Now only change the differing properties
cp.Location = new System.Drawing.Point(12, 12+(64*i));
cp.Name = "cpTest" + i;
cp.TabIndex += i + 1;
MenuItems.Add(cp);
}
but attention: If the cloned control is a container control containing other controls, you must clones those recursively as well. I.e., you must perform a deep clone! Creating new controls as shown in my first code snippet is safer.

Related

How do I call a control that was created dynamically in c#

First, a great thank you to those who asked/responded to questions. You were able to get me this far.
I wanted to help a young Belgian entrepreneur by taking on a challenge, build a Media managing software to display various media types (Images, Videos, links, text) on huge LED screens.
I have limited coding experience as I work in EDI.
My issue is that I create playlists dynamically based on the number of playlists in the DB (see screenshot), but I cannot trigger the playing of the right playlist when pressing the play button.
Warning, my code is noob code.
PlayList ScreenShot
Label playListLbl = new Label();
GroupBox playListGrp = new GroupBox();
public GroupBox addplayListGrp(int i, int start, int end)
{
GroupBox playListGrp = new GroupBox();
playListGrp.Name = "playListGrp"+ Convert.ToString(1 + i);
playListGrp.Text = "Play list " + Convert.ToString(1 + i);
playListGrp.Font = new Font("Century Gothic", 12F,
FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
playListGrp.Width = 425;
playListGrp.Height = 525;
playListGrp.Margin = new Padding(1);
playListGrp.Location = new Point(start, end);
return playListGrp;
}
Button addPlayBtn(int i)
{
Button PlayBtn = new Button();
PlayBtn.Font = new Font("Century Gothic", 9.75F,
System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point, ((byte)(0)));
PlayBtn.ForeColor = Color.Black;
PlayBtn.Location = new Point(10, 467);
PlayBtn.Name = "playBtn" + Convert.ToString(1 + i);
PlayBtn.Size = new Size(100, 30);
PlayBtn.TabIndex = 6;
PlayBtn.Text = "Play";
PlayBtn.UseVisualStyleBackColor = true;
PlayBtn.Click += new EventHandler(playBtn1_Click);
return PlayBtn;
}
public BMS_main()
{
int startPos = 5;
int endPos = 5;
for (int i = 1; i <= playlistCountInc; i++)
{
playListGrp = addplayListGrp(i, startPos, endPos);
playListLbl = addLabel(i);
Label playListLblTime = addLabelTime(i);
Button PlayBtn = addPlayBtn(i);
}
playListGrp.Controls.Add(playListLbl);
playListGrp.Controls.Add(playListLblTime);
playListGrp.Controls.Add(playlistView);
playListGrp.Controls.Add(PlayBtn);
}
private void playBtn1_Click(object sender, EventArgs e)
{
if (ScreenStatus)
{
Playing = true;
DisplayTimer.Stop();
DisplayTimer.Enabled = false;
InitialScreenTimer.Stop();
InitialScreenTimer.Enabled = false;
PlayListTimer.Enabled = true;
PlayListTimer.Start();
}
else
{
message = "Veuillez alimenter les panneaux";
result = MessageBox.Show(message, caption, buttons);
}
public void PlayListTimer_Tick(object sender, EventArgs e)
{
Label lblAcessorio4 =
(Label)playListLbl.Controls.Find("playLbl4",
true).FirstOrDefault();
if (lblAcessorio4 != null)
{
lblAcessorio4.Text = "Test lblAcessorio4";
}
else
{
message = "Label is null";
result = MessageBox.Show(message, caption, buttons);
}
Set the Tag property of your button with something which will help you decide later on which song to play:
playListGrp = addplayListGrp(i, startPos, endPos);
playListLbl = addLabel(i);
Label playListLblTime = addLabelTime(i);
Button PlayBtn = addPlayBtn(i);
// You can do this
PlayBtn.Tag = playListGrp; // or anything else
Then in the button click handler, get the value of the Tag and make a decision based on that. Just keep in mind that whatever you set the Tag to, you will need to cast it back to that type. For example, in the above I set it GroupBox so I will cast it to a GroupBox:
private void playBtn1_Click(object sender, EventArgs e)
{
GroupBox gb = ((Button)(sender)).Tag as GroupBox;
// Now make the decision
if(gb.Name == "whatever you need to put here"){ // do whatever }
}
I would put the lisbox and then get the selected item and play that.

Resizing a form introduces visual artifacts on bottom docked controls

I am working on a "bottom-right docked" popup form.
The layout of the form follows this structure:
Header
Content (text message)
Footer
This form should resize according to its content.
I am using SetBounds to make it grow to the top instead of the bottom (remember that the window is docked bottom-right).
However, when the animation occurs, the footer redraws itself in a very bad way, as its form-relative location is continuously updated.
I provide a sample to give you an idea:
using System.Windows.Forms;
namespace AnimatorTest
{
public class Form3 : Form
{
Timer timer = new Timer();
public Form3()
{
timer.Interval = 30;
timer.Tick += timer_Tick;
// Create 3 test buttons
for (int i = 0; i < 3; i++)
{
Button b = new Button() { Dock = DockStyle.Bottom };
b.Click += (s, e) => timer.Start();
b.Text = "Click and watch how ugly I am during the animation.";
Controls.Add(b);
}
Height = 100;
StartPosition = FormStartPosition.CenterScreen;
}
void timer_Tick(object sender, System.EventArgs e)
{
int desiredHeight = 500;
int difference = desiredHeight - Height;
int change = difference / 6;
if (System.Math.Abs(change) < 1)
{
SetBounds(Left, Top - difference, Width, Height + difference);
timer.Stop();
}
else
{
SetBounds(Left, Top - change, Width, Height + change);
}
}
}
}
I don't really have any idea to work around this.
Thanks.

Finding controls in Windows Forms C# .NET?

Using Windows Forms, two link labels are created dynamically. When the user clicks on anyone of links labels, one dynamic form is created. In that form I created one data grid, a text box and a button placed dynamically (in that dynamic form). Now I want to access the dynamic data grid in the dynamic button click event. How can I do that?
private void Users_Load(object sender, EventArgs e)
{
da = new SqlDataAdapter("Usp_Get_Employees", con);
ds = new DataSet();
da.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
string somecode = i.ToString() + ds.Tables[0].Rows[i]["eid"].ToString();
LinkLabel lbluser = new LinkLabel();
lbluser.Name = ds.Tables[0].Rows[i]["eid"].ToString();
lbluser.Text = ds.Tables[0].Rows[i]["ename"].ToString();
lbluser.Location = new System.Drawing.Point(40, i * 40);
lbluser.Size = new System.Drawing.Size(50, 30);
Controls.Add(lbluser);
lbluser.Click += new EventHandler(lbluser_Click);
}
}
}
void lbluser_Click(object sender, EventArgs e)
{
LinkLabel lnkClis = (LinkLabel)sender;
Form frm = new Form();
frm.Name = lnkClis.Name;
frm.Text = lnkClis.Text;
frm.Show();
DataGrid dtgrd = new DataGrid();
dtgrd.Location = new System.Drawing.Point(10, 1 * 40);
dtgrd.Name = lnkClis.Name;
names = lnkClis.Name;
TextBox tx = new TextBox();
tx.Location = new System.Drawing.Point(10, 5 * 40);
tx.Size = new Size(80, 30);
tx.Multiline = true;
tx.LostFocus += new EventHandler(tx_LostFocus);
Button btn = new Button();
btn.Location = new System.Drawing.Point(10, 7 * 40);
btn.Size = new System.Drawing.Size(50, 30);
btn.Name = lnkClis.Name;
btn.Click += new EventHandler(btn_Click);
frm.Controls.Add(dtgrd);
frm.Controls.Add(tx);
frm.Controls.Add(btn);
}
// Now I am trying to access the data grid in the btn_click event
void btn_Click(object sender, EventArgs e)
{
Button btsave = (Button)sender;
string eid = btsave.Name;
object grd = btsave.Parent.Controls.Find("dtgrd", true).FirstOrDefault();
((DataGrid)grd).DataSource = ds.Tables[0];
}
Now I am getting an error object set of instances of an object at:
((DataGrid)grd).DataSource = ds.Tables[0];
The exception message you have written:
Now I am getting an error object set of instances of an object at
makes no sense, but it looks like
Object reference not set to an instance of an object
If this is the case, I think the error lays in Find method call. According to documentation:
Searches for controls by their Name property and builds an array of all the controls that match.
In your button click handler you assume that grid is called dtgrd, but while you create a grid you name it like this:
dtgrd.Name = lnkClis.Name;
it will suffice if you change this line to:
dtgrd.Name = "dtgrd";
Having said that, you should consider using an anonymous method for the button click handler. It will eliminate need for calling the Find method in the first place.
void lbluser_Click(object sender, EventArgs e)
{
//...
DataGrid dtgrd = new DataGrid();
//...
Button btn = new Button();
//...
btn.Click += (sender,args)=> dtgrd.DataSource = ds.Tables[0];
Try the following code
public Form1()
{
Form f1 = new Form();
f1.Text = "New Form";
TextBox t1 = new TextBox();
t1.Top = 0;
t1.Name = "t1";
t1.Visible = true;
f1.Controls.Add(t1);
Button b1 = new Button();
b1.Top = 30;
b1.Name = "b1";
b1.Text = "Click";
b1.Click += b1_Click;
f1.Controls.Add(b1);
f1.Show();
}
public void b1_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
object txt = btn.Parent.Controls.Find("t1", false).First();
((TextBox)txt).Text = "Hi, you have clicked me.";
}
I modified Nitesh's code a bit. Just capture the textbox in the click handler using a lambda:
public Form1()
{
Form f1 = new Form();
f1.Text = "New Form";
TextBox t1 = new TextBox();
t1.Top = 0;
t1.Name = "t1";
t1.Visible = true;
f1.Controls.Add(t1);
Button b1 = new Button();
b1.Top = 30;
b1.Name = "b1";
b1.Text = "Click";
b1.Click += (sender, args) => MessageBox.Show("The text is: " + t1.Text);
f1.Controls.Add(b1);
f1.Show();
}
The error you are getting is from the statement (as the grd object is null):
((DataGrid)grd).DataSource = ds.Tables[0];
Since you are trying to catch hold of a dynamic control, it's good have a proper null checks, type checks, and error handling. Something like this:
if(grd != null && grd is DataGrid)
((DataGrid)grd).DataSource = ds.Tables[0];

Why is the ListView with an ImageList very slow? (re: 1000+ thumbnails)

I'm trying to use a ListView component to show around 1,000 image thumbnails and I'm having some performance problems.
First I create an ImageList containing my 1,000 images. This is lightning fast and takes under a second.
However, once I assign the ImageList to my ListView, it takes around 10+ seconds.
Example:
ImageList _imgList = GetMyImageList(); // takes under 1 second
ListView _lstView = new ListView();
lstView.LargeImageList = _imgList; // takes 10+ seconds
Is there anything I can do to increase performance? My ImageList contains images that are already resized into thumbnail size (197x256 pixels) so that's not the problem... (and creating my ImageList only takes 1 second at the most).
Does the data in your list view change frequently? Do you load new image lists frequently?
I tried your scenario and got a few seconds of loading time (since I'm generating random images) but very fast refresh times when changing list view [View] modes as well as scrolling.
Here is the sample code. Try it out and let me know how it works.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication10
{
public partial class FormListView:
System.Windows.Forms.Form
{
public FormListView ()
{
string [] names = null;
this.InitializeComponent();
names = Enum.GetNames(typeof(View));
for (int i=0; i < names.Length; i++)
{
this.comboBox1.Items.Add(names [i]);
if (names [i] == this.ListView.View.ToString())
this.comboBox1.SelectedIndex = i;
}
}
private void comboBox1_SelectedIndexChanged (object sender, EventArgs e)
{
this.ListView.View = (View) Enum.Parse(typeof(View), this.comboBox1.SelectedItem.ToString());
this.ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
}
private void ButtonLoadImages_Click (object sender, System.EventArgs e)
{
Image image;
Stopwatch watch;
this.Enabled = false;
this.Cursor = Cursors.WaitCursor;
this.ListView.SmallImageList = null;
this.ListView.LargeImageList = null;
this.ListView.StateImageList = null;
while (this.ImageList.Images.Count > 0)
{
this.ImageList.Images [0].Dispose();
this.ImageList.Images.RemoveAt(0);
}
this.ImageList.ImageSize = new System.Drawing.Size(256, 256);
watch = Stopwatch.StartNew();
for (int i=0; i < 1000; i++)
{
image = new Bitmap(this.ImageList.ImageSize.Width, this.ImageList.ImageSize.Height);
using (Graphics graphics = Graphics.FromImage(image))
{
graphics.Clear(Color.White);
graphics.DrawRectangle(Pens.Red, 10, 10, this.ImageList.ImageSize.Width - 20, this.ImageList.ImageSize.Height - 20);
graphics.DrawString(i.ToString(), this.Font, Brushes.Blue, 20, 20);
}
this.ImageList.Images.Add(image);
}
watch.Stop();
this.ListView.SmallImageList = this.ImageList;
this.ListView.LargeImageList = this.ImageList;
this.ListView.StateImageList = this.ImageList;
this.Text = watch.Elapsed.TotalSeconds.ToString();
this.Cursor = Cursors.Default;
this.Enabled = true;
}
private void ButtonLoadItems_Click (object sender, System.EventArgs e)
{
Stopwatch watch;
ListViewItem item;
this.Enabled = false;
this.Cursor = Cursors.WaitCursor;
this.ListView.Items.Clear();
this.ListView.Columns.Clear();
this.ListView.Columns.Add("Id", "Id");
this.ListView.Columns.Add("Name", "Name");
this.ListView.SmallImageList = null;
this.ListView.LargeImageList = null;
this.ListView.StateImageList = null;
this.ListView.BeginUpdate();
watch = Stopwatch.StartNew();
for (int i=0; i < 1000; i++)
{
item = new ListViewItem();
item.ImageIndex = i;
item.Text = i.ToString();
item.SubItems.Add("qwerty");
this.ListView.Items.Add(item);
}
this.ListView.EndUpdate();
this.ListView.SmallImageList = this.ImageList;
this.ListView.LargeImageList = this.ImageList;
this.ListView.StateImageList = this.ImageList;
this.ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
watch.Stop();
this.Text = watch.Elapsed.TotalSeconds.ToString();
this.Cursor = Cursors.Default;
this.Enabled = true;
}
}
}

Add WPF control at runtime

I've written a WPF UserControl, and want to add one or more of it to my Window at runtime when I click a button. How can I do that?
Edit: Further specification
I want to add the usercontrols to a Canvas, and put in a absolute position. The canvas is a drawing of the floors in my house, and each usercontrol has properties to indicate where in the house it is positioned. So I want all the controls to be positioned in the correct position on the canvas.
I'm thinking something like this
var light = new LightUserControl(2);
HouseCanvas.Children.Add(light); // this should be positioned in a specific place
After you add the your control to the Canvas you need to specify the top and left co-ordinates using the Canvas.Top and Canvas.Left attached properties as follows.
var light = new LightUserControl(2);
HouseCanvas.Children.Add(light);
Canvas.SetLeft(light, 20);
Canvas.SetTop(light, 20);
In case you want to add the control to a Grid instead of a Canvas you can specify all the Grid properties through the Grid static class as follows:
Label newLabel = new Label();
newLabel.Content = "The New Element";
Main.Children.Add(newLabel);
Grid.SetColumn(newLabel, 0);
Grid.SetRow(newLabel, 0);
Add a StackPanel to the window and on each button click,
_stackPanel.Children.Add(new YourControl());
You can do this in many ways.
My solution:
for (i = 1; i <= buttoncount; i++)
{
Button mybutton = new Button();
Grid1.Children.Add(mybutton);
mybutton.Height = 100;
mybutton.Width = 100;
mybutton.Name = "button" + i;
mybutton.Content = mybutton.Name;
}
public static void AddChild(this Visual parent, UIElement child)
{
if (InternalAddChild(parent, child))
{
return;
}
throw new NotSupportedException();
}
private static bool InternalAddChild(Visual parent, UIElement child)
{
Panel panel = parent as Panel;
if (panel != null)
{
panel.Children.Add(child);
return true;
}
for (int i = VisualTreeHelper.GetChildrenCount(parent) - 1; i != -1; i--)
{
Visual target = VisualTreeHelper.GetChild(parent, i) as Visual;
if (target != null && InternalAddChild(target, child))
{
return true;
}
}
return false;
}
Just complementing the answer:
for (i = 1; i <= buttoncount; i++)
{
Button mybutton = new Button();
Grid1.Children.Add(mybutton);
mybutton.Height = 100;
mybutton.Width = 100;
mybutton.Name = "button" + i;
mybutton.Content = mybutton.Name;
mybutton.Click += button_Click;
}
private void button_Click(object sender, RoutedEventArgs e)
{
// do something
}

Resources