I want to create a form with columns of buttons. The buttons should fit all the width of form. Also I want to push it to top of form as possible. It should look like this:
|----------------------------------|
| Form caption |
|----------------------------------|
||--------------------------------||
||Button0 ||
||--------------------------------||
||--------------------------------||
||Button1 ||
||--------------------------------||
||--------------------------------||
||Button2 ||
||--------------------------------||
| |
| |
| free space |
| |
|----------------------------------|
Usually I work with C++/Qt and it's rich of layouts. As I understand c# is not so good in that. I've found that TableLayoutPanel with 1 column can do that. The only thing I want is to push all the buttons to top.
So I've created the following code:
// panelButton was created by VS with following params:
this.panelButton = new System.Windows.Forms.TableLayoutPanel();
this.panelButton.Dock = System.Windows.Forms.DockStyle.Fill;
this.panelButton.Name = "panelButton";
this.panelButton.RowCount = 1;
for(int i = 0;i < 3;i ++)
{
Button button = new Button();
button.Dock = DockStyle.Fill;
button.Height = 40;
button.Text = "Button" + i;
button.Click += new EventHandler(delegate(object o, EventArgs args) {});
panelButton.Controls.Add(button, 0, i);
}
But the layout I get is wrong - button0 and button1 are 40px height as expected but button2 fills all the space when I expect it will be 40px.
ADDED: I've found a workaround. I add
panelButton.Controls.Add(new Control(), 0, rowIndex);
after the loop and so thet works as expected.
You don't need to use TableLayoutPanel for such task. It's enough to use a Panel and add buttons to it. For each button you need to set its Dock to Top.
If you want the panel grows instead of showing scrolls, you can set the panel AutoSize=true and AutoScroll=false.
If you want scrollbars, just set it's AutoSize=false and AutoScroll=true.
Example 1
An auto sized form containing a panel which has a list of buttons:(Screenshot)
public Form1()
{
InitializeComponent();
this.Controls.Clear();
this.AutoSize = true;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
var panel = new Panel();
panel.Dock = DockStyle.Fill;
panel.AutoScroll = false;
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.Controls.Add(panel);
for (int i = 0; i < 20; i++)
{
var buttun = new Button();
buttun.Text = string.Format("Button {0}", i + 1);
buttun.Dock = DockStyle.Top;
panel.Controls.Add(buttun);
}
}
Example 2
A form containing an auto-scroll panel which has a list of buttons:(Screenshot)
public Form1()
{
InitializeComponent();
this.Controls.Clear();
this.AutoSize = false;
var panel = new Panel();
panel.Dock = DockStyle.Fill;
panel.AutoScroll = true;
this.Controls.Add(panel);
for (int i = 0; i < 20; i++)
{
var buttun = new Button();
buttun.Text = string.Format("Button {0}", i + 1);
buttun.Dock = DockStyle.Top;
panel.Controls.Add(buttun);
}
}
Note: Layout is not related to C# it's the job of UI frameworks like Windows Forms. To learn more about layout in windows forms take a look at these documents:
Windows Forms Layout
Layout in Windows Forms Controls
Related
I am working on a homework c# winforms project and would like to add date and time in top right corner of my main form in a way that in first row I have a date written in one label, and on second row I have time written in second label.
Also I need that those stick in the top right corner if form is resized.
I don't know if it matters, but those label controls are inside panel which is top docked in form, and this panel already contains two controls that are docked left.
example of what I want
I've been playing with anchor and dock properties but I can't get it to work in a way I want.
private void GlavnaForma_Load(object sender, EventArgs e)
{
timerDateTime.Start();
lblDate.Text = DateTime.Now.ToString("dddd, dd.M.yyyy", new CultureInfo("hr-HR"));
lblTime.Text = DateTime.Now.ToString("HH:mm:ss", new CultureInfo("hr-HR"));
}
private void timerDateTime_Tick(object sender, EventArgs e)
{
lblDate.Text = DateTime.Now.ToString("dddd, dd.M.yyyy", new CultureInfo("hr-HR"));
lblTime.Text = DateTime.Now.ToString("HH:mm:ss", new CultureInfo("hr-HR"));
}
Set the anchor to Top, Right like so:
There are several ways to do this.
I would probably make the main form have a table layout panel with one column and three rows. Make the top two rows be absolutely sized and the third row have size type "percent" with a value of 100.0% to take up all remaining room. Then put a label each in the top two rows and justify the labels to the right via setting their "Dock" property to "Right".
All of this can be done in the form designer GUI. The generated code looks like the following:
this.tableLayout = new System.Windows.Forms.TableLayoutPanel();
this.labelDate = new System.Windows.Forms.Label();
this.labelTime = new System.Windows.Forms.Label();
this.tableLayout.SuspendLayout();
this.SuspendLayout();
//
// tableLayout
//
this.tableLayout.ColumnCount = 1;
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Controls.Add(this.labelDate, 0, 0);
this.tableLayout.Controls.Add(this.labelTime, 0, 1);
this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayout.Location = new System.Drawing.Point(0, 0);
this.tableLayout.Name = "tableLayout";
this.tableLayout.RowCount = 3;
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Size = new System.Drawing.Size(800, 450);
this.tableLayout.TabIndex = 0;
//
// labelDate
//
this.labelDate.AutoSize = true;
this.labelDate.Dock = System.Windows.Forms.DockStyle.Right;
this.labelDate.Location = new System.Drawing.Point(742, 0);
this.labelDate.Name = "labelDate";
this.labelDate.Size = new System.Drawing.Size(55, 24);
this.labelDate.TabIndex = 0;
this.labelDate.Text = "26-8-2019";
//
// labelTime
//
this.labelTime.AutoSize = true;
this.labelTime.Dock = System.Windows.Forms.DockStyle.Right;
this.labelTime.Location = new System.Drawing.Point(748, 24);
this.labelTime.Name = "labelTime";
this.labelTime.Size = new System.Drawing.Size(49, 24);
this.labelTime.TabIndex = 1;
this.labelTime.Text = "19:59:58";
Add whatever further content you want to the third row. Maybe add a panel to that row docked to "Fill"
I have set this flowLayoutPanel, the controls inside arrange well, till the last arrives to the bottom border of the panel, then the controls start arranging on the right side (forming another column) keepping the vertical flow. I just want one column.
this.panel.Anchor =
((System.Windows.Forms.AnchorStyles)
(((System.Windows.Forms.AnchorStyles.Top |
System.Windows.Forms.AnchorStyles.Bottom)| System.Windows.Forms.AnchorStyles.Right)));
this.panel.AutoScroll = true;
this.panel.BorderStyle = BorderStyle.None;
this.panel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.panel.Location = new System.Drawing.Point(0, 184);
this.panel.Name = "myPanel";
this.panel.RightToLeft = System.Windows.Forms.RightToLeft.No;
this.panel.Size = new System.Drawing.Size(300, 371);
this.panel.TabIndex = 9;
Use
this.panel.FlowDirection = System.Windows.Forms.FlowDirection.LeftToRight;
instead of
this.panel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
if you want only one column than please add below code to your application just after control added to your flowlayoutpanel
this.panel.SetFlowBreak(<<YOUR_ADDED_CONTROL_NAME>>, true);
Example
Button btn1 = new Button();
btn1.Text = "TEST";
btn1.Height = 30;
btn1.Width = 100;
this.panel.Controls.Add(btn1);
this.panel.SetFlowBreak(btn1, true);
I'm supposed to create a magic square in 2D using Windows Forms Application. It should look like this:
However, the user should be able to decide the size of the square (3x3, 5x5, 7x7, etc). I already wrote the code in a Console Application, but I don't know how to add the 2D graphics.
Somebody already asked this question (How do I put my result into a GUI?), and one of the answers was to use DataGridView, but I'm not sure if that's what I'm looking for, since I can't make it look like the picture.
Any ideas or advice?
You can use a TableLayoutPanel and add buttons to panel dynamically.
If you don't need interaction with buttons, you can add Label instead.
Create square dynamically:
public void CreateSquare(int size)
{
//Remove previously created controls and free resources
foreach (Control item in this.Controls)
{
this.Controls.Remove(item);
item.Dispose();
}
//Create TableLayoutPanel
var panel = new TableLayoutPanel();
panel.RowCount = size;
panel.ColumnCount = size;
panel.BackColor = Color.Black;
//Set the equal size for columns and rows
for (int i = 0; i < size; i++)
{
var percent = 100f / (float)size;
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, percent));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, percent));
}
//Add buttons, if you have your desired output in an array
//you can set the text of buttons from your array
for (var i = 0; i < size; i++)
{
for (var j = 0; j < size; j++)
{
var button = new Button();
button.BackColor = Color.Lime;
button.Font = new Font(button.Font.FontFamily, 20, FontStyle.Bold);
button.FlatStyle = FlatStyle.Flat;
//you can set the text of buttons from your array
//For example button.Text = array[i,j].ToString();
button.Text = string.Format("{0}", (i) * size + j + 1);
button.Name = string.Format("Button{0}", button.Text);
button.Dock = DockStyle.Fill;
//If you need interaction with buttons
button.Click += b_Click;
panel.Controls.Add(button, j, i);
}
}
panel.Dock = DockStyle.Fill;
this.Controls.Add(panel);
}
If you need interaction with buttons
void button_Click(object sender, EventArgs e)
{
var button = (Button)sender;
//Instead put your logic here
MessageBox.Show(string.Format("You clicked {0}", button.Text));
}
As an example, you can call
CreateSquare(3);
Screenshot:
You can create a Form and add a TableLayoutPanel with this property
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.BackColor = Color.Gold;
and this is the result
When you create Row and Column, to fit correctly set the percentage in this way:
After this you can add a Button or Label in each square.
I am working on a Winform app using .net 4.5.
In my project, I have a window that contains only a button.
WHen the window is displayed, I add lots of controls dynamically to the point that a scrollbar is needed.
Once the controls are added, I move the single button to the bottom of the controls in the window.
This step of moving the button to the bottom of the controls makes the window scroll to the bottom. I have tested this by not moving the button to the bottom of the form, and the scroll stays at the top.
I have tried "this.VerticalScroll.Value = 0;" both before and after setting the position of the button.
Here is the code so you can get a clearer idea of what I am attempting to do:
public SignoffSurvey(int task_id)
{
InitializeComponent();
this.task_id = task_id;
int form_top = 10;
int question_num = 0;
XmlDocument doc = new XmlDocument();
doc.Load(DBHandler.getSetting("files_directory") + "\\" + "questionaire.xml");
foreach (XmlNode node in doc.SelectNodes("/Questions/Question"))
{
question_num++;
string type = node.Attributes["type"].Value;
int top = 0;
Panel pnl = new Panel();
pnl.AutoSize = true;
pnl.Top = form_top;
pnl.Left = 10;
Label text_lbl = new Label();
text_lbl.Top = top;
text_lbl.AutoSize = true;
text_lbl.Text = node["text"].InnerText;
pnl.Controls.Add(text_lbl);
top += text_lbl.Height + 5;
if (type == "mc" || type == "mct")
{
XmlNode choices = node["choices"];
Boolean fc = false;
foreach (XmlNode choice in choices.ChildNodes)
{
RadioButton rb = new RadioButton();
rb.AutoSize = true;
rb.Text = choice.InnerText;
rb.Top = top;
rb.Left = 10;
top += rb.Height + 5;
pnl.Controls.Add(rb);
if (!fc) // check first item.
{
fc = true;
rb.Checked = true;
}
}
}
if (type == "mct" || type == "txt")
{
TextBox tb = new TextBox();
tb.Multiline = true;
tb.Width = 500;
tb.Height = 250;
tb.Top = top;
tb.Left = 10;
pnl.Controls.Add(tb);
top += tb.Height + 5;
}
pnl.Height = top;
this.Controls.Add(pnl);
form_top += pnl.Height + 10;
}
this.VerticalScroll.Value = 0;
this.save_btn.Top = form_top;
}
HOw can I force the window vertical scroll to the top regardless of where this 1 button is moved to?
Okie. For anyone with a similar issue, here is the reason for the problem and how to overcome it.
The problem appears to be that the single button is 'selected' by default on the window. So when the window launches, it scrolls to the position of the button.
I added this:
this.Controls[1].Select();
after adding all my controls.
You have to use [1] because the button already on the form is [0].
I have a split container on panel 1 I have added a groupbox, in that groupbox is a flowcontrol which has dynamic number of textboxes, i have set both the groupbox and flowcontrol to dockstyle to fill.
In code i have also set the textboxes to dock style to fill, but they wont resize when i move the splitter, while the parent flowcontrol does resize.
Label labelInput = new Label();
TextBox listBoxNewInput = new TextBox();
listBoxNewInput.Name = ce.ToString();
labelInput.AutoSize = true;
labelInput.Font = new Font(labelInput.Font, FontStyle.Bold);
listBoxNewInput.Multiline = true;
// Add vertical scroll bars to the TextBox control.
listBoxNewInput.ScrollBars = ScrollBars.Vertical;
// Allow the RETURN key in the TextBox control.
listBoxNewInput.AcceptsReturn = true;
// Allow the TAB key to be entered in the TextBox control.
listBoxNewInput.AcceptsTab = true;
// Set WordWrap to true to allow text to wrap to the next line.
listBoxNewInput.WordWrap = true;
listBoxNewInput.Text = ts.ToString();
//listBoxNewInput.Width = 150;
listBoxNewInput.MinimumSize = new Size(200,150);
listBoxNewInput.MaximumSize = new Size(1000, 150);
listBoxNewInput.Dock = DockStyle.Fill;
listBoxNewInput.TextChanged += new EventHandler(listBoxNewInput_TextChanged);
//Add the newly created text box to the list of input text boxes
inputTextBoxesList.Add(listBoxNewInput);
//Add the labels and text box to the form
flowLayoutPanel1.Controls.Add(labelInput);
flowLayoutPanel1.Controls.Add(listBoxNewInput);
if i try to put controls directly in to spliter panel 1 only the first two controls appear, which do resize when i move the splitter
splitContainer1.Panel1.Controls.Add(labelInput); splitContainer1.Panel1.Controls.Add(listBoxNewInput);
->if the controls when i put them in flow control resize, when i move the splitter that would be good
OR
->All controls appear when i put them directly into the splitter panel 1
Based on your comments and what I think you are trying to accomplish, I think you need to replace the FlowLayoutPanel with a TableLayoutPanel because it sounds like you are just stacking one TextBox below another.
Create a TableLayoutPanel with 1 column and 1 row.
Here is a working example:
tableLayoutPanel1.AutoScroll = true;
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
tableLayoutPanel1.RowStyles.Clear();
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 150));
for (int i = 0; i < 4; i++) {
AddTextBox("TextBox #" + i.ToString());
}
private void AddTextBox(string info) {
TextBox tx = new TextBox();
tx.Multiline = true;
tx.Text = info;
tx.ScrollBars = ScrollBars.Vertical;
tx.WordWrap = true;
tx.Height = 150;
tx.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
tableLayoutPanel1.Controls.Add(tx);
}
Instead of docking, I set the height of the TextBox and then I set the Anchors so that when the SplitPanel resizes, the TextBoxes resize appropriately.