This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I'm trying to construct a form layout, which will meet following requirements:
panel1 is fixed height - it will contain only a label which will be some kind of header with title, etc
panel2 will contain datagridview with dockstyle fill, here user will find objects which he can select to show their properties
panel3 is fixed height - it will be positioned at the bottom of the form and will contain properties of selected object from the datagridview
My problem is to make panel2 to to fill the whole place which left after panel1 and panel3. So if panel1 and panel3 has both fixed height 100, and the form has height 500, then panel2 should have 300 height, if form will be resized to 600, then panel2 should also resize to 400, etc.
I was trying some dock combinations, trying to set panel1 dock as top, panel3 as bottom and panel2 as fill, but it gives other results than expected. I know I can handle the form resize event and resize panel2 to a size which will left, but I'm wondering if there's some more elastic and clean way to do this.
The docking space is related to the order of objects on your form as im_a_noob has mentioned. You can change object z-order to change how they dock. You should be able to right-click the panel that needs to fill the space in the middle and then select "Bring to Front" from the menu. That should make it fill the space correctly and the whole form behind the other panels.
So you would dock your top panel to the top, bottom panel to the bottom, and then the center one to "Fill". Then right-click and bring the center one to the front.
this is because of the Document Outline.
go to
View -> Other Windows -> Document Outline (or ctrl + w, u)
make sure your dock fill pannel (middle one) is the first of the 3 pannel in that list. This should fix you.
It seems like a tableLayoutPanel would be a good choice here. That way you have the ability to set absolute values for row 1 (panel 1) and row 3 (panel 3) and then use 100% for the middle row (panel 2) guaranteeing that it will take up the remaining space without overlapping the other panels.
Then once you put your datagridview into the middle row of the tableLayoutPanel you should be able to set the Dock property to fill and it should work correctly.
TableLayoutPanels only allow for one element to be placed in a cell, however you can get around this by adding a panel as the main element and then configure everything in that panel.
Just set the panel1 anchors as top, left, right; panel 2 anchors to be top, bottom, left, right; panel 3 anchors bottom, left, right. This will ensure that the top panel stays in place, bottom moves down with the window and middle will expand in between.
First, Dock Panels 1 and 3 (Panel 1 to Top, Panel 3 to Bottom):
Once those two are set, select Panel 2 and set it to Fill:
UPDATE:
Here is a quick piece of code I did to verify your panels are in the right order in your code:
public partial class PanelForm : Form {
public PanelForm() {
InitializeComponent();
int iHead = Controls.GetChildIndex(panelHead);
int iData = Controls.GetChildIndex(panelData);
int iFoot = Controls.GetChildIndex(panelFoot);
if ((iHead < iData) || (iFoot < iData)) {
panelHead.Dock = DockStyle.None;
panelData.Dock = DockStyle.None;
panelFoot.Dock = DockStyle.None;
Controls.SetChildIndex(panelData, 0);
Controls.SetChildIndex(panelHead, 1);
Controls.SetChildIndex(panelFoot, 2);
panelData.Dock = DockStyle.Fill;
panelHead.Dock = DockStyle.Top;
panelFoot.Dock = DockStyle.Bottom;
}
ShowData(DateTime.Now);
}
private void ShowData(DateTime now) {
var table = new DataTable();
var c1 = table.Columns.Add("Name", typeof(string));
var c2 = table.Columns.Add("Even", typeof(bool));
var c3 = table.Columns.Add("Index", typeof(int));
var c4 = table.Columns.Add("Times 2", typeof(int));
var c5 = table.Columns.Add("Inverse", typeof(double));
var c6 = table.Columns.Add("Timespan", typeof(TimeSpan));
var c7 = table.Columns.Add("Binary Time", typeof(long));
var c8 = table.Columns.Add("Display", typeof(string));
for (int i = 0; i < 1000; i++) {
DataRow r = table.NewRow();
r[c1] = string.Format("Row {0}", i);
r[c2] = (i % 2 == 0);
r[c3] = i;
r[c4] = 2 * i;
r[c5] = (0 < i) ? 1 / (double)i : double.NaN;
r[c6] = DateTime.Now - now;
r[c7] = DateTime.Now.ToBinary();
r[c8] = string.Format("{0:g}", DateTime.Now);
table.Rows.Add(r);
}
dataGridView1.DataSource = table;
}
}
Related
There's a StackPanel
<StackPanel x:Name="sp1" Orientation="Horizontal" BorderBrush="Black" BorderThickness="1" />
which is dynamically populated with rectangles:
var values = new[] { 30, 50, 20 };
foreach (int val in values)
{
sp1.Children.Add(new Rectangle
{
// Width = perecntage, not supported on Rectangle
VerticalAlignment = VerticalAlignment.Stretch,
Fill = new SolidColorBrush(colors.Pop()),
});
}
Width has to be a proportion of the parent's width based on val.
I've tried to set it via the SizeChanged event, but this works only for enlarging. Shrinking the window doesn't bring the width back.
sp1.SizeChanged += (sender, args) =>
{
int i = 0;
foreach (var val in values)
{
var prop = (double)val / valuesSum;
var r = (Rectangle)sp1.Children[i++];
r.Width = (int)(prop * args.NewSize.Width);
}
}
The only objective is to have a bar that is proportionally divided and stretched. Any idea?
It isn't entirely clear why you want to use a StackPanel for this. A StackPanel grows as it's contents get larger, the individual controls are 'stacked' to fit into the space available to the StackPanel. That doesn't match your requirements for "a bar that is proportionally divided and stretched".
If you use something like a Grid, you can define the columns and rows to be proportionally spaced. For example, a Grid with two columns whose widths are defined as 2* and 3* those columns will be two-fifths of the width of the Grid, and three-fifths (fifths because 2+3=5). Needless to say, you can have as many columns as you like, and define the relative sizing as you require, possibly using 30*,50*,20* from information in the question. Or if you take the stars away, the values become literal values rather than relative values.
If you just want something to evenly space things out, try a UniformGrid. You can limit the number of columns or rows.
If you have some complex need which is not covered by anything else, you can write a custom container control.
This can be achieved by calling child.Arrange(), which lets you set X.
The child has to have Width unset, otherwise it takes precedence.
sp1.SizeChanged += (sender, e) =>
{
var childRect = new Rect(0, 0, 0, e.NewSize.Height);
for (int i = 0; i < values.Length; i++)
{
var childWidth = (double)values[i] / valuesSum * e.NewSize.Width;
childRect.Width = childWidth;
so1.Children[i].Arrange(childRect);
childRect.X += childWidth;
}
}
I tried RelativePanel and StackPanel and both worked the same.
This can also be done in a class derived from Panel. The code is the same, but it's called from overridden ArrangeOverride().
I have a tablelayout panel and it has 3 rows first row is 70 percent and remaining 2 is 15 percent each.
in the first row i put a picture box and i dock it top but i want it's height to be the height of the row i put in.
I am trying it like this but it takes the height in int. And instead of 70% it accepts 70.
So how can i set it's height in percent.
public CtrlBasketItems()
{
InitializeComponent();
pictureBox1.Height = (int)(tableLayoutPanel1.RowStyles[0].Height);
}
pictureBox1.Height = (int)(tableLayoutPanel1.Height * 0.7)
Then you would have to position it in the row as needed, of course
Set your PictureBox to be Dock = Fill instead: your picture box will fill the cell it occupies (i.e. be the same height as the cell/row).
I need to set the width and length of my components(e.g. DGV(DataGridView)) after re-sizing the form (e.g. maximizing) by percentage. For example the width of my DGV should become 100% and the height of it should become 45%.
I read about Dock and Anchor but I could not implement my idea by these options.
Is there a appropriate method for this problem?
You could use a TableLayoutPanel handle your layouting.
For your example, create a TableLayoutPanel with a single column and two rows, set the row size to 55% and 45%, and add the DataGridView to the second column.
Example:
var form = new Form();
var dgv = new DataGridView { Dock = DockStyle.Fill };
var table = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 1,
RowCount = 2
};
table.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
table.RowStyles.Add(new RowStyle(SizeType.Percent, 55F));
table.RowStyles.Add(new RowStyle(SizeType.Percent, 45F));
table.Controls.Add(dgv, 0, 1);
form.Controls.Add(table);
form.ShowDialog();
I am dynamically creating a TableLayoutPanel, and then dynamically creating Labels and TextBoxes to put inside it.
It would seem logical that I could just assign the number of columns and rows to the TableLayoutPanel:
tableLayoutPanelGreatGooglyMoogly = new TableLayoutPanel();
tableLayoutPanelGreatGooglyMoogly.RowCount = NUMBER_OF_ROWS;
tableLayoutPanelGreatGooglyMoogly.ColumnCount = NUMBER_OF_COLUMNS;
...create the controls to place inside it:
Label lbl = new Label();
lbl.Parent = tableLayoutPanelGreatGooglyMoogly;
. . .
...and then put the created control[s] in the specified "cell" (column and row):
tableLayoutPanelGreatGooglyMoogly.SetColumn(lbl, ACol); // "ACol" is the current column
tableLayoutPanelGreatGooglyMoogly.SetRow(lbl, i); // "i" is the current row
...but that's not working - neither if I specify the width and height values for the dynamically created child controls or if I don't (in which case they are too large - specifically, their width is too great).
UPDATE
I added this code, and it makes no difference:
// ROWS
tableLayoutPanelGreatGooglyMoogly.RowCount = NUMBER_OF_ROWS;
TableLayoutRowStyleCollection rowStyles =
this.tableLayoutPanelGreatGooglyMoogly.RowStyles;
foreach (RowStyle rStyle in rowStyles) {
rStyle.SizeType = SizeType.Percent;
rStyle.Height = 8;
}
// COLUMNS
tableLayoutPanelGreatGooglyMoogly.ColumnCount = TOTAL_NUMBER_OF_COLUMNS;
TableLayoutColumnStyleCollection columnStyles =
this.tableLayoutPanelGreatGooglyMoogly.ColumnStyles;
foreach (ColumnStyle cStyle in columnStyles) {
cStyle.SizeType = SizeType.Percent;
cStyle.Width = 12;
}
UPDATE to the UPDATE
I see that at design-time, a Label or TextBox (presumably, any control) has a Cell[col,row] property. I would like to access that dynamically, if it is not readonly, so that I could set:
lbl.Cell = i,i
txtbox.Cell = i+1,i
Is that possible to do in code? The "Cell" property does not seem to be recognized (understandably, I guess) at that time.
UPDATE to the UPDATE REVISITED
I added this line of code:
tableLayoutPanelGreatGooglyMoogly.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single;
...and now I see that the labels and textBoxes actually are in the cells (columns and rows) I'm expecting them to inhabit.
However, I need to get the labels to move down from the upper left corner of their cells to the center of the cell (both vertically and horizontally).
Even at design time (with a "test"/temporary TableLayoutPanel on the form), added Labels do not respond to changes to the TextAlign property when they are sitting inside a TableLayoutPanel cell - no matter what I set TextAlign to ("Middle Center" seems the most sensible), they stubbornly remain affixed to the top left of the cell.
Similary, changing the Labels' Location.X and Location.Y at design time does nothing. The Labels stick to the NW corner of the cell like a barnacle to a long-lost anchor.
An important part of using the GridLayoutPanel that is rarely mentioned is the use of the Anchor property in child controls.
The Anchor property determines which edge of the cell each child control will extend to.
When you create your labels you do it like this:
Label lbl = new Label();
lbl.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
lbl.Parent = tableLayoutPanelGreatGooglyMoogly;
It should stretch the labels to the full size of the cell.
See http://msdn.microsoft.com/en-us/library/w4yc3e8c(v=vs.80).aspx in the "Positioning Controls Within Cells Using Docking and Anchoring" secton.
I have a tablelayoutpanel. 2x2 - 2 columns 2 rows.
For example, I added a button button1 in a 1 row, second column. button1 has a dock property set to Fill. VS Designer allows to set column/row span properties of button1.
I want an availability to change row span property of button1 programatically, so it can fill all second column(1 row and second row) and availability to set it back.
How?
What about this code?
private void button1_Click(object sender, EventArgs e)
{
var control = sender as Control;
if(control == null)
return;
if (1 == tableLayoutPanel1.GetRowSpan(control))
{
tableLayoutPanel1.SetRowSpan(control, 2);
}
else
{
tableLayoutPanel1.SetRowSpan(control, 1);
}
}
While I find the current up-voted answer quite adequate, it also appears slightly messier than need be. You must add the controls to the tableLayoutPanel before setting their properties.
Visual Studio (2013 and likely other versions) will show these properties as part of the control. When in reality, they are part of the tableLayoutPanel.
Explanation:
tableLayoutPanel.Controls.Add(**control**, x, y)
tableLayoutPanel.SetColumnSpan(**control**, '# of cols to span')
Example:
tableLayoutPanel1.Controls.Add(**button1**, 0, 0);
tableLayoutPanel1.SetColumnSpan(**button1**, 2);
tableLayoutPanel1.SetRowSpan(**button1**, 3);
Result: A button which 'occupies' this space. (Provided it is large enough to cover the area. Even if it does not 'cover' the space, it will still 'reserve' it.)
O O X X X
O O X X X
O O X X X
X X X X X
X X X X X
Setting the span larger than the size of the grid will.. :
NOT change the grid size.
NOT crop/edit the number to the size of the grid.
NOT throw an error at compile.
It WILL act/perform as if the span was set to the current grid (tableLayoutPanel) maximum size. This is only relevant if the TLP/grid size changes.
If you add two controls two the same grid location programmatically, the first control in a grid keeps its location. Any subsequently added control gets pushed to the next cell block. If a 'span' is added, it will treat that cell block as used and continue searching for an unused cell block.
Ex: label1, label2 and label3 are added to 0,0.
label1 will appear in 0,0
label2: 0,1
label3: 0,2
Ex 2: label 1 has a row span of 2.
label1: 0,0
label2: relocated to 0,2
label3: 0,3
After you have selected the correct grid point and spans, you can then further optimize your layout using the dock and anchor properties.