Autosizing panel by text content - c#

I have a WinForms-Application where I want to add UserControls dynamicly docking to the top:
this.Controls.Clear();
this.Controls.Add(myCustomControl(){Title="first", content="first text", Dock=DockStyle.Top});
this.Controls.Add(myCustomControl(){Title="second", content="very long text, where......", Dock=DockStyle.Top});
now myCostumControl [YELLOW] is a userControl with the following content:
TopTitle [PINK]: A Label, docked to the top
BottomContent [GREEN]: A Panel, Fills out the rest of the Control below the TopTitle (Dockstyle Fill)
TextContent [BLUE]: A multiline Textbox, docked (fill) within the Panel.
So it looks like this:
Now what I need to achieve is that the Height from myCustomControl is according to the Text-Content of the "TextContent" - TextBox, so I can stack multiple Controls. So if there is only a "Hello World" in it, the height should be small, if I put the Windows EULA in it it should be very long.
I already tried messing around with all "AutoSize"-properties I could get my hands on, but the Textbox either disappeared completely or it hat no effect.
I also tried resizing the Textbox on Change:
Size size = TextRenderer.MeasureText(txtContent.Text, txtContent.Font);
txtContent.Height = size.Height;
No success, either

To make your composite control auto-size, perform these settings:
Add a Label to user control and set AutoSize of label to false and set it's height to a suitable height and set its Dock to top.
Add a TextBox to user control and set its Dock to Fill.
override SetBoundsCore and calculate the preferred size of control:
protected override void SetBoundsCore(int x, int y, int width, int height,
BoundsSpecified specified)
{
var flags = TextFormatFlags.WordBreak | TextFormatFlags.NoPrefix;
var proposedSize = new Size(width, int.MaxValue);
var size = TextRenderer.MeasureText(textBox1.Text, textBox1.Font,
proposedSize, flags);
height = Math.Max(size.Height, textBox1.Font.Height) + label1.Height + 5;
base.SetBoundsCore(x, y, width, height, specified);
}
Handle TextChanged event of the TextBox to refresh size of control when content text changes:
void textBox1_TextChanged(object sender, EventArgs e)
{
SetBoundsCore(Left, Top, Width, Height, BoundsSpecified.Size);
}
Here is the result:

If you want myCustomControlto be autosized, then obvioulsy, you cannot use fill docking for any child controls as docking set the size of the child according to parent size and you want parent size to adjust according to child size.
So you should either use a table layout or a flow layout for the child. If you use a table, then, you have to use auto-size for the rows that should adapt.
Then the whole layout control could be set to auto-size and should be docked at top (or maybe anchored).
An you can the parent to display a vertical scrollbar if the layout control does not fit in visible area.

Related

Dynamically resize WinForms controls when Form is resized

I have a WinForms project on which I would like all of the controls to grow proportionally along with the form as the form is resized. This is what the form looks like in normal state: Normal State Form
I have tried setting the Anchor properties to their appropriate values given the location of each control on the form, and while it does move the controls, they remain the same size. I tried using the AutoSize property, but also to no avail. Here is what the form looks like after being maximized with the Anchor properties set: Maximized Form
I also tried using a formula from Shaun Halverson to dynamically resize everything but it does not relocate the control properly, and I can't seem to figure out why. Here is the code I used to try and resize dynamically:
private void Main_Load(object sender, EventArgs e)
{
originalFormSize = new Rectangle(this.Location.X, this.Location.Y, this.Size.Width, this.Size.Height);
submitBtnOriginal = new Rectangle(submitButton.Location.X, submitButton.Location.Y, submitButton.Width, submitButton.Height);
}
private void Main_Resize(object sender, EventArgs e)
{
resizeControl(submitBtnOriginal, submitButton);
}
private void resizeControl(Rectangle r, Control c)
{
float xRatio = (float)(this.Width) / (float)(originalFormSize.Width);
float yRatio = (float)(this.Height) / (float)(originalFormSize.Height);
int newWidth = (int)(r.Width * xRatio);
int newHeight = (int)(r.Height * yRatio);
int newX = (int)(r.Width * xRatio);
int newY = (int)(r.Height * yRatio);
c.Location = new Point(newX, newY);
c.Size = new Size(newWidth, newHeight);
}
When I run this code, it moves the button to the opposite corner of the form, but it resizes it properly.
This would obviously be quite redundant given that I have to get an original size for every control I want to resize, but I would be fine with that if I could get dynamic resizing to work. I am surprised that this is not a more common problem, and I couldn't find hardly anything on this specific topic other than to use the Anchor and Dock properties. Is there an easy way to do this that I am missing? Is this a more difficult problem than it seems?
Put TextBox anchor property values as Top, Bottom, Left, Right and resize the form. That should work.

Get width directly after Button.AutoResize is set

I'm adding buttons dynamically to a Form and am trying to lay the out side by side. I'm perfectly content with using the latest Button.Right as a starting point for the next button (with a margin left between of course), but the buttons have to adjust to fit the text.
So, what I'm doing is setting the AutoResize property to true and then storing the Right property, which however does not work because I guess the resizing doesn't happen until the button is drawn (I think). I tried Invalidate(), Refresh(), Update() and I think a couple more functions, and of course all together, but to no avail, I still get the old position and the next button starts beneath this one.
So the question is, after setting AutoResize to true on a Forms component, how do I force it to resize so I can grab the new Width/Right without waiting for the window to be redrawn?
Thanks in advance!
Note: If all else fails I'll do a rough approximation of the width of the buttons based on the string's length, so don't bother with something too fancy as a solution, it's not a requirement that it is perfect
You can use the Control's GetPreferredSize Method to obtain the final auto-sized dimensions. The Font property must either be explicitly set or the Control must be parented to a displayed control such that it can inherit the Font to use in the layout. In the following example, the control's Parent property is set so that it inherits the parent control's Font.
private Random rnd = new Random(1000);
private void button1_Click(object sender, EventArgs e)
{
const Int32 xDelta = 5; // the horizontal distance between the added Buttons
Int32 y = button1.Location.Y + 5 + button1.Height;
Int32 x = button1.Location.X;
Point loc = new Point(x, y);
this.SuspendLayout(); // this is Form that is the Parent container of the Buttons
for (Int32 i = 1; i <= 10; i++)
{
Button btn = new Button { Parent = this, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
btn.Text = new string('A', rnd.Next(1, 21));
btn.Location = loc;
Size sz = btn.GetPreferredSize(Size.Empty); // the size of btn based on Font and Text
loc.Offset(sz.Width + xDelta, 0);
}
this.ResumeLayout(true);
}

Scrollbar handling scroll event in winforms

I am creating a form which looks like this.
Form1 has two panels:
the bottom panel (yellow)
the top panel, which is himself composed by two panels:
the left panel (red)
the right panel (green)
As the image shows, the top panel, exceeds form1's height, so I would like to use a scrollbar. I have several problems:
a) Setting auto-scroll to true in the top panel does not work.
b) If I put a button on the top panel whose location exceeds form1's height, then it scrolls, but NOT if I put it in any of its subpanels (red or green)
c) Instead of setting auto-scroll to true in the top panel, I could create my own scrollbar, but I just couldn't find a simple example handling the scroll event that moves the panel so that the view port changes as desired.
How could I properly add a scrollbar with the desired behaviour?
As LarsTech and Hans Passant suggested, AutoScrollMinSize worked, so a and b questions were solved.
Regarding question C, I thought there was a way to create a scrollbar so that it behaves like I guess 99% of scrollbars should behave (just to move a desired panel). Finally, I coded that logic myself.
I added a Scrollbar1 as a control of the basic form. initialPanelY is catched on the constructor.
This is the code that responds to the scroll event. I just move the panel panelRest2's Y location depending on the scrollbar value
using System;
.
.
using System.Windows.Forms;
namespace WindowsFormMyTests
{
public partial class Form1 : Form
{
int initialPanelY;
public Form1()
{
InitializeComponent();
initialPanelY = this.panelRest2.Location.Y;
}
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
int v = (sender as VScrollBar).Value; //between 1 and 100
int hiddenPanelHeight = this.panelRest2.Size.Height - this.Size.Height;
float moveY = (float)hiddenPanelHeight * ((float)v / 100);
int newY = initialPanelY - (int)moveY;
this.panelRest2.Location = new Point(this.panelRest2.Location.X, newY);
}
}
}

Why the scrollbar thumb moving to the starting position while leaving the thumb after dragging?

I have created a custom control for having the scrolling support. This control consist of two scrollbars namely HScollBar and VScrollBar.
When i drag and leave the thumb at the particular location, it moves to the starting location(0).
How can i prevent the thumb moving to the default position and is there any easy way to achieve the custom scroll control using the scrollbars?
You don't need to use scrollbars for scrolling. You can set AutoScroll to true. Also if your control is a custom paint control, set AutoScrollMinSize to a suitable value. For a normal container control, you don't need to manipulate AutoScrollMinSize yourself, it will be calculated based on locations of child controls. For example:
using System.Drawing;
using System.Windows.Forms;
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.AutoScroll = true;
}
protected override void OnPaint(PaintEventArgs e)
{
//for a custom paint control, calculate the minimum size which needs scrollbars
//for a normal container control you don't need to calculate minimum size
this.AutoScrollMinSize = new Size(300, 500);
base.OnPaint(e);
var r = this.DisplayRectangle;
r.Width--; r.Height--;
e.Graphics.DrawRectangle(Pens.Red, r);
TextRenderer.DrawText(e.Graphics, "Top-Left", Font, r, ForeColor,
TextFormatFlags.Top | TextFormatFlags.Left);
TextRenderer.DrawText(e.Graphics, "Bottom-Right", Font, r, ForeColor,
TextFormatFlags.Bottom | TextFormatFlags.Right);
}
}

How do I keep a label centered in WinForms?

In WinForms I am using a Label to display different messages like success, failure, etc.
I'd like to center that label in the center form. I want a solution that will keep it centered whether there's just one word or a whole sentence in the label.
Set Label's AutoSize property to False, TextAlign property to MiddleCenter and Dock property to Fill.
You will achive it with setting property Anchor: None.
Some minor additional content for setting programmatically:
Label textLabel = new Label() {
AutoSize = false,
TextAlign = ContentAlignment.MiddleCenter,
Dock = DockStyle.None,
Left = 10,
Width = myDialog.Width - 10
};
Dockstyle and Content alignment may differ from your needs. For example, for a simple label on a wpf form I use DockStyle.None.
If you don't want to dock label in whole available area, just set SizeChanged event instead of TextChanged. Changing each letter will change the width property of label as well as its text when autosize property set to True. So, by the way you can use any formula to keep label centered in form.
private void lblReport_SizeChanged(object sender, EventArgs e)
{
lblReport.Left = (this.ClientSize.Width - lblReport.Size.Width) / 2;
}
The accepted answer didn't work for me for two reasons:
I had BackColor set so setting AutoSize = false and Dock = Fill causes the background color to fill the whole form
I couldn't have AutoSize set to false anyway because my label text was dynamic
Instead, I simply used the form's width and the width of the label to calculate the left offset:
MyLabel.Left = (this.Width - MyLabel.Width) / 2;
I wanted to do something similar, but on a form with a background image, I found that when the text in the label changed the repaints were obvious with this method, so I did the following:
* Set the label AutoSize to true and TextAlign to MiddleCenter
Then, each time the text changed (mine was done using a timer) I called the following method:
private Point GetPosition()
{
int y = (this.Height / 2) - (label1.Height / 2);
int x = (this.Width / 2) - (label1.Width / 2);
return new Point(x, y);
}
And set the label's Location property to this return value. This ensured that the label was always in the center of the form when the text changed and the repaints for a full-screen form weren't obvious.
You could try out the following code snippet:
private Point CenterOfMenuPanel<T>(T control, int height=0) where T:Control {
Point center = new Point(
MenuPanel.Size.Width / 2 - control.Width * 2,
height != 0 ? height : MenuPanel.Size.Height / 2 - control.Height / 2);
return center;
}
It's Really Center

Categories

Resources