I have a problem with my scrollable panel in Windows Forms C#.
My Situation
I have a Form with a tabControl.
In 1 of the tabs i have a panel that fills the whole tab and is scrollable.
In runtime i fill this panel with about 60 other panels and 6 pictures in each of the added panels. This leads to 1 big panel that i can scroll down.
My Problem
Now drawing 1 of these sub panels takes about 0.2 seconds on my bad pc which is fine and reaosnable but i have the problem that it is only drawing the things that are currently visible in the main panel.
Even if i let the main panel load for 1 minute he still only draws the first couple of sub panels and if i scroll down he has to draw the rest.
If i scroll up again everything is smoot, so it looks like it is storing everything that was drawn once.
I would like abehaviour where he basically draws the whole main panel in the beginning and afterwards the scrolling is smooth.
PS: I am not sure if "drawing" is the right word for what the form is doing.
PPS: I know that this code is not perfect, but I am not complaining about performance issues but I just want to understand how this loading works
Some code:
private void Form1_Load(object sender, EventArgs e) {
pnEverything.Controls.Clear();
int yPosition = 20;
for (int i = 1; i <= 48; i++) {
Panel panel = new Panel();
Label label = new Label();
if (i % 2 != 0) {
panel.Location = new Point(10, yPosition);
}
else {
panel.Location = new Point(this.Size.Width / 2 + 10, yPosition);
yPosition += this.Size.Width / 10;
}
panel.Size = new Size(this.Size.Width / 2 - 80, ((this.Size.Width / 2 - 80) - 90) / 6 + 40);
panel.Tag = i;
panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
label.AutoSize = true;
label.Font = new Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
label.ForeColor = Color.Black;
label.Name = "lbLade" + i;
label.Location = new Point(panel.Width / 2 - label.Size.Width / 2, panel.Size.Height - 30);
label.Text = i.ToString();
panel.Controls.Add(label);
int xPosition = 30;
for (int j = 1; j <= 6; j++) {
MyPictureBox pb = new MyPictureBox();
pb.Location = new Point(xPosition, 10);
pb.Margin = new Padding(2);
pb.Name = "pbLade" + i + "Nummer" + j;
pb.Size = new Size((panel.Size.Width - 90) / 6, (panel.Size.Width - 90) / 6);
pb.SizeMode = PictureBoxSizeMode.StretchImage;
pb.Cursor = System.Windows.Forms.Cursors.Hand;
pb.Position.Lade = i;
pb.Position.Nummer = j;
xPosition += pb.Size.Width + 10;
pb.ImageLocation = #"Bilder\plus.png";
pb.Click += new EventHandler(pbAddCar_Click);
panel.Controls.Add(pb);
}
pnEverything.Controls.Add(panel);
}
}
After Activate the form, Load one Panel and remaining you can load in the Background worker and Invoke.
Related
I have a WinForm that has a TableLayoutPanel control. My code will detect the number of attached monitors on screen, create a column per monitor, and then add a button for each display within each individual column in the TableLayoutControl so I can ensure that no matter how many monitors are attached, the buttons will appear "centered" across the form. One/two monitors renders just fine, however three monitors results in end columns not being evenly distributed.
Here is my code:
int count = Screen.AllScreens.Count();
this.monitorLayoutPanel.ColumnCount = count;
ColumnStyle cs = new ColumnStyle(SizeType.Percent, 100 / count);
this.monitorLayoutPanel.ColumnStyles.Add(cs);
this.monitorLayoutPanel.AutoSize = true;
var buttonSize = new Size(95, 75);
int z = 0;
foreach (var screen in Screen.AllScreens.OrderBy(i => i.Bounds.X))
{
Button monitor = new Button
{
Name = "Monitor" + screen,
AutoSize = true,
Size = buttonSize,
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Properties.Resources.display_enabled,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font("Segoe UI", 10, FontStyle.Bold),
ForeColor = Color.White,
BackColor = Color.Transparent,
Text = screen.Bounds.Width + "x" + screen.Bounds.Height,
Anchor = System.Windows.Forms.AnchorStyles.None
};
this.monitorLayoutPanel.Controls.Add(monitor, z, 0);
z++;
monitor.MouseClick += new MouseEventHandler(monitor_Click);
}
I've tried making the buttons smaller, and increased the form size but the last column is always smaller than the first two. I can't understand it!
Clear ColumnStyles first.
this.monitorLayoutPanel.ColumnStyles.Clear();
then:
int count = Screen.AllScreens.Count();
for (int i = 0; i < count; i++)
{
ColumnStyle cs = new ColumnStyle(SizeType.Percent, (float)100 / count);
this.monitorLayoutPanel.ColumnStyles.Add(cs);
}
this.monitorLayoutPanel.AutoSize = true;
...
Reza Aghaei pointed me to this thread How to create a magic square using Windows Forms? which pointed me in the right direction. Updated (and working) code below. :)
int screens = Screen.AllScreens.Count();
this.monitorLayoutPanel.ColumnStyles.Clear();
this.monitorLayoutPanel.ColumnCount = screens;
this.monitorLayoutPanel.AutoSize = true;
int z = 0;
foreach (var screen in Screen.AllScreens.OrderBy(i => i.Bounds.X))
{
var percent = 100f / screens;
this.monitorLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, percent));
Button monitor = new Button
{
Name = "Monitor" + screen,
Size = new Size(95, 75),
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Properties.Resources.display_enabled,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font("Segoe UI", 10, FontStyle.Bold),
ForeColor = Color.White,
BackColor = Color.Transparent,
Text = screen.Bounds.Width + "x" + screen.Bounds.Height,
Anchor = System.Windows.Forms.AnchorStyles.None
};
this.monitorLayoutPanel.Controls.Add(monitor, z, 0);
z++;
monitor.MouseClick += new MouseEventHandler(monitor_Click);
I have a panel, inside which I am trying to center the controls. But apparently, panels don't like padding when they are docked to the edge of a control. Here is the current code for my panel:
buttonPanel = new Panel();
buttonPanel.Width = 300;
buttonPanel.Height = 50;
buttonPanel.Dock = DockStyle.Bottom;
buttonPanel.Padding = new Padding((this.ClientSize.Width - buttonPanel.Width) / 2, 0,
(this.ClientSize.Width - buttonPanel.Width) / 2, 0);
buttonPanel.BackColor = Color.Transparent;
this.Controls.Add(buttonPanel);
I have a single button placed inside the panel. What I would expect with the above code, is that the control is placed nicely to the left of a 300 wide "rectangle" centered in the panel. But the button is placed on the very left, as if the padding is ignored:
How can I center a collection of buttons to the center of my form?
Design Time Approach
At design time, put your button in your container and select your button. Then, use Center Horizontally and Center Vertically from Layout toolbar to put your button in center of panel. After, go to your buttons properties and remove every anchor from Anchor property.
Coding Approach
Panel panel = new Panel();
panel.Size = new Size(300, 100);
panel.Dock = DockStyle.Bottom;
Button button = new Button();
button.Size = new Size(70, 25);
button.Location = new Point((panel.Width - button.Width) / 2, (panel.Height - button.Height) / 2);
button.Anchor = AnchorStyles.None;
panel.Controls.Add(button);
this.Controls.Add(panel);
You have to align the button inside the panel by setting the button's position not the panel's padding:
button1.Left = (int)(panel1.Width * 0.5f - button1.Width * 0.5f);
button1.Top = (int)(panel1.Height * 0.5f - button1.Height * 0.5f);
or
button1.Location = new Point()
{
X = panel1.Width / 2 - button1.Width / 2,
Y = panel1.Height / 2 - button1.Height / 2
};
the result is the same.
If you override the OnResize method the button will stay centered also when you change the size of the form:
protected override void OnResize(EventArgs e)
{
button1.Location = new Point()
{
X = panel1.Width / 2 - button1.Width / 2,
Y = panel1.Height / 2 - button1.Height / 2
};
base.OnResize(e);
}
I'm using vs2012 (C#) for my app and I'm looking for a way to add a label and textbox to a tabPage on my form, dynamically. But because the # of controls to be added could be larger than 10, I am also trying to add them in "columns" so the container control will only scroll horizontally, not vertically.
For instance, I'm trying to do something like this:
LabelControl LabelControl LabelControl LabelControl
TextboxControl TextboxControl TextboxControl TextboxControl
LabelControl LabelControl LabelControl LabelControl
TextboxControl TextboxControl TextboxControl TextboxControl
etc.
The "container" control is a TabPage, so I know I have to grab the height from that and use it. I am able to get the textboxes to render but am having difficulty with the label controls being on top, then the textboxes below.
Here's what I've got so far:
int height = tabPageBicycle.Height;
Point startLocation = new Point(0, 0);
int previousX = 0;
int previousY = 0;
int currentX = 0;
for (int x = 0; x < 75; x++)
{
Label label = new Label();
TextEdit text = new TextEdit();
label.Text = x.ToString();
text.Text = x.ToString();
label.Location = new Point(currentX, previousY);
tabPageBicycle.Controls.Add(label);
if ((height - previousY) < text.Height)
{
currentX += 100;
previousY = 0;
}
text.Location = new Point(currentX + text.Height + 5, previousY + 50);
previousX = text.Location.X;
previousY = text.Location.Y;
tabPageBicycle.Controls.Add(text);
}
Any clues as to what I am doing wrong?
Ended up figuring it out, after taking it line-by-line and seeing what was being done in the loop. Here's the final code I used....as always, I'm open to suggestions/comments/etc. on how to make it better/more efficient.
int labelY = 0;
int textY = 0;
int startX = 5;
int startY = 5;
int height = tabPageBicycle.Height;
Point startLocation = new Point(0, 0);
for (int x = 0; x < 75; x++)
{
Label label = new Label();
TextEdit text = new TextEdit();
label.AutoSize = true;
label.Text = x.ToString();
text.Text = x.ToString();
//Determine if the next set of controls will be past the height of the container.
//If so, start on the next column (change X).
if ((height - textY) < ((label.Height + 10) + text.Height))
{
startX += 125;
labelY = 0;
}
//Start of new column.
if (labelY == 0)
label.Location = new Point(startX, startY);
else
label.Location = new Point(startX, textY + label.Height + 10);
tabPageBicycle.Controls.Add(label);
labelY = label.Location.Y;
textY = labelY + 15;
text.Location = new Point(startX, textY);
textY = text.Location.Y;
tabPageBicycle.Controls.Add(text);
}
and the results:
I hope it helps someone else out!
I got a windows form which has more than one page of mainly labels and textboxes, I'm trying to keep the font that i have in the winform already, so far I'm able to print the first page, but when i try to add the rest of the controls it does all sort of weird stuff this is the part of my code where i'm putting everything to print but not all the controls in the panel show in the print preview. So i found out that the controls in the panel are not in order and what i need to do is create the number of printing pages first then put the controls in those printing pages. any help on trying to create the print pages first to add the controls to it. it will always be 4 print pages.
int mainCount = 0;
public void printStuff(System.Drawing.Printing.PrintPageEventArgs e)
{
Font printFont = new Font("Arial", 9);
int dgX = dataGridView1.Left;
int dgY = dataGridView1.Top += 22;
double linesPerPage = 0;
float yPos = 0;
int count = 0;
float leftMargin = e.MarginBounds.Left;
float topMargin = e.MarginBounds.Top;
float bottomMargin = e.MarginBounds.Bottom;
StringFormat str = new StringFormat();
linesPerPage = e.MarginBounds.Height / printFont.GetHeight(e.Graphics);
Control ctrl;
while ((count < linesPerPage) && (panel1.Controls.Count != mainCount))
{
ctrl = panel1.Controls[mainCount];
yPos = topMargin + (count * printFont.GetHeight(e.Graphics));
mainCount++;
count++;
if (ctrl is Label)
{
e.Graphics.DrawString(ctrl.Text, printFont, Brushes.Black, ctrl.Left + 5, ctrl.Top + 40);
}
else if (ctrl is TextBox)
{
e.Graphics.DrawString(ctrl.Text, printFont, Brushes.Black, ctrl.Left + 5, ctrl.Top + 40);
e.Graphics.DrawRectangle(Pens.Black, ctrl.Left, ctrl.Top + 40, ctrl.Width, ctrl.Height);
}
}
if (count > linesPerPage)
{
e.HasMorePages = true;
}
else
{
e.HasMorePages = false;
}
}
//Print
private void exportFileToolStripMenuItem_Click(object sender, EventArgs e)
{
printPreviewDialog1.Document = printDocument1;
printPreviewDialog1.ShowDialog();
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
printStuff(e);
}
It seems to me that the problem is that on subsequent pages you are not subtracting the "page offset" form the control Top positions when printing. You are essentially trying to use the screen coordinates of the controls when placing them on the printed page which obviously only works correctly for the first page. On subsequent pages you need to map the screen coordinates by subtracting a quantity which is the equivalent of the "total-printed-surface-so-far"..
You will want to modify this line for instance:
e.Graphics.DrawString(ctrl.Text, printFont, Brushes.Black, ctrl.Left + 5, ctrl.Top + 40);
to something like this:
e.Graphics.DrawString(ctrl.Text, printFont, Brushes.Black, ctrl.Left + 5, ctrl.Top + 40 - pageOffset);
where the pageOffset is a variable that should be computed for each page, based on the Height of the printable area: pageOffset = currentPageNumber * heightOfPrintableArea so you will also need to maintain a variable for the number of pages printed, similar to the mainCount
Of course, the same would apply to the other branch of your if statement:
e.Graphics.DrawString(ctrl.Text, printFont, Brushes.Black, ctrl.Left + 5, ctrl.Top + 40 - pageOffset);
e.Graphics.DrawRectangle(Pens.Black, ctrl.Left, ctrl.Top + 40 - pageOffset, ctrl.Width, ctrl.Height);
Hey frenz, In my project I merged rows with cell painting. It works fine. but when i use scroll bar it gives random output. i.e. Data on that merged cell goes to header of the datagrid view. so any solution . The code is as follows:
private void Daywisegrid_Paint(object sender, PaintEventArgs e)
{
for (int k = 0; k < BranchIndex.Count; k++)
{
Font fnt = new Font("Arial", 10, FontStyle.Bold, GraphicsUnit.Point);
Rectangle rct1 = new Rectangle((Daywisegrid.GetColumnDisplayRectangle(0, true).X),
(Daywisegrid.GetColumnDisplayRectangle(0, true).Y),
Daywisegrid.GetColumnDisplayRectangle(0, true).Width - 1,
(Daywisegrid.GetRowDisplayRectangle((Daywisegrid.Rows.Count - 1), true).Top -
Daywisegrid.GetRowDisplayRectangle((Daywisegrid.Rows.Count - 1), true).Height));
Rectangle rct = Daywisegrid.GetRowDisplayRectangle(Convert.ToInt32(BranchIndex[k]), true);
rct.Height -= 1;
SizeF s = e.Graphics.MeasureString("Branch", Daywisegrid.Font);
float lefts = (rct.Width / 2) - (s.Width / 2);
float tops = rct.Top+((rct.Height/2)-(s.Height / 2));
e.Graphics.FillRectangle(Brushes.White, rct);
e.Graphics.DrawString(BranchName[k].ToString(), fnt, Brushes.Black,0, tops);
}
}