C# Form Button Padding - c#

In C# Form i'am trying to make nicely looking Button but i cant get over text "padding" problem.
Problematic Button
Desired Button
I already made my mind of how it should look but i just can't achieve it.
It is supposed to be Flat Button with Black Borders and "Options" text in it (like on second picture).
But some kind of "padding" hides pretty big part of the text.
Changing Font Size kinda helped but i want to perserve Button's ~16px Height and Font that small so it can fit in there is just unreadable.
I've already tried setting Button's Padding property to 0.
I was already thinking about some workaround like overriding OnPaint Event / making multiple controls (like, combine it with label) but i'am worried about performance impact.

Hm I took a shot at it, this is the best I could do.
I used an image with a flat button, with the border providing the grey area beyond the black border. Sort of a workaround. Whatever.
Here is the image for the button background .
And the code for the button:
//
// button1
//
this.button1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.button1.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("button1.BackgroundImage")));
this.button1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.button1.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.button1.FlatAppearance.BorderSize = 4;
this.button1.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Silver;
this.button1.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(128)))), ((int)(((byte)(255)))));
this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.button1.Font = new System.Drawing.Font("Verdana", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button1.ForeColor = System.Drawing.Color.White;
this.button1.ImageAlign = System.Drawing.ContentAlignment.TopCenter;
this.button1.Location = new System.Drawing.Point(94, 124);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(61, 28);
this.button1.TabIndex = 0;
this.button1.Text = "Options";
this.button1.TextAlign = System.Drawing.ContentAlignment.TopLeft;
this.button1.UseVisualStyleBackColor = false;

So i solved it with
class FixedButton : Button {
public string FixedText;
public Point TextOffset;
/*public PointF FixedTextLocation {
get{return new PointF( (float)(Location.X+TextOffset.X), (float)(Location.Y+TextOffset.Y) );}
}*/
protected override void OnPaint(PaintEventArgs e){
base.OnPaint(e);
if(String.IsNullOrEmpty(Text) && !String.IsNullOrEmpty(FixedText) ){
e.Graphics.DrawString(FixedText, Font, new SolidBrush(ForeColor), TextOffset);
}
}
}
EDIT: I found out i need to use TextOffset instead of FixedTextLocation in DrawString / DrawText (which can also be used instead of DrawString).

Related

Winform C# Application, how do I align text inside a button?

I would like to create a button with a plus sign, however when I created the plus sign with the text, it is not aligned from the top of the button as in the image below:
The problem occurs when I try to increase the font size. In default size of the text the plus sign is centered. What can I do to center it in my case?
TextAlign (TopLeft) and UseCompatibleTextRendering didn't work. (Winforms C# Visual Studio 2019)
Note: Text align property did not work because my font size is larger than default it is 36+.
You can use an png of + from resource instead of setting directly the text property of the button.
Example
this.button1.Image = NameSpace1.Properties.Resources.Image2.png;
One way is to set the Button's FlatStyle (https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.buttonbase.flatstyle?view=net-5.0) to System (System.Windows.Forms.FlatStyle.System), if you are Ok to change the FlatStyle.
I created a test application and the combination of font "Microsoft Sans Serif" in size 36pt with button size 32x32px and FlatStyle = FlatStyle.System (as elimad already mentioned) works quite well for me.
namespace ControlTest
{
public class MainForm : Form
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
private Button button1;
public MainForm()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.FlatStyle = System.Windows.Forms.FlatStyle.System;
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 36F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button1.Location = new System.Drawing.Point(16, 16);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(32, 32);
this.button1.TabIndex = 0;
this.button1.Text = "+";
//
// MainForm
//
this.ClientSize = new System.Drawing.Size(157, 92);
this.Controls.Add(this.button1);
this.Name = "MainForm";
this.Text = "ControlTest";
this.ResumeLayout(false);
}
}
}
You can create custom controls and then render the text yourself
https://www.c-sharpcorner.com/UploadFile/f5a10c/creating-custom-controls-in-C-Sharp/
After compiling it should just show up inside the toolbox

How to generate a button exactly NEXT TO another button?

I created separate class with custom button, which i want to be poped when this "newButton" is clicked.
How do i do it manually? so if there is multiple "newButtons" and i want to hide previous apeared Custom Buttons
My problem is that i dont know how to seek locations of the new buttons because it is in flowLayout which puts them automatically.
Button newButton = new Button();
newButton.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(175)))), ((int)(((byte)(241)))));
newButton.FlatAppearance.BorderSize = 2;
newButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
newButton.Font = new System.Drawing.Font("Arial", 10.2F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
newButton.ForeColor = System.Drawing.Color.White;
newButton.Location = new System.Drawing.Point(158, 288);
newButton.Size = new System.Drawing.Size(150, 38);
newButton.UseVisualStyleBackColor = true;
newButton.Text = k.grp;
newButton.Click += (sender, e) => newButton_Click(sender, e);
if (checkinNames.Contains(newButton.Text))
{ }
else
flowLayoutPanel5.Controls.Add(newButton);
checkinNames.Add(k.grp);
You can't achieve it by specific property directly in FlowLayoutPanel. All visual controls placed in it will automatically adjust their positions according to the size of the FlowLayoutPanel.
Here is a workaround via setting button border, backcolor, text and enable.
private void HideButton(Button button)
{
button.BackColor = this.BackColor;
button.Text = string.Empty;
button.TabStop = false;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.Enabled = false;
}
Hope this can help you.

Define style for all labels

I'm working in a c# application in winform.
I saw that there is no Style for element (unlike WPF). But is there a way to simply set all the labels to a specific design ?
Actually I do :
public partial class myControl : UserControl
{
private Color LabelColor = Color.Indigo;
private Color LabelFont = new System.Drawing.Font("Arial",
18F,
System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point,
((byte)(0)));
public myControl()
{
InitializeComponent();
//Set design
designLabels();
}
private void designLabels()
{
List<Label> labelsToStyle = new List<Label>();
labelsToStyle.Add(labelName);
labelsToStyle.Add(labelAge);
labelsToStyle.Add(labelSize);
foreach (Label l in labelsToStyle)
{
l.ForeColor = LabelColor;
l.Font = LabelFont;
l.Dock = DockStyle.Fill;
}
}
}
It works but it doesn't display correctly in designer (I have to run application to see my design). And maybe it exists a simplest way ?
As per my comment the easiest is to create a custom control and use that one in your windows.
here how simple it is. Simply override the label and set in constructor the default value you want
public class DesignLabel : Label
{
public DesignLabel()
{
ForeColor = Color.Indigo;
Font = new System.Drawing.Font("Arial", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
}
}
then compile once and go to your design view, open your toolbox and you will see the "DesignLabel", simply drag drop on the window and that's it. If you change default in the class it will be changed all over the place.
If you want to change the style already at design time so that you can see in in the Form.cs[design] you need to edit the Form.Designer.cs file! But this you have to do label for label by hand. I saved in this example the Font and Color in the project properties (sorry for the german version):
in my example I have 3 Labels. In the Form.Designer.cs file you can add the properties:
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(41, 15);
this.label1.TabIndex = 0;
this.label1.Text = "label1";
this.label1.Font = Properties.Settings.Default.LabelFont;
this.label1.ForeColor = Properties.Settings.Default.LabelColor;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 68);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(41, 15);
this.label2.TabIndex = 1;
this.label2.Text = "label2";
this.label2.Font = Properties.Settings.Default.LabelFont;
this.label2.ForeColor = Properties.Settings.Default.LabelColor;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 122);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(41, 15);
this.label3.TabIndex = 2;
this.label3.Text = "label3";
this.label3.Font = Properties.Settings.Default.LabelFont;
this.label3.ForeColor = Properties.Settings.Default.LabelColor;
The result looks like this:
DISCLAIMER
I would not recommend this approach! Editing the Form.Desginer.cs file is never a good idea! I would stick to the run time changes. If you want to change all Labels just filter the this.Controls for it and foreach through the collection like this:
this.Controls.OfType<Label>().ToList().ForEach(lbl =>
{
lbl.Font = LabelFont;
lbl.ForeColor = LabelColor;
//lbl.Dock = DockStyle.Fill; // uncommented, because I could see only 1 Label
});
The result will be the same.

How to get FlowLayoutPanel.AutoSize to work with FlowBreak

I have a problem with a FlowLayoutPanel and I don't know how to solve it.
I'm placing two FlowLayoutPanels inside another; the second inner flp has 3 buttons inside.
The properties from FlowLayoutPanel child are:
FlowDirection = LeftToRight;
AutoSize = true;
AutoSizeMode = GrowAndShrink;
WrapContents = true;
Now I set for each button the FlowBreak property to true, however the behavior I see is not the one I want, I want the FlowLayoutPanel to shrink to the width of the buttons,
Changing FlowDirection to UpToDown is not an option.
Anyone know why the AutoSize is not working?
this is the code.
//
//FlowLayoutPanel1
//
this.FlowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.FlowLayoutPanel1.Controls.Add(this.FlowLayoutPanel3);
this.FlowLayoutPanel1.Location = new System.Drawing.Point(84, 77);
this.FlowLayoutPanel1.MinimumSize = new System.Drawing.Size(10, 10);
this.FlowLayoutPanel1.Name = "FlowLayoutPanel1";
this.FlowLayoutPanel1.Size = new System.Drawing.Size(308, 265);
this.FlowLayoutPanel1.TabIndex = 0;
//
//FlowLayoutPanel3
//
this.FlowLayoutPanel3.AutoSize = true;
this.FlowLayoutPanel3.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.FlowLayoutPanel3.Controls.Add(this.Button1);
this.FlowLayoutPanel3.Controls.Add(this.Button2);
this.FlowLayoutPanel3.Controls.Add(this.Button3);
this.FlowLayoutPanel3.Location = new System.Drawing.Point(127, 3);
this.FlowLayoutPanel3.MinimumSize = new System.Drawing.Size(10, 10);
this.FlowLayoutPanel3.Name = "FlowLayoutPanel3";
this.FlowLayoutPanel3.Size = new System.Drawing.Size(162, 87);
this.FlowLayoutPanel3.TabIndex = 1;
//
//Button1
//
this.FlowLayoutPanel3.SetFlowBreak(this.Button1, true);
this.Button1.Location = new System.Drawing.Point(3, 3);
this.Button1.Name = "Button1";
this.Button1.Size = new System.Drawing.Size(75, 23);
this.Button1.TabIndex = 0;
this.Button1.Text = "Button1";
this.Button1.UseVisualStyleBackColor = true;
//
//Button2
//
this.FlowLayoutPanel3.SetFlowBreak(this.Button2, true);
this.Button2.Location = new System.Drawing.Point(3, 32);
this.Button2.Name = "Button2";
this.Button2.Size = new System.Drawing.Size(75, 23);
this.Button2.TabIndex = 1;
this.Button2.Text = "Button2";
this.Button2.UseVisualStyleBackColor = true;
//
//Button3
//
this.Button3.Location = new System.Drawing.Point(3, 61);
this.Button3.Name = "Button3";
this.Button3.Size = new System.Drawing.Size(75, 23);
this.Button3.TabIndex = 2;
this.Button3.Text = "Button3";
this.Button3.UseVisualStyleBackColor = true;
It is a bug, it's been around for a very long time. The issue is that the layout engine for FlowLayoutPanel calculates the width of the first row wrong, including the width of the 2nd control, even though it got wrapped to the second row.
The workaround is silly but effective, add a dummy Panel with a Width of 0 after the 1st control. If you are doing this with the designer then drop it first and drag it in the right place, to the right of the 1st control. Then set its Margin to (0, 0, 0, 0) and Size to (0, 0) in the Properties window.
I don't believe the FlowLayoutPanel was designed to do what you're trying to do.
A TableLayoutPanel would probably be better suited. Add a TableLayoutPanel with a single column, and add each button to a row.
Edit: I found a hackish work around. After the first button, create a Panel with the size of 0,0 and the margin of 0,0. Make sure that FlowBreak is set to false.
Edit: You only need to create one panel, after the first button, not one for each.
Not a solution, but a workaround. It looks like you are trying to simulate behavior of TableLayoutPanel by using flow breaks in FlowLayoutPanel. Did you try using TableLayoutPanel instead? According to your screenshots in the comments, it should work perfectly for your needs.

When is Panel.Size updated after adding controls when Panel.AutoSize = true?

I'm creating a GUI in C# using WinForms.
I'm trying to position programaticaly created panels one below the other. As the content of these panel can vary depending on their content, I'm using Panel.AutoSize to let WinForms perform the correct resizing.
The problem is: if I'm using Panel.Height (or Panel.Size.Height) right after populating the Panel, the value returned is always my default value. The resizing do occur, as I can see when launching the app, but I just don't know when.
Here's a simplified version of what I'm doing:
this.SuspendLayout();
int yPos = 0;
foreach (String entry in entries)
{
Panel panel = new Panel();
panel.SuspendLayout();
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
panel.BackColor = System.Drawing.SystemColors.Window; // Allows to see that the panel is resized for dispay
panel.Location = new System.Drawing.Point(0, yPos);
panel.Size = new System.Drawing.Size(this.Width, 0);
this.Controls.Add(panel);
Label label = new Label();
label.AutoSize = true;
label.Location = new System.Drawing.Point(0, 0);
label.MaximumSize = new System.Drawing.Size(panel.Width, 0);
label.Text = entry;
panel.Controls.Add(label);
panel.ResumeLayout(false);
panel.PerformLayout();
yPos += panel.Height; // When breaking here, panel.Height is worth 0
yPos += label.Height; // This works perfectly, label.Height was updated according to the text content when breaking at that point
}
this.ResumeLayout(false);
this.PerformLayout();
So the real question is: How can I get the updated Panel.Size after adding controls to it, to get its proper height value?
Note: I know I can use the TextBox height, but I find it inelegant and impractical, as in my actual code there are more controls in the Panel and I need to use that panel height a few lines below.
What I beleive is happening is that the Size of the Panel will be determined when you do PerformLayout on its Parent. You can make it work like you are wanting by moving the panel's parent SuspendLayout / ResumeLayout code into the Loop.
int yPos = 0;
foreach (String entry in entries)
{
this.SuspendLayout();
Panel panel = new Panel();
panel.SuspendLayout();
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
panel.BackColor = System.Drawing.SystemColors.Window; // Allows to see that the panel is resized for dispay
panel.Location = new System.Drawing.Point(0, yPos);
panel.Size = new System.Drawing.Size(this.Width, 0);
this.Controls.Add(panel);
Label label = new Label();
label.AutoSize = true;
label.Location = new System.Drawing.Point(0, 0);
label.MaximumSize = new System.Drawing.Size(panel.Width, 0);
label.Text = entry;
panel.Controls.Add(label);
panel.ResumeLayout(true);
this.ResumeLayout(true);
yPos += panel.Height; // When breaking here, panel.Height is worth 0
//yPos += label.Height; // This works perfectly, label.Height was updated according to the text content when breaking at that point
}
this.PerformLayout();

Categories