Set dimensions of component by percentage - c#

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();

Related

Stretch children proportionally inside StackPanel

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().

three panels layout on form [closed]

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;
}
}

Fix row height of every row in TableLayoutPanel

I'm working on Windows c#.
Firstly, the things those can not be change as my need are following:
The Size of TableLayoutPanel is fixed.
The Total # of columns are fixed.
Now, I want to set a fix height for all rows but as increasing the rows, if I set the RowStyle property to Percent with 100.0F then it works fine for 3 to 4 items, but after 4-5 items, the control on one row overwrites controls on another row.
I have searched for this so more but i'm not able to get the proper answer. I have also tried the AutoSize, Percent, Absolute properties of RowStyle, even though it is not working.
So what to do and how? How can I achieve this?
Ultimately, I want to do same like as DataGridView of Windows C#.
Thanks in advance....
I'm working on WinForms...the sample code is here..
int cnt = tableLayout.RowCount = myDataTable.Rows.Count;
tableLayout.Size = new System.Drawing.Size(555, 200);
for (int i = 1; i <= cnt; i++)
{
Label lblSrNo = new Label();
lblSrNo.Text = i.ToString();
TextBox txt = new TextBox();
txt.Text = "";
txt.Size = new System.Drawing.Size(69, 20);
tableLayout.Controls.Add(lblSrNo, 0, i - 1);
tableLayout.Controls.Add(txt, 1, i - 1);
}
tableLayout.RowStyles.Clear();
foreach (RowStyle rs in tableLayout.RowStyles)
tableLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
The label and textboxes are working fine for 4-5 #of rows but whenever the #of row(in this case, variable cnt in for loop) increases, the rows are overwriting each other that is one control overwrite to another...I had drag-drop the TableLayoutPanel control and created just one row and 2 columns manually.
So please tell me how to do it.
I'm still new to tableLayoutPanels myself, but I noticed that at the bottom of your code, you're Clearing all the rowstyles from the collection, then you're trying to iterate through them in your foreach loop.
You did this:
tableLayout.RowStyles.Clear(); //now you have zero rowstyles
foreach (RowStyle rs in tableLayout.RowStyles) //this will never execute
tableLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
Try this instead.
TableLayoutRowStyleCollection styles =
tableLayout.RowStyles;
foreach (RowStyle style in styles){
// Set the row height to 20 pixels.
style.SizeType = SizeType.Absolute;
style.Height = 20;
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Edit: I just realized that adding N rows doesn't add N rowstyles that you can iterate through. I think what's happening is that you're adding N rows, but none of them have a rowstyles.
I suppose you can Clear() the rowstyles, then just add N rowstyles similar to how you're already doing.
There are 2 ways to increase the row height of table layout panel.
Look into the following link :
https://social.msdn.microsoft.com/Forums/windows/en-US/d80db8e1-d6cc-48b8-957f-0f73263c6d4a/how-to-change-the-row-height-of-a-tablelayoutpanel-at-runtime?forum=winforms
It specifies by setting the YourTableLayoutPanel.RowStyles[index].Height int he code behind class.
The other way is to set the row height in the designer of your UI. Through UI, go into Rows properties of the panel, select the row and set the required height using percent or absolute

Why don't controls placed in TableLayoutPanels "snap to" their "cells"?

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.

DataGridView header alignment slightly to left even after setting it to MiddleCenter

I'm setting the following properties for a DataGridView in my C# project ...
sampleDataGridView.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
sampleDataGridView.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
But I notice that the heading's (i.e. text in header cell is offset slightly to the left, for some reason .. The data rows alignment in perfectly in center though ...
What can be causing this ?
It is because there is a sorting glyph (little arrow) for which the DataGridView reserves some space to show the sort order. If you want to disable the sorting glyph, set the SortMode on your column to NotSortable, and your text should then be centered.
Old topic,
i had this problem, and i found out that the problem was that i had the grid datasource-linked to my stuff, and when the columns where created they put the ALLOW SORT ON by default, which created the arrow down button to sort, and the text gets pushed to the left.
small fix to this is:
private void MyDataGridView_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
{
e.Column.SortMode = DataGridViewColumnSortMode.NotSortable;
}
I had the same problem as you and it seems it's a framework issue: MS Connect
Figure out the glyph width without refection and supress when out of line:
In the following code I turn on and off sorting while autoadjusting column width to the column header text width. The difference in width between sorting on/off will reveal the width used by the column sort glyph.
As column looses the glyph when column width is set less than autoadjusted width I supress it by inserting symetric left and right column padings.
I also use a dictionary to store ColumnWidth events by datagridview instance to turn width events on and off while seting the width.
I call this crazy code both to autoadjust initial column widths and to manage the column header padings when user drags column widths.
void AdaptColumnHeaderText(DataGridViewColumn column, bool autoSize=false)
{
//Supress column width events
if (dataGridViewColumnWidthEventHandlers.ContainsKey(column.DataGridView))
{
dataGridView1.ColumnWidthChanged -= dataGridViewColumnWidthEventHandlers[column.DataGridView];
}
//Save initial column with as preferred
var w_preferred = column.Width;
if (
column.SortMode.Equals(DataGridViewColumnSortMode.Automatic) &&
column.HeaderCell.Style.Alignment.Equals(DataGridViewContentAlignment.MiddleCenter))
{
//remove all header padding
column.HeaderCell.Style.Padding = new Padding(0, 0, 0, 0);
//Fit column width to header text with AND sort glyph
column.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader;
//save column width sort enabled
var w_sort = column.Width;
//Fit column width to header text with NO sort glyph
column.SortMode = DataGridViewColumnSortMode.NotSortable;
//save column width when sort disable
var w_nosort = column.Width;
//Calculate width consumed by sort glyph
var w_glyph = w_sort - w_nosort;
//Nominal column width if glyph width applied left and right of header text
var w_nominal = w_glyph + w_nosort + w_glyph;
//Disable column width autosize
column.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
//Enable column autosorting
column.SortMode = DataGridViewColumnSortMode.Automatic;
//If autosize option - ignore preferred width and set to nominal
if (autoSize)
{
w_preferred = w_nominal;
}
//Pad depending on final column width
if (w_preferred >= w_nominal)
{
//Preferred width greater than nominal - pad left of text to balance width used by glyph
column.HeaderCell.Style.Padding = new Padding(w_glyph, 0, 0, 0);
}
else
{
//Two symetric glyphs will not fit - pad left and right to supress glyph
w_glyph = (w_preferred - w_nosort) / 2;
column.HeaderCell.Style.Padding = new Padding(w_glyph, 0, w_glyph, 0);
}
column.Width = w_preferred;
Console.WriteLine("name:{0}, glyph:{1}, w_preferred:{2}", column.Name, w_glyph, w_preferred);
}
//re-enable column width events
if (dataGridViewColumnWidthEventHandlers.ContainsKey(column.DataGridView))
{
dataGridView1.ColumnWidthChanged += dataGridViewColumnWidthEventHandlers[column.DataGridView];
}
}
You can add two spaces at the beginning of the column name to compensate the space reseved for the glyph.

Categories

Resources