C# Ways to handle dynamic font sizing - c#

What would be a good way to dynamically change the font size on my application? I have many screens with many labels. Those labels are at least inheriting from a common label. The other issue is should I leave the labels to autosize and just use the line breaks in the label so that it can break up? I've switched many of the labels to not be autosized because it was going wide and not wrapping itself.
Currently I have everything set to anchor, etc. and any of the buttons and such will be fine. It's just the font now that needs to be sized dyanmically.
Thanks!

Before InitializeComponent(); in each form's constructor, simply put this.Font = new Font( ... ); as you desire. However, it will only cascade through the controls if you left each control at the default. You can always put a loop after the initialization:
foreach(Control c in this.Controls)
{
if(c is Label) //if you want to change Labels only
c.Font = new Font( ... );
}
If it makes things look weird, change your AutoScaleMode and related properties.
To address the question of how to handle wrapping the label text, use Label1.AutoSize = true, and simply set Label1.MaximumSize = new Size(x, 0);, where x is your maximum width.
That all said, if you're going to be dynamically scaling things often, you really should look into using WPF instead of WinForms. It has more ability to handle these types of tasks automatically.

You can save the font size as an integer in the application settings.
Then your application will remember its font state upon start when you get the font size.
Properties.Settings.Default.FontSize = 3;
Properties.Settings.Default.Save();
Then as said above use a foreach loop.
foreach(Control c in this.Controls)
{
c.Font = new Font( .. );
// if(c is Panel)
// {
// foreach(Control d in c.Controls)
// {
// d.Font = new Font( .. );
// }
}
}

Related

Resizing and Collapsing label in winforms

first time poster, sorry if something isn't as it should be.
I'm new to Winforms and am trying to build a simple application that will display multiple features of an item (like Size, Composition, etc.). Each Characteristic has a Name, can have a Descritpion, and some can have sub-characteristics (having also a name and sometimes a descritpion).
I want to display them one under the other, with the Name of the feature on a blue background that span the whole width of the container, with the description underneath. The name will be (or have) a button (or similar) that when clicked collapse or expand the description. This must be created at run time because I don't know how many feature an object has until the user generate it.
The issues I'm running in are that either I can't span the blue background the whole width of the container (if using a FlowLayoutPanel), or I have some issue with the Description text being not the right size (either it wraps but is too big, or it doesn't wrap and then I can't see the whole text).
Some things are fixed, mainly the number of main sections (like Size, Composition, Weather, etc.), so I can prepare the skeleton before runtime.
The closest i've been to making it work gives this. This issue here is that the height of the Panel which the description label is embded in is fixed, and if I put in in Autosize, the text don't show (probably because the label is in Fill dock style). Just as information, this is what it looks like when collapsed (this is indeed what I'm looking for)
I know some library exists with collapsible panels, but I'd rather try to make it work without external libraries. Thanks in advance for any help !
This is the code that produces the results in the screenshots :
Panel SizeDescrPanel = new Panel();
SizeDescrPanel.Dock = DockStyle.Top;
//SizeDescrPanel.AutoSize = true;
SizeDescrPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
SizeDescrPanel.BackColor = Color.Bisque;
SizePanel.Controls.Add(SizeDescrPanel);
Label SizeDescrLbl = new Label();
SizeDescrLbl.Text = Lorem;
SizeDescrLbl.AutoSize = false;
SizeDescrLbl.Dock = DockStyle.Fill;
SizeDescrLbl.BackColor = Color.BurlyWood;
SizeDescrPanel.Controls.Add(SizeDescrLbl);
/*using(Graphics g = CreateGraphics())
{
SizeF size = g.MeasureString(SizeDescrLbl.Text, SizeDescrLbl.Font, SizePanel.Width);
SizeDescrPanel.Height = (int) Math.Ceiling(size.Height);
}*/
Panel SizeNamePanel = new Panel();
SizeNamePanel.Dock = DockStyle.Top;
SizeNamePanel.BackColor = Color.Cyan;
SizeNamePanel.AutoSize = true;
SizePanel.Controls.Add(SizeNamePanel);
Button SizeNameBtn = new Button();
SizeNameBtn.Text = "<Size Name> ..." + SizeDescrLbl.Height;
SizeNameBtn.TextAlign = ContentAlignment.MiddleLeft;
SizeNameBtn.FlatStyle = FlatStyle.Flat;
SizeNameBtn.AutoSize = true;
SizeNamePanel.Controls.Add(SizeNameBtn);
SizeNameBtn.Click += delegate { HideShowPanel(SizeDescrPanel); };
It,s a test project, so later I'll put that in different methods. What isn't shown here :
I have a main panel set to Fill containing everything.
The text "SIZE" is a label set to Top
Under it is another Panel (SizePanel) that is set to Top and Autosize is at True. This is the Panel inside which I'm puttin my size name and my size description. If I had a subfeature, it would be included (ideally) inside descritpion with the same configuration (button expanding/collapsing the descritpion of the SubFeature)

what is the correct way to change the directions of Winforms controls

what is the correct way to change the directions/position of Winforms controls, so it supports for example languages that start from Right-To-Left, programmatically so it's one form , one design, but when I switch the languages , it's switches it's position and directions.
EDIT: after checking #vcsjones answer ;
it worked with the layout ,but the controls position didn't change. here an image
I have tried nested loops to change the direction , but it didn't work :
if (Constantes["RightToLeft"] != null && Constantes["RightToLeft"].ToLower().Equals("true"))
{
RightToLeft = RightToLeft.Yes;
RightToLeftLayout = true;
foreach (Control c in base.Controls)
{
foreach (Control item in c.Controls)
{
foreach (Control i in item.Controls)
{
i.RightToLeft = RightToLeft.Yes;
}
item.RightToLeft = RightToLeft.Yes;
}
c.RightToLeft = RightToLeft.Yes;
}
}
WinForms can already handle this reasonably well using the right-to-left unicode character. For example:
button1.Text = "\u202EHello World";
This will display the button's text way like this:
Most likely, your text of your application will be done using localization files. If you use the RTL unicode character in the strings of your localization file, then you do not need to do any special coding - that unicode character is handled by WinForms already.
Setting these two properties will also switch around the control layout of the window:
RightToLeftLayout = true;
RightToLeft = RightToLeft.Yes;

Multiline TextBox doesn't display cursor when resized

I am creating a custom control, a custom looking TextBox designed to only allow numeric input. It consists of a plain control, with a TextBox as one of it's properties:
class NumericControl : Control
{
private TextBox text;
//rest of the code
}
This TextBox is drawn inside of the control. It all works very nicely, except that when you press enter it makes this horrible DING noise. To fix this, I thought I'd make the TextBox multiline. However, when I do this, because the font size of the TextBox is changed to resize it, the blinking cursor inside the TextBox appears to disappear, which is a problem because then you can't tell by looking at it whether or not it has focus. If I don't change the font size of the TextBox, the cursor appears and acts normally, however I need the font size to change based on the height of the control, otherwise it doesn't look any good.
The code setting the TextBox properties is as follows, and resides within the constructor of my control:
text = new TextBox();
text.AutoSize = false;
text.Left = 10;
text.Top = 2;
text.Text = "0";
text.Multiline = true;
text.BorderStyle = BorderStyle.None;
text.TextChanged += text_TextChanged;
text.LostFocus += text_LostFocus;
this.Controls.Add(text);
The font size is changed inside the OnPaint event, and looks like this:
text.BackColor = Enabled ? Background : SystemColors.Control; //Fixes an issue I had with disabled TextBox BackColor being able to be changed
text.Font = new Font(TextBox.DefaultFont.FontFamily, (float)(rc.Height * 0.7 - 2), FontStyle.Regular);
text.Width = this.Width - 21;
text.Height = this.Height - 4;
How do I both resize the textbox (and font size), and make it multiline while retaining the blinking cursor when it has focus?

Displaying different controls on different button events

I'm not entirely sure this is possible using C# and Windows Forms, but here goes:
I want to split my window into two. On the left is a series of buttons arranged vertically.
On clicking each button, the stuff on the right side has to change. I want different controls displaying for different buttons clicks.
(Kind of like web browser tabs, just on the left pane instead of on top)
I'm guessing I have to use split container, but most of the split container tutorials I've seen have them using the same control on the right, just with different data displaying every time something is clicked.
I hope I'm being clear with what I need. Any help or pointers would be greatly appreciated.
You can get the job done with a TabControl, as explained on MSDN. It shows an example on how to put the tabs on the right side but switching "right" with "left" should provide the behavior you seek.
I tried it in a WinForms app and it seems to work fine, despite the ugly colors used in the example... The following takes ~30 secs to implement.
Quoting from the link:
To display right-aligned tabs
Add a TabControl to your form.
Set the Alignment property to Right. if you set Left it goes on the left no problem
Set the SizeMode property to Fixed, so that all tabs are the same width.
Set the ItemSize property to the preferred fixed size for the tabs. Keep in mind that the ItemSize property behaves as though the tabs were on top, although they are right-aligned. As a result, in order to make the tabs wider, you must change the Height property, and in order to make them taller, you must change the Width property.
Set the DrawMode property to OwnerDrawFixed.
Define a handler for the DrawItem event of TabControl that renders the text from left to right.
And this is the code (converted to C# since MSDN only shows VB):
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _TextBrush = default(Brush);
// Get the item from the collection.
TabPage _TabPage = tabControl1.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _TabBounds = tabControl1.GetTabRect(e.Index);
if ((e.State == DrawItemState.Selected))
{
// Draw a different background color, and don't paint a focus rectangle.
_TextBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.Gray, e.Bounds);
}
else
{
_TextBrush = new System.Drawing.SolidBrush(e.ForeColor);
e.DrawBackground();
}
// Use our own font.
Font _TabFont = new Font("Arial", 10, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _StringFlags = new StringFormat();
_StringFlags.Alignment = StringAlignment.Center;
_StringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_TabPage.Text, _TabFont, _TextBrush, _TabBounds, new StringFormat(_StringFlags));
}
Just put your content in the TabControl and you're (sorta) good to go.
It is possible and one way is to create new user controls for each of the right-hand interfaces. When you click a button it can load a new instance of one of these controls into the right-hand panel:
if (splitContainer.Panel2.Controls.Count > 0)
{
splitContainer.Panel2.Controls[0].Dispose(); // Edit
splitContainer.Panel2.Controls.Clear();
}
splitContainer.Panel2.Controls.Add(new RightHandControl());
This seems to work when I have tried in the past.
EDIT: As has been pointed out in the comments, this example does not properly dispose of the user controls when cleared! To solve this, call Dispose() prior to clearing the list.
The exact layout and the controls you need will be up to you.This is meant as an simplistic starting point to make you see the options you have(here of course 1 option is provided):
1) add splitcontainer to the form.
2) add flowLayoutPanel to left panel of splitcontainer and dock in parent container
3) since you asked for vertical layout set FlowDirection on the
flowLayoutPanel to topdown
4) in my example i used 6 buttons and named them(instead could be
their text) btncontrol1,2,3...so on, do the same.
5) set all buttons click handler to the same(in this case
buttons_Click)
6) copy this code and paste
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttons_Click(object sender, EventArgs e)
{
Button b = sender as Button;
switch (b.Name)
{
//of course after each ClearPanel what i do is only for
//demonstration.
case "btnControl1":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new ListBox());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl2":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new RadioButton());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl3":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new Button());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl4":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new DateTimePicker());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl5":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new DataGridView());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl6":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new TextBox());
splitContainer1.Panel2.ResumeLayout();
break;
default:
break;
}
}
private void ClearPanel()
{
if (splitContainer1.Panel2.HasChildren)
{
foreach (Control c in splitContainer1.Panel2.Controls)
{
c.Dispose();
}
splitContainer1.Panel2.Controls.Clear();
}
}
}
Hope it help you.

Change the Textbox height?

How do I change the height of a textbox ?
Neither of the below work:
this.TextBox1.Size = new System.Drawing.Size(173, 100);
or
this.TextBox1.Size.Height = 100;
I wanted to be able to change the single line text box height to fit a font size on it without using multi-line if possible.
Go into yourForm.Designer.cs
Scroll down to your textbox. Example below is for textBox2 object.
Add this
this.textBox2.AutoSize = false;
and set its size to whatever you want
this.textBox2.Size = new System.Drawing.Size(142, 27);
Will work like a charm - without setting multiline to true, but only until you change any option in designer itself (you will have to set these 2 lines again).
I think, this method is still better than multilining. I had a textbox for nickname in my app and with multiline, people sometimes accidentially wrote their names twice, like Thomas\nThomas (you saw only one in actual textbox line). With this solution, text is simply hiding to the left after each char too long for width, so its much safer for users, to put inputs.
There are two ways to do this :
Set the textbox's "multiline" property to true, in this case you don't want to do it so;
Set a bigger font size to the textbox
I believe it is the only ways to do it; the bigger font size should automatically fit with the textbox
You can set the MinimumSize and/or the MaximumSize properties of the textbox. This does not affect the size immediately, but when you resize the textbox in the forms designer, the size will automatically be adjusted to satisfy the minimum/maximum size constraints. This works even when Multiline is set to false and does not depend on the font size.
Just found a great little trick to setting a custom height to a textbox.
In the designer view, set the minimumSize to whatever you desire, and then completely remove the size setting. This will cause the designer to update with the new minimum settings!
set the minimum size property
tb_01.MinimumSize = new Size(500, 300);
This is working for me.
Try the following :)
textBox1.Multiline = true;
textBox1.Height = 100;
textBox1.Width = 173;
Steps:
Set the textboxes to multiline
Change the height
Change the font size. (so it fit into the big textboxes)
Set the textboxes back to non-multiline
public partial class MyTextBox : TextBox
{
[DefaultValue(false)]
[Browsable(true)]
public override bool AutoSize
{
get
{
return base.AutoSize;
}
set
{
base.AutoSize = value;
}
}
public MyTextBox()
{
InitializeComponent();
this.AutoSize = false;
}
}
May be it´s a little late. But you can do this.
txtFoo.Multiline = true;
txtFoo.MinimumSize = new Size(someWith,someHeight);
I solved it that way.
AutoSize, Minimum, Maximum does not give flexibility. Use multiline and handle the enter key event and suppress the keypress. Works great.
textBox1.Multiline = true;
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.Handled = true;
e.SuppressKeyPress = true;
}
}
You can put it inside a panel that has the same back color with your desired height. This way has this advantage that the text box can center horizontally, which is not provided by other solutions.
You can make it even more natural by using the following methods
private void textBox1_Enter(object sender, EventArgs e)
{
panelTextBox.BorderStyle = BorderStyle.FixedSingle;
}
private void textBox1_Leave(object sender, EventArgs e)
{
panelTextBox.BorderStyle = BorderStyle.None;
}
The Simplest Way to do that
Right click on the TextBox.
Go to properties.
Set Multiline = True.
Now you will be able to resize the TextBox vertically as you wish.
for me, the best approach is remove border of the textbox, and place it inside a Panel, which can be customized as you like.
The following code added in your constructor after calling InitializeComponent() will make it possible to programmatically set your text box to the correct height without a) changing the Multiline property, b) having to hardcode a height, or c) mucking with the Designer-generated code. It still isn't necessarily as clean or nice as doing it in a custom control, but it's fairly simple and robust:
if (txtbox.BorderStyle == BorderStyle.None)
{
txtbox.BorderStyle = BorderStyle.FixedSingle;
var heightWithBorder = txtbox.ClientRectangle.Height;
txtbox.BorderStyle = BorderStyle.None;
txtbox.AutoSize = false;
txtbox.Height = heightWithBorder;
}
I decided to make it cleaner and easier to use by putting it in a static class and make it an extension method on TextBox:
public static class TextBoxExtensions
{
public static void CorrectHeight(this TextBox txtbox)
{
if (txtbox.BorderStyle == BorderStyle.None)
{
txtbox.BorderStyle = BorderStyle.FixedSingle;
var heightWithBorder = txtbox.ClientRectangle.Height;
txtbox.BorderStyle = BorderStyle.None;
txtbox.AutoSize = false;
txtbox.Height = heightWithBorder;
}
}
}
Some of you were close but changing designer code like that is annoying because you always have to go back and change it again.
The original OP was likely using an older version of .net because version 4 autosizes the textbox height to fit the font, but does not size comboboxes and textboxes the same which is a completely different problem but drew me here.
This is the problem I faced when placing textboxes next to comboboxes on a form. This is a bit irritating because who wants two controls side-by-side with different heights? Or different fonts to force heights? Step it up Microsoft, this should be simple!
I'm using .net framework 4 in VS2012 and the following was the simplest solution for me.
In the form load event (or anywhere as long as fired after InitializeComponent): textbox.AutoSize = false Then set the height to whatever you want. For me I wanted my text boxes and combo boxes to be the same height so textbox.height = combobox.height did the trick for me.
Notes:
1) The designer will not be affected so it will require you to start your project to see the end result, so there may be some trial and error.
2) Align the tops of your comboboxes and textboxes if you want them to be aligned properly after the resize because the textboxes will grow down.
This is what worked nicely for me since all I wanted to do was set the height of the textbox. The property is Read-Only and the property is in the Unit class so you can't just set it. So I just created a new Unit and the constructor lets me set the height, then set the textbox to that unit instead.
Unit height = txtTextBox.Height;
double oldHeight = height.Value;
double newHeight = height.Value + 20; //Added 20 pixels
Unit newHeightUnit = new Unit(newHeight);
txtTextBox.Height = newHeightUnit;
You can make multiline : false and then just change the text size on the text box then the height will automatically increment
you can also change you can also change MinimumSize
So after having the same issue with not being able to adjust height in text box, Width adjustment is fine but height never adjusted with the above suggestions (at least for me), I was finally able to take make it happen. As mentioned above, the issue appeared to be centered around a default font size setting in my text box and the behavior of the text box auto sizing around it. The default font size was tiny. Hence why trying to force the height or even turn off autosizing failed to fix the issue for me.
Set the Font properties to the size of your liking and then height change will kick in around the FONT size, automatically. You can still manually set your text box width. Below is snippet I added that worked for me.
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(60,300)
$textBox.Size = New-Object System.Drawing.Size(600,80)
$textBox.Font = New-Object System.Drawing.Font("Times New Roman",18,[System.Drawing.FontStyle]::Regular)
$textBox.Form.Font = $textbox.Font
Please note the Height value in '$textBox.Size = New-Object System.Drawing.Size(600,80)' is being ignored and the FONT size is actually controlling the height of the text box by autosizing around that font size.
All you have to do is enable the multiline in the properties window, put the size you want in that same window and then in your .cs after the InitializeComponent put txtexample.Multiline = false; and so the multiline is not enabled but the size of the txt is as you put it.
InitializeComponent();
txtEmail.Multiline = false;
txtPassword.Multiline = false;
I think this should work.
TextBox1.Height = 100;

Categories