Set Text to the lowest right edge of a label? - c#

Easy example, lets say I'm creating a Label like that:
Label label = new Label();
label.Text = "Hello" + "20.50";
label.Width = 250;
label.Height = 100;
panel1.Controls.Add(label);
How could I say that the "20.50" should appear in the lowest right edge of the label?
For clarity I made a little example in word:
How could I achieve this? Any help appreciated!

There's no built-in support for this with a Label control. You'll need to inherit from Label to create a custom control, and then write the painting code yourself.
Of course, you'll also need some way to differentiate between the two strings. The + sign, when applied to two strings, is concatenation. The two strings are joined together by the compiler, so all you get is this: Hello20.50. You will either need to use two separate properties, each with their own strings, or insert some sort of delimiter in between the two strings that you can use to split them apart later. Since you're already creating a custom control class, I'd go with the separate properties—much cleaner code, and harder to get wrong.
public class CornerLabel : Label
{
public string Text2 { get; set; }
public CornerLabel()
{
// This label doesn't support autosizing because the default autosize logic
// only knows about the primary caption, not the secondary one.
//
// You will either have to set its size manually, or override the
// GetPreferredSize function and write your own logic. That would not be
// hard to do: use TextRenderer.MeasureText to determine the space
// required for both of your strings.
this.AutoSize = false;
}
protected override void OnPaint(PaintEventArgs e)
{
// Call the base class to paint the regular caption in the top-left.
base.OnPaint(e);
// Paint the secondary caption in the bottom-right.
TextRenderer.DrawText(e.Graphics,
this.Text2,
this.Font,
this.ClientRectangle,
this.ForeColor,
TextFormatFlags.Bottom | TextFormatFlags.Right);
}
}
Add this class to a new file, build your project, and then drop this control onto your form. Make sure to set both the Text and Text2 properties, and then resize the control in the designer and watch what happens!

Here is what you need, a custom label:
public class CustomLabel : Label
{
public CustomLabel()
{
TopLeftText = BottomRightText = "";
AutoSize = false;
}
public string TopLeftText {get;set;}
public string BottomRightText {get;set;}
protected override void OnPaint(PaintEventArgs e)
{
using (StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Near})
{
using(SolidBrush brush = new SolidBrush(ForeColor)){
e.Graphics.DrawString(TopLeftText, Font, brush, ClientRectangle, sf);
sf.LineAlignment = StringAlignment.Far;
sf.Alignment = StringAlignment.Far;
e.Graphics.DrawString(BottomRightText, Font, brush, ClientRectangle, sf);
}
}
}
}
//use it:
//first, set its size to what you want.
customLabel1.TopLeftText = house.Name;
customLabel2.BottomRightText = house.Number;

Related

WinForms: Changing ForeColor of Selected item in ListView

I am setting the ForeColor of all items in my ListView to a different color, but this get's overrided when the item is selected (changes to Black again; changes back to custom color on deselection).
I want my items to retain my custom color, even in selection.
I'm basically asking the same question that was asked here 7 years ago, and doesn't seem to have any satisfactory answer.
I tried searching in SO and elsewhere, and no luck. The only solution provided so far is to draw the whole thing (the DrawItem method), which I gave a try but is ridiculously complicated for such a petty requirement...
Is this the only way? Say it ain't so.
Enable your ListView OwnerDraw mode, then subscribe its DrawItem and DrawColumnHeader events.
If your design requires it, also subcribe the DrawSubitem event.
At this point, you can draw anything in the related areas of your ListView.
In the example, I've painted a little symbol in the Header area.
The Header text needs to be painted too.
If the Background color doesn't change (same as in design mode), you just need to use the DrawListViewItemEventArgs e parameter function e.DrawBackground();
If not, use e.Graphics.FillRectangle() to color the Item area, defined by e.Bounds.
The Item Text is drawn using e.Graphics.DrawString().
The item Text is e.Item.Text, the text area is defined by e.Bounds again.
If you don't need any specific details/settings for the item's text, you can simply use e.DrawText();, which uses the default properties (defined at design-time).
Here, the item color complex logic is that the color is specified inside the item text. Could be anything else. The item tag, its Index position, a List<Parameters>, you name it.
This is how it might look like:
(I added e.Graphics.TextRenderingHint = [] to show how you can control the quality of the rendered text. e.Graphics.TextContrast can be also used to enhance the contrast).
Note: this code sample only draws a generic image, if the ListView has an ImageList. You should also verify whether the SmallIcon/LargeIcon ImageLists are defined and draw the related Image in the specified size. It's the same procedure, though.
protected void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.Item.UseItemStyleForSubItems = true;
int imageOffset = 0;
Rectangle rect = e.Item.Bounds;
bool drawImage = !(e.Item.ImageList is null);
Color itemColor = Color.FromName(e.Item.Text.Substring(e.Item.Text.LastIndexOf(" ") + 1));
using (var format = new StringFormat(StringFormatFlags.FitBlackBox)) {
format.LineAlignment = StringAlignment.Center;
if (drawImage) {
imageOffset = e.Item.ImageList.ImageSize.Width + 1;
rect.Location = new Point(e.Bounds.X + imageOffset, e.Item.Bounds.Y);
rect.Size = new Size(e.Bounds.Width - imageOffset, e.Item.Bounds.Height);
e.Graphics.DrawImage(e.Item.ImageList.Images[e.Item.ImageIndex], e.Bounds.Location);
}
if (e.Item.Selected) {
using (var bkgrBrush = new SolidBrush(itemColor))
using (var foreBrush = new SolidBrush(e.Item.BackColor)) {
e.Graphics.FillRectangle(bkgrBrush, rect);
e.Graphics.DrawString(e.Item.Text, e.Item.Font, foreBrush, rect, format);
}
e.DrawFocusRectangle();
}
else {
//e.DrawDefault = true;
using (var foreBrush = new SolidBrush(itemColor)) {
e.Graphics.DrawString(e.Item.Text, e.Item.Font, foreBrush, rect, format);
}
}
}
}
// Draws small symbol in the Header beside the normal Text
protected void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawBackground();
string extra = (e.ColumnIndex == 1) ? (char)32 + "\u2660" + (char)32 : (char)32 + "\u2663" + (char)32;
using (var brush = new SolidBrush(e.ForeColor)) {
e.Graphics.DrawString(extra + e.Header.Text, e.Font, brush, e.Bounds, StringFormat.GenericTypographic);
}
}

Customizing default inputs?

I wonder if it's possible to customize my C# application (winforms) to get a better design, I made a PSD (photoshop document) so I can generate png jpeg... pictures if I need them.
Example of a form like the one I want :
Indeed as it was pointed out in the comments, it is easy to use WPF (indows Presentation Foundation) to achieve that result, but if you really need that it must be made in windows forms I can help you with that...
ControlBox and Border
It seens that your form does not have a control box (minimize, maximize and close buttons)
to achieve that you can set
form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
I'm not sure if that galaxy behind your form is part of the application so i'll be considering that it is not
To achieve that irregular shape of the form we have to do a workaround here
Irregular Shape of the Form
we are going to set a Color to TransparentKey, so everything in the form in that specific color will be transparent, like it does not exists (if you click in that part it will go into de desktop or whatever application you have behind in your form)
So let's use a specific color which we will probably dont use in the form
form.TransparencyKey = Color.FromArgb(111, 111, 111); //You can do it by the editor
So in order to make that white part we are going to use an Panel and a PictureBox outsite of the Panel trying to copy the shape of your image
Stylized Inputs
To make it easier and reusable I'm going to make a userControl in this one
the usercontrol will have
a Panel called HighLightPanel, its dock property will be set to Fill
a Panel called BackColorPanel, it will be inside the HighLightPanel
a PictureBox called InputPicture, its dock property will be set to Left, it will be inside BackColorPanel and its acessor will be public
a TextBox called TextBox, its dock property wil be set to fill, it will be inside BackColorPanel, the BorderStyle Property set to None, you should set the size and font you most desize in this one, I'm going to use Segoe UI; 15,75pt and its acessor will be public
Now we have to make some properties in our UserControl to make it work without work in other controls
First in the SizeChanged event of the HighLightPanel we will make the BackColorPanel be exacly two points smaller in every direction and its position to 1;1 so we can see the HighLightPanel
private void HighlightPanel_SizeChanged(object sender, EventArgs e)
{
this.BackColorPanel.Size = new Size(
HighlightPanel.Width - 2,
HighlightPanel.Height - 2);
}
Now we will create two propertys to handle the Highlight Color
public Color HighlightBorderColor { get; set; }
public Color NonHighlightBorderColor { get; set; }
And in the Enter and Leave Property of our TextBox we are going to change the HighlightPanel
private void TextBox_Enter(object sender, EventArgs e)
{
HighlightPanel.BackColor = HighlightBorderColor;
}
private void TextBox_Leave(object sender, EventArgs e)
{
HighlightPanel.BackColor = NonHighlightBorderColor;
}
So now every time the user enter the Input it will appear that the Input has an Border in the specified Color
Now to enhance its usability to developers we will make some wrappers in its controls to be easier change property of child controls in the editor
public Image InputImage
{
get { return InputPicture.Image; }
set { InputPicture.Image = value; }
}
public PictureBoxSizeMode InputImageLayout
{
get { return InputPicture.SizeMode; }
set { InputPicture.SizeMode = value; }
}
public char PasswordCharacter
{
get { return TextBox.PasswordChar; }
set { TextBox.PasswordChar = value; }
}
public bool ShowInputImage
{
get { return InputPicture.Visible; }
set { InputPicture.Visible = value; }
}
In the InputImage set the picture you want for the User and the Key
Insert the two controls in the position you like
Position of the Form
if you want your form to be moveable without the border you will have to use this snippet, it is more easy in WPF
#region MoveForm
Point LastPoint;
bool ShouldMove;
private void form_MouseDown(object sender, MouseEventArgs e)
{
LastPoint = e.Location;
ShouldMove = true;
this.TransparencyKey = Color.FromArgb(111, 111, 111);
}
private void form_MouseUp(object sender, MouseEventArgs e)
{
ShouldMove = false;
}
private void form_MouseMove(object sender, MouseEventArgs e)
{
if (ShouldMove)
{
this.Location = new Point(
this.Location.X - LastPoint.X + e.X,
this.Location.Y - LastPoint.Y + e.Y);
}
}
#endregion
If you need a lot of special graphics effects learning WPF will indeed be a sound investement.
If all you want is that login screen, it is trivial in Winforms and doesn't take any horrible hacks as you've been told..
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.BackColor = System.Drawing.Color.LavenderBlush;
this.TransparencyKey = System.Drawing.Color.LavenderBlush;
this.ControlBox = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Text= "";
These seven lines are all it takes for a form to be transparent. I copied them from the Designer code; you can simply set the 7 Properties in the property grid.
Now add a panel, dock it to the bottom and give it the right color; add a picturebox and your other controls and you are set.
To create the two input groups you also need just a few regular controls and only a few simple lines of code:
You place one Panel, BorderStyle = FixedSingle; and add a Label and a TextBox to it. The Label has AutoSize = False; and both ImageAlign and TextAlign are set to MiddleLeft. You assign an image to the Label's Image and prefix the Text with enough blanks to not overlap. Obviously you should define a PasswordChar for the 2nd TextBox. Now all you need is to script the Enter and Leave events to change the BackColor of the respective Panels between, say SystemColors.Control and SystemColors.MenuHighlight. Size the Labels to almost fill the Panels and you are done. Less code than the WPF version, I'd bet.
If you need such input an controls again and again, simply create Usercontrols for each type you need!
Here is an example of the limits you will hit: Wouldn't it be nice to add a dropshadow effect to the image? It is doable in Winforms. But it would involve painting that effect; this would take at least 15 or 20 lines of involved code instead of simply turning the effect on with (estimated) 1-3 simple lines.
Do you need any nice hover effects? Not easy, to say the least..
These limits will be all over the place, so it really depends on how fancy your requirements will get.
Maybe you should use this example as a starter to compare the two techniques and to warm you up to WPF?

Drawing Rectangle as TextBox Border

I'm using validation methods for my textboxes in a class named Validators. I'm trying also to draw a rectangle on the textbox which failed to validate.
Im using this code:
private void TextBoxStyle(TextBox textBox)
{
Graphics graphics = textBox.CreateGraphics();
Pen redPen = new Pen(Color.Red);
graphics.DrawRectangle(redPen, textBox.Location.X, textBox.Location.Y, textBox.Width, textBox.Height);
}
/// <summary>
/// Validates TextBoxes for string input.
/// </summary>
public bool ValidateTextBoxes(params TextBox[] textBoxes)
{
foreach (var textBox in textBoxes)
{
if (textBox.Text.Equals(""))
{
Graphics graphics = textBox.CreateGraphics();
Pen redPen = new Pen(Color.Red);
graphics.DrawRectangle(redPen, textBox.Location.X, textBox.Location.Y, textBox.Width, textBox.Height);
return false;
}
}
return true;
}
The problem is... the rectangles wont show. Am I doing something wrong with the code ? If yes, help please.
A couple potential problems I see:
You get the Graphics object for the text box but use the textbox's offset in the form to do the drawing. Net result: the rectangle is translated outside the visible area of the textbox. Try using the location (0,0).
You draw the rectangle as wide as the textbox. Net result: right and bottom edges won't be visible. You should subtract the width of the pen from these values.
While you're at it, check out the ErrorProvider class. It may just take care of your needs off-the-shelf.
write a user control
public partial class UserControl1 : UserControl
{
private string text;
private bool isvalid = true;
public string Text
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
public bool isValid
{
set
{
isvalid = value;
this.Refresh();
}
}
TextBox textBox = new TextBox();
public UserControl1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(UserControl1_Paint);
this.Resize += new EventHandler(UserControl1_Resize);
textBox.Multiline = true;
textBox.BorderStyle = BorderStyle.None;
this.Controls.Add(textBox);
}
private void UserControl1_Resize(object sender, EventArgs e)
{
textBox.Size = new Size(this.Width - 3, this.Height - 2);
textBox.Location = new Point(2, 1);
}
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
if (isvalid)
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, Color.Black, ButtonBorderStyle.Solid);
else
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
}
update:
just added the isvalid property
you can put properties to show the border or not. if the input is valid show normal border and if the control input is invalid show the red border.
Anything drawn directly onto the TextBox will disappear as soon as the TextBox control is invalidated in some way.
A correct approach is to add a User Control to your project and add a TextBox on its canvas. Leave a little border around it.
You can now simply color the background of the user control's canvas red when needed and it will look like a border drawn around the TextBox.
You can add code directly to the user control to validate it whenever the text changes. That way, you only have to write code once and just add as many TextBoxes as you need to your forms or pages.
You shouldn't paint on a control simply from somewhere. The build in painting will override it on the next occasion. The Control has a paint event where you should paint. That will be used whenever painting is needed.
In your validate method you should just store the result of the validation somewhere so that it can be used in the paint event and call Invalidate() so that a repainting is enforced.
// You may use this
Label lblHighlight = new Label ();
Rectangle rc = new Rectangle(this.Left - 2, this.Top - 2, this.Width + 4, this.Bottom - this.Top + 4);
this.Parent.Controls.Add(lblHighlight);
lblHighlight.Bounds = rc;
lblHighlight.BackColor = "Red";

Can StatusStrip automatically change its height depending on its items' size?

I've got a statusstrip with a number of items. One of them is a ToolStripStatusLabel with Spring = True.
When the text of the label is too long, one can't see it.
Is it possible to make the statusstrip become higher and show whole text in multiline?
This is an interesting problem....I tried a couple of things but no success...basicall the ToolStripStatusLabel is very limited in capability.
I ended up trying a hack that gives the result you want but am not sure even I would recommend this unless of course this is absolutely necessary...
Here's what I have got...
In the properties of your StatusStrip set AutoSize = false, this is to allow the StatusStrip to be resized to accommodate multiple lines. I am assuming statusStrip called ststusStrip1 containing label called toolStripStatusLabel1.
At form Level declare a variable of TextBox type:
TextBox txtDummy = new TextBox();
At Form Load set some of its properties:
txtDummy.Multiline = true;
txtDummy.WordWrap = true;
txtDummy.Font = toolStripStatusLabel1.Font;//Same font as Label
Handle the paint event of the toolStripStatusLabel1
private void toolStripStatusLabel1_Paint(object sender, PaintEventArgs e)
{
String textToPaint = toolStripStatusLabel1.Tag.ToString(); //We take the string to print from Tag
SizeF stringSize = e.Graphics.MeasureString(textToPaint, toolStripStatusLabel1.Font);
if (stringSize.Width > toolStripStatusLabel1.Width)//If the size is large we need to find out how many lines it will take
{
//We use a textBox to find out the number of lines this text should be broken into
txtDummy.Width = toolStripStatusLabel1.Width - 10;
txtDummy.Text = textToPaint;
int linesRequired = txtDummy.GetLineFromCharIndex(textToPaint.Length - 1) + 1;
statusStrip1.Height =((int)stringSize.Height * linesRequired) + 5;
toolStripStatusLabel1.Text = "";
e.Graphics.DrawString(textToPaint, toolStripStatusLabel1.Font, new SolidBrush( toolStripStatusLabel1.ForeColor), new RectangleF( new PointF(0, 0), new SizeF(toolStripStatusLabel1.Width, toolStripStatusLabel1.Height)));
}
else
{
toolStripStatusLabel1.Text = textToPaint;
}
}
IMP: Do not assign the text property of your label instead put it in Tag we would use it from Tag
toolStripStatusLabel1.Tag = "My very long String";

sub-class of Control that combines multiple Controls

I'm trying to extend TextBox to add a Label to the left of it and treat it as one Control so I don't have to keep track of both of their sizes, locations, etc.
I've created a TextBoxWithLabel class that extends Control and has TextBox and Label fields, but I'm not really sure what to do for onPaint() - do I have to tell it to manually draw both items? If so, how? I'm guessing the default inherited behaviour doesn't go so far as 'check if I contain any child Controls and if I do, draw them'...
Is this even the best way to do it? I previously had my class extend TextBox and just added the Label field, but of course that didn't get added to the Panel containing the TextBoxWithLabel and so wasn't drawn.
Any suggestions or pokes in the right direction appreciated.
Thanks,
Alex
The typical approach here is a UserControl in which you put both the label and the text box. It is painful though, you have to add a lot of the properties and events of the text box to the user control so it at least resembles a text box. Ugly boilerplate code.
Another way to do it is to make a custom text box that sneaks in a label control on the parent. That completely behaves like a TextBox without having to do any work. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form. Set the Description property to the text you want to see appear in the label.
using System;
using System.Drawing;
using System.Windows.Forms;
class MyTextBox : TextBox {
private Label label;
public MyTextBox() {
label = new Label();
label.AutoSize = true;
label.Font = this.Font;
label.Location = this.Location;
label.Resize += new EventHandler(label_Resize);
}
protected override void OnParentChanged(EventArgs e) {
// Keeps label on the same parent as the text box
base.OnParentChanged(e);
label.Parent = this.Parent; // NOTE: no dispose necessary
}
private void moveLabel() {
// Keep label right-aligned to the left of the text box
label.Location = new Point(this.Left - label.Width - 10, this.Top);
}
private void label_Resize(object sender, EventArgs e) {
moveLabel();
}
protected override void OnLocationChanged(EventArgs e) {
base.OnLocationChanged(e);
moveLabel();
}
public string Description {
get { return label.Text; }
set { label.Text = value; }
}
public override Font Font {
get { return base.Font; }
set { base.Font = label.Font = value; }
}
}
Did you consider using a UserControl? The benefit of a usercontrol is that you can easily put your label and textbox with correct relative positioning.
Custom Control - An extension to an existing control
User Control - A composition of multiple existing controls
Choose your candidate.

Categories