I'm trying to center a fixed size control within a form.
Out of interest, is there a non-idiotic way of doing this? What I really want is something analogous to the text-align css property.
At the moment, I'm setting the padding property of the surrounding form to a suitable size and setting the Dock property of the control to fill.
You could achieve this with the use of anchors. Or more precisely the non use of them.
Controls are anchored by default to the top left of the form which means when the form size will be changed, their distance from the top left side of the form will remain constant. If you change the control anchor to bottom left, then the control will keep the same distance from the bottom and left sides of the form when the form if resized.
Turning off the anchor in a direction will keep a control centered when resizing, if it is already centered. In general, a control not anchored maintains its proportional position to the dialog. E.g. If you put a control at X=75% of the dialog width and turn off left/right anchors, the control will maintain its center at X=75% of the dialog width.
NOTE: Turning off anchoring via the properties window in VS2015 may require entering None (instead of default Top,Left)
myControl.Left = (this.ClientSize.Width - myControl.Width) / 2 ;
myControl.Top = (this.ClientSize.Height - myControl.Height) / 2;
Since you don't state if the form can resize or not there is an easy way if you don't care about resizing (if you do care, go with Mitch Wheats solution):
Select the control -> Format (menu option) -> Center in Window -> Horizontally or Vertically
I found a great way to do this and it will work with multiple controls. Add a TableLayout with 3 columns. Make the center column an absolute size (however much room you need). Set the two outside columns to 100%. Add a Panel to the center column and add any controls you need and place them where you want. That center panel will now remain centered in your form.
To center Button in panel o in other container follow this step:
At design time set the position
Go to properties Anchor of the button and set this value as the follow image
In addition, if you want to align it to the center of another control:
//The "ctrlParent" is the one on which you want to align "ctrlToCenter".
//"ctrlParent" can be your "form name" or any other control such as "grid name" and etc.
ctrlToCenter.Parent = ctrlParent;
ctrlToCenter.Left = (ctrlToCenter.Parent.Width - ctrlToCenter.Width) / 2;
ctrlToCenter.Top = (ctrlToCenter.Parent.Height - ctrlToCenter.Height) / 2;
You can put the control you want to center inside a Panel and set the left and right padding values to something larger than the default. As long as they are equal and your control is anchored to the sides of the Panel, then it will appear centered in that Panel. Then you can anchor the container Panel to its parent as needed.
It involves eyeballing it (well I suppose you could get out a calculator and calculate) but just insert said control on the form and then remove any anchoring (anchor = None).
you can put all your controls to panel and then write a code to move your panel to center of your form.
panelMain.Location =
new Point(ClientSize.Width / 2 - panelMain.Size.Width / 2,
ClientSize.Height / 2 - panelMain.Size.Height / 2);
panelMain.Anchor = AnchorStyles.None;
To keep the control centered even the form or parent control were resize.
Set the following properties of the parent element (you can set it through the property window):
parentControl.AutoSize = true;
parentControl.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Put this code in the Resize event of the form or the parent control (if the control is inside of another control).
controlToCenter.Left = (parentControl.Width- controlToCenter.Width) / 2;
controlToCenter.Top = (parentControl.Height - controlToCenter.Height) / 2;
If the parent control is docked to the form, add this line of code.
//adjust this based on the layout of your form
parentControl.Height = this.ClientSize.Height;
So, I am currently working on a pagination control and I came up with the following to achieve the below result.
Add a PanelLayout to your container (e.g. form or usercontrol)
Set the PanelLayout properties:
Dock: Bottom (or Top)
AutoSize: False
This will center the PanelLayout horizontally.
Now, in your codebehind, do something like this:
public MyConstructor()
{
InitializeComponent();
for (var i = 0; i<10; i++)
{
AddButton(i);
}
}
void AddButton(int i)
{
var btn = new Button();
btn.Width = 30;
btn.Height = 26;
btn.Text = i.ToString();
this.flowLayoutPanel1.Controls.Add(btn);
btn.Anchor = AnchorStyles.None;
}
There is a ceveat, however. If I make my form too small (horizontally) buttons will "disappear" outside of the viewport. In my case, that's not a problem, but you could take care of this by writing code that listens to the Resize event, and remove elements (buttons) based on the viewport Width.
Related
Description Created a form in Winform C# app.
Added a panel (PBack) with dock type fill. (Scrollable)
Then added a picturebox(pbDraw) in panel(PBack) that height depends upon image size.
I want to add a control on the bottom left of the current screen view. (assume client scrolled down)
What i tried
Rectangle rect = Screen.GetWorkingArea(pbDraw);
ctrl.Top = rect.Top + rect.Height;
ctrl.Top = Screen.PrimaryScreen.WorkingArea.Top + Screen.PrimaryScreen.WorkingArea.Height;
ctrl.Top = Screen.FromControl(pbDraw).WorkingArea.Top+ Screen.FromControl(pbDraw).WorkingArea.Height;
Issue control is adding at top of pbDraw (0,0) and not on current screen bounds top.
As far as I understand, Control.Top takes the scrolled view into account all by itself.
Gets or sets the distance, in pixels, between the top edge of the control and the top edge of its container's client area.
So, you should be able to assign the coordinates relevant to the pBack and its client view:
ctrl.Top = pBack.Height - ctrl.Height;
Assuming that ctrl is a child of pBack this code should place it at the bottom of the current (scrolled) part of pBack
UPDATE:
As you said in the comments, ctrl is actually a child of pbDraw. In this case, you'll need to take the scrolling into account. For that, you can use Panel.VerticalScroll:
//scroll position + panel height - control height
ctrl.Top = pBack.VerticalScroll.Value + pBack.Height - ctrl.Height;
If I were you, I'd add ctrl to the panel, on top of the picture box. This will make it easier to calculate offsets relative to the panel.
I have a user control which consists of two controls and four buttons, arranged on a Form: the two controls are on the sides and the buttons in the middle in a vertical row.
When using the control in an app, I place it on a form.
Now, when re-sizing the form horizontally, the two controls just move left or right w/o changing their size.
What I need is that the controls stay anchored to the middle of the form and grow to the sides (sorry about the lack of clarity, I prepared screenshots but the site wouldn't let me attach them).
Is there a way to accomplish this without overriding the Resize event?
Use a TableLayoutPanel as base for your user control.
You need 3 columns and one row. The middle column needs to have a fixed size, and the other 2 you set to 50%. Not to worry, .Net is smart enough to calculate the percent they actually take.
Inside the right and left columns you put your controls and set the Dock property of both to fill. In the middle column you put a panel and set it's Dock property to fill as wall, and In that panel you put the buttons in the middle.
Set your table layout panel Dock to fill as well, and when adding the user control to the form use Dock top, bottom or fill as well.
Erratum:
The above code works most of the time, but it fails for certain Move-Resize sequences. The solution is to respond to the Move and Resize events of the parent form (the consumer of the control), not of the control itself.
One more thing: due to the event firing order (Move first followed by Resize, had to move the working code from Resize() to Move(), which seems counterintuitive but it seems the right way nevertheless.
It seems indeed that it cannot be done in the Designer, but here is the solution using overrides.
It works ok, except for some control flickering which I haven't been able to overcome.
public partial class SB : UserControl
{
//variables to remember sizes and locations
Size parentSize = new Size(0,0);
Point parentLocation = new Point (0,0);
......
// we care only for horizontal changes by dragging the left border;
// all others take care of themselves by Designer code
public void SB_Resize(object sender, EventArgs e)
{
if (this.Parent == null)
return;//we are still in the load process
// get former values
int fcsw = this.parentSize.Width;//former width
int fclx = this.parentLocation.X;//former location
Control control = (Control)sender;//this is our custom control
// get present values
int csw = control.Parent.Size.Width;//present width
int clx = control.Parent.Location.X;//present location
// both parent width and parent location have changed: it means we
// dragged the left border or one of the left corners
if (csw != fcsw && clx != fclx)
{
int delta = clx - fclx;
int lw = (int)this.tableLayoutPanel1.ColumnStyles[0].Width;
int nlw = lw - delta;
if (nlw > 0)
{
this.tableLayoutPanel1.ColumnStyles[0].Width -= delta;
}
}
this.parentSize = control.Parent.Size;//always update it
this.parentLocation = control.Parent.Location;
}
//contrary to documentation, the Resize event is not raised by moving
//the form, so we have to override the Move event too, to update the
//saved location
private void SB_Move(object sender, EventArgs e)
{
if (this.Parent == null)
return;//we are still in the load process
this.parentSize = this.Parent.Size;//always update it
this.parentLocation = this.Parent.Location;
}
}
The above code works most of the time, but it fails for certain Move-Resize sequences. The solution is to respond to the Move and Resize events of the parent form (the consumer of the control), not of the control itself.
One more thing: due to the event firing order (Move first followed by Resize, had to move the working code from Resize() to Move(), which seems counterintuitive but it seems the right way nevertheless.
i have a program i made that can pead replays from a game then get data out of them i am trying to add support for tag replays so i need 2 more list boxes and 4 more buttons on the form but i want them hidden when they aren't being used(when a tag replay isnt opened) i have tried autosize with a minimal and maximal width-height and it didnt work dont know what else to try oh and im using a table layout panel to organize everything
heres some shots to show what i want
Here's something to get you started.
Your controls resize with the form because the table layout is anchored to the form from the bottom.
So, what I just tried and could work for you is to temporarily turn off the bottom anchor, change the form height, then bring back the bottom anchor as it was.
Since you haven't shared any code, I'm using typical control names:
var oldanchor = tableLayoutPanel1.Anchor; // memorize the old anchor
tableLayoutPanel1.Anchor = AnchorStyles.Left | AnchorStyles.Top; // reset anchor
this.Height = 200; // change the form's height
tableLayoutPanel1.Anchor = oldanchor; // bring back the old anchor
I have a Form with only a single TabControl, with many tabs, where each tab has only square buttons side by side. I am trying to make it so that when the user clicks on a tab, the form resizes itself to a size where you can see all the buttons in a particular tab or a size where you can see all the tabs, whichever is greater.
I am just curious if there is a way to query where the last control in a tab page is? So I can just do something like:
tabForm.Width = currentTabPage.UsedContentBorder + 10;
Or do I have to do this by adding all the controls and the sizes between them, etc?
You want to find out the maximum coordinates of all controls in a specific tab? Easy with LINQ:
int right = tab.Controls.Cast<Control>().Max(c => c.Right);
int bottom = tab.Controls.Cast<Control>().Max(c => c.Bottom);
Now, to properly choose the size of the form, I imagine you just have to figure out how much larger the Form is than its TabPages... I would guess something like this:
int extraWidth = form.Width - tabControl.SelectedTab.Width;
int extraHeight = form.Height - tabControl.SelectedTab.Height;
Then you just do
form.Size = new Size(right + extraWidth, bottom + extraHeight);
(the TabControl will resize automatically if its Anchor property is set to all four sides.) It occurs to me that this may malfunction if the user resizes the form very small... you may be able to compensate by calculating extraWidth and extraHeight in the Form.Load event and then saving those values for when you need them later.
.NET Framework / C# / Windows Forms
I'd like the FlowLayoutPanel to automatically adjust its width or height depending on number of controls inside of it. It also should change the number of columns/rows if there is not enough space (wrap its content). The problem is that if I set autosize then the flowlayoutpanel doesn't wrap controls I insert. Which solution is the best?
Thanks!
Set the FlowLayoutPanel's MaximumSize to the width you want it to wrap at. Set WrapContents = true.
Have you tried using the TableLayoutPanel? It's very useful for placing controls within cells.
There is no such thing like impossible in software development. Impossible just takes longer.
I've investigated the problem. If there is really need for Flow Layout, it can be done with a bit of work. Since FlowLayoutPanel lays out the controls without particularly thinking about the number of rows/columns, but rather on cumulative width/height, you may need to keep track of how many controls you've already added. First of all, set the autosize to false, then hook your own size management logic to the ControlAdded/ControlRemoved events. The idea is to set the width and height of the panel in such a way, that you'll get your desired number of 'columns' there
Dirty proof of concept:
private void flowLayoutPanel1_ControlAdded(object sender, ControlEventArgs e)
{
int count = this.flowLayoutPanel1.Controls.Count;
if (count % 4 == 0)
{
this.flowLayoutPanel1.Height = this.flowLayoutPanel1.Height + 70;
}
}
if the panel has initially width for 4 controls, it will generate row for new ones. ControlRemoved handler should check the same and decrease the panel height, or get all contained controls and place them again. You should think about it, it may not be the kind of thing you want. It depends on the usage scenarios. Will all the controls be of the same size? If not, you'd need more complicated logic.
But really, think about table layout - you can wrap it in a helper class or derive new control from it, where you'd resolve all the control placing logic. FlowLayout makes it easy to add and remove controls, but then the size management code goes in. TableLayout gives you a good mechanism for rows and columns, managing width and height is easier, but you'd need more code to change the placement of all controls if you want to remove one from the form dynamically.
If possible, I suggest you re-size the FlowLayoutPanel so that it makes use of all the width that is available and then anchor it at Top, Left and Right. This should make it grow in height as needed while still wrapping the controls.
I know this is an old thread but if anyone else wonders on here then here's the solution I produced - set autosize to true on the panel and call this extension method from the flow panel's Resize event:
public static void ReOrganise(this FlowLayoutPanel panel)
{
var width = 0;
Control prevChildCtrl = null;
panel.SuspendLayout();
//Clear flow breaks
foreach (Control childCtrl in panel.Controls)
{
panel.SetFlowBreak(childCtrl, false);
}
foreach (Control childCtrl in panel.Controls)
{
width = width + childCtrl.Width;
if(width > panel.Width && prevChildCtrl != null)
{
panel.SetFlowBreak(prevChildCtrl, true);
width = childCtrl.Width;
}
prevChildCtrl = childCtrl;
}
panel.ResumeLayout();
}
Are you adding the controls dynamically basing on the user's actions? I'm afraid you'd need to change the FlowLayout properties on the fly in code, when adding new controls to it, then refreshing the form would do the trick.