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
Related
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);
}
In my code I draw a rectangle and usually the rectangle is too large for the screen, even when maximised. I have set the form property AutoScroll to true and this doesn't seem to do anything. There won't be anything else on my form except the rectangle painting, how can I implement a vertical and horizontal scroll?
PrintingDesignForm form = new PrintingDesignForm();
form.Paint += (se, pe) => {
var r = new Rectangle(parameters.RectangleXPosition, parameters.RectangleYPosition, (int)Math.Ceiling(parameters.RectangleWidth) * 72, (int)Math.Ceiling(parameters.RectangleLength) * 72);
var brush = new SolidBrush(Color.FromArgb(255, 255, 204));
pe.Graphics.FillRectangle(brush, r);
using (var pen = new Pen(brush.Color, 2))
pe.Graphics.DrawRectangle(pen, r);
};
form.WindowState = FormWindowState.Maximized;
form.Show();
Setting AutoScroll = true on a Control/Form alone will only ensure that all Controls you add/nest to the parent will either show or can be reached by the scrollbars which will show up as needed.
This doesn't do anything for stuff you draw.
To make the drawing scrollable you need to set the AutoScrollMinSize to large enough values.
If you don't know in advance then at least while drawing you should be able to determine them from your data.
In Form Properties Set
AutoScroll = True
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.
I'm working on a C# project using .NET 3.5 and Windows Forms. I need to design a decision step with multiple options that require a bit of explanatory text. For this, I want to have a set of RadioButtons to choose an option, followed by an additional Label each that contains the explanation.
I want to keep the label of the radio buttons and the label containing the explanatory text aligned - I've added red lines to the image to illustrate this. I could probably tweak some margins or other settings on the second label, but that would probably start to look weird as soon as the user chooses a different theme or changes some other settings. What is the canonical (and most robust) way to do this?
Your question boils down to two partial problems:
How large is the RadioButton (or the CheckBox when thinking ahead)..
How large is the gap between the glyph and the Text.
The first question is trivial:
Size s = RadioButtonRenderer.GetGlyphSize(graphics,
System.Windows.Forms.VisualStyles.RadioButtonState.CheckedNormal);
..using a suitable Graphics object. Note that I use the RadioButtonState CheckedNormal as I don't you want the Lables to align differently when the Buttons are checked or unchecked..
The second one is anything but trivial. The gap may or may not be constant and there is another gap to the left of the glyph! If I really wanted to get it right I guess I would write a routine to measure the text offset at startup:
public Form1()
{
InitializeComponent();
int gapRB = getXOffset(radioButton1);
int gapLB = getXOffset(label1);
label1.Left = radioButton1.Left + gapRB - gapLB;
}
Here is the measurement function. Note that is doesn't even use the Glyph measurement. Also note that it isn't enough to measure the text offset of the RadioButton. You also need to measure the offset of the Label!
int getXOffset(Control ctl)
{
int offset = -1;
string save = ctl.Text; Color saveC = ctl.ForeColor; Size saveSize = ctl.Size;
ContentAlignment saveCA = ContentAlignment.MiddleLeft;
if (ctl is Label)
{
saveCA = ((Label)ctl).TextAlign;
((Label)ctl).TextAlign = ContentAlignment.BottomLeft;
}
using (Bitmap bmp = new Bitmap(ctl.ClientSize.Width, ctl.ClientSize.Height))
using (Graphics G = ctl.CreateGraphics() )
{
ctl.Text = "_";
ctl.ForeColor = Color.Red;
ctl.DrawToBitmap(bmp, ctl.ClientRectangle);
int x = 0;
while (offset < 0 && x < bmp.Width - 1)
{
for (int y = bmp.Height-1; y > bmp.Height / 2; y--)
{
Color c = bmp.GetPixel(x, y);
if (c.R > 128 && c.G == 0) { offset = x; break; }
}
x++;
}
}
ctl.Text = save; ctl.ForeColor = saveC; ctl.Size = saveSize;
if (ctl is Label) { ((Label)ctl).TextAlign = saveCA; }
return offset;
}
Now the Texts do align pixel perfect..:
Note that I use two original controls from my form. Therefore much of the code is simply storing and restoring the properties I need to manipulate for the measurement; you can save a few lines by using two dummies.. Also note that I wrote the routine so that it can measure RadioButtons and Labels and probably CheckBoxes as well..
Is it worth it? You decide..!
PS: You could also owner-draw the RadioButton and the Label text in one.. this would have the interesting side-effect, that the whole text would be clickable..:
Here is a quick and dirty implementation of owner drawing a CheckBox: Prepare it by setting AutoSize = false and by adding the real text together with the extra text into the Tag, separated by a e.g. "§". Feel free to change this setup, maybe using the Label control..
I clear the Text to prevent it from drawing it and I decide on an offset. To measure it, you could use the GetGlyphSize from above.. Note how the DrawString method honors embedded '\n' characters.
The Tag contained this string:
A Rose is a Rose is a Rose..§A Rose is a rose is a rose is a rose is /
A rose is what Moses supposes his toes is / Couldn't be a lily or a
taffy daphi dilli / It's gotta be a rose cuz it rhymes with mose!
And I for the screenshot I actually used this line:
e.Graphics.DrawString(texts[1].Replace("/ ", "\n"), ...
Here is the Paint event:
private void checkBox1_Paint(object sender, PaintEventArgs e)
{
checkBox1.Text = "";
string[] texts = checkBox1.Tag.ToString().Split('§');
Font font1 = new Font(checkBox1.Font, FontStyle.Regular);
e.Graphics.DrawString(texts[0], checkBox1.Font, Brushes.Black, 25, 3);
if (texts.Length > 0)
{
SizeF s = e.Graphics.MeasureString(texts[1], checkBox1.Font, checkBox1.Width - 25);
checkBox1.Height = (int) s.Height + 30;
e.Graphics.DrawString(texts[1], font1, Brushes.Black,
new RectangleF(new PointF(25, 25), s));
}
}
The simplest out-of-the-box solution (it seems to me) would be to use 3 controls instead of 2: a radio button (with the text set to ""), a label (to go beside the radio button) and another label (to go below them). This would allow you easier configuration in designer, but (far more importantly) simpler run-time evaluation and adjustment, if necessary, to keep them in alignment should styles change.
I do understand that this takes away the benefit of clicking the label to select the radio button, but you could add that behavior in the label's Click event if you need it.
Alternatively, you could create a UserControl containing the text-free radio button and the label, and handle the behavior within that UserControl while exposing the label's location.
If you don't care about the radiobutton's text being bold, you could set it's label to a multiline string, and set CheckAlign to TopLeft:
radioButton2.CheckAlign = ContentAlignment.TopLeft;
radioButton2.Text = #"Radiobutton
Explanation text";
Don't know why I didn't think of this earlier, but the following approach seems to work:
Use a TableLayoutPanel with two columns that are set to adjust their width automatically.
Place all RadioButtons in the first column and set them to span both columns.
Place all Labels in the second column, setting all margins to 0.
Add a disabled, but visible (!) "spacer" RadioButton without text in an additional row at the end of the layout.
When displaying the form, convert the first column to a fixed size and hide the "spacer".
The key point seems to be that the "spacer" has to be visible initially - otherwise the column will get a size of 0.
This is my test form in the designer:
To change the layout, I used the following Load handler:
private void TestForm_Load(object sender, EventArgs e)
{
// find the column with the spacer and back up its width
int column = tableLayoutPanel.GetColumn(radioButtonSpacer);
int width = tableLayoutPanel.GetColumnWidths()[column];
// hide the spacer
radioButtonSpacer.Visible = false;
// set the column to the fixed width retrieved before
tableLayoutPanel.ColumnStyles[column].SizeType = SizeType.Absolute;
tableLayoutPanel.ColumnStyles[column].Width = width;
}
And this is the result at runtime:
You could add an invisible dummy label having the same text as the radiobutton. Then, get the length of that label and calculate the correct position of the explanation label.
labelDummy.Text = radioButton1.Text;
labelExplanation.Left = radioButton1.Right - labelDummy.Width;
However, this still appears to be some pixels off, even though I the label's margin to 0, maybe some additional tweaking can fix this. Here's a screenshot to show what I mean. The label's background is green to be able to see the extra margin.
I have a Label on a Windows.Form. I set the AutoSize property on the label to True and I noticed that when I do that, it pads the right hand side with ~5px of white background. I have the Padding property set to [0, 0, 0, 0]. Is there a way to get rid of this?
I would like to get the bounds of the label as close as possible to the text within the label.
There's no way when you use only padding and margin. That's the default behavior.
In the above Window I've set the Padding and Margin to [0,0,0,0]. Those 5 pixels are still there.
If you set FlatStyle = System and AutoSize = False you can get this:
In the above Window you don't have those 5 pixels anymore.
Ok, so FlastStyle = System; AutoSize = false; and then set up a property that will calculate the width like this:
public string LabelText
{
set
{
_label.Text = value;
using (Graphics g = CreateGraphics()) {
_label.Width = (int)g.MeasureString(_label.Text, _label.Font).Width;
}
}
}