I am adding a new way to distinguish the user privileges in my program.
It is a small circular panel that appears after the username and that changes color depending on its privileges and that is shown after the user nick leaving a spacing of 5 pixels
:
private void SetNick(string nick)
{
this.NickLabel.Text = nick;
this.NickLabel.Left = ((this.ProfilePicturePanel.ClientSize.Width - this.NickLabel.Width) / 2) - 5;
Hector.Framework.Utils.Ellipse.Apply(this.BadgePanel, 6);
this.BadgePanel.Top = this.NickLabel.Top + 3;
this.BadgePanel.Left = this.NickLabel.Width + this.BadgePanel.Width + 5;
}
The nick of the user has a minimum of 3 characters and a maximum of 6 characters, then when the nickname has 6 characters (example: Jhon S), the panel is aligned correctly:
But if the nickname have 3 characters (example: Ben), then this happens:
It is assumed that the panel should always be shown near the label leaving a space of 5 pixels even if the label changes its content.
Could you tell me what I'm doing wrong?
You can override the Label Control and write your own implementation that draws your badge directly in the Label. Here's a simple example.
public class LabelWithBadge : Label
{
public Color BadgeColor { get; set; }
private Size BadgeSize { get; set; }
public LabelWithBadge()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
if (BadgeColor == null)
BadgeColor = Color.Red;
if (BadgeSize == null)
BadgeSize = new Size(20, 20);
}
protected override Size SizeFromClientSize(Size clientSize)
{
var textSize = TextRenderer.MeasureText("doesn't matter", this.Font);
this.BadgeSize = new Size(textSize.Height, textSize.Height);
var baseSize = base.SizeFromClientSize(clientSize);
return new Size(baseSize.Width + BadgeSize.Width, baseSize.Height);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillEllipse(new SolidBrush(this.BadgeColor), this.ClientSize.Width - this.BadgeSize.Width, 0, this.BadgeSize.Width, this.BadgeSize.Height);
}
}
By overriding SizeFromClientSize you can control the AutoSize ability of the label, and pad it to make room for your badge.
If you want to support manual sizing for the badge, then you'll need to tweak this to work with AutoSize off.
Then I set Styles on the control to handle painting. Overriding OnPaint allows you to draw in the extra padded on in the SizeFromClientSize override.
I added a property for the Badge Color. The Badge Size is determined by the font on the control using TextRenderer.MeasureText. So if you make the font bigger, the badge get's bigger with it.
This control will show up in your Toolbox when you build. Then you can use it like any other label but this one has a badge in it.
Related
I am working on a UserControl that contains a multiline TextBox.
When using my control, one will be able to set the text that will be displayed. The TextBox should then adapt its Height to make the text fit, the Width cannot change.
So here is the property that handles the text :
[Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
public string TextToDisplay
{
get
{
return internalTextBox.Text;
}
set
{
internalTextBox.Text = value;
AdaptTextBoxSize();
}
}
My first attempt was rather simple :
private void AdaptTextBoxSize()
{
int nbLignes = internalTextBox.Lines.Length;
float lineHeight = internalTextBox.Font.GetHeight();
internalTextBox.Height = (int)((nbLignes) * lineHeight);
}
This did not work as it doesn't take into account spacing between two lines of text. So the more lines I have in the text, the more I get clipped.
So I tried this :
private void AdaptTextBoxSize()
{
Size textSize = internalTextBox.GetPreferredSize(new Size(internalTextBox.Width, 0));
internalTextBox.Height = textSize.Height;
}
This does work when all the lines in the textbox are shorter than the Width. But when one line is longer and should be clipped to the next line, GetPreferredSize() returns a larger width than the one I passed, and therefore the height is too small.
So I changed again and tried this one:
private void AdaptTextBoxSize()
{
Size textSize = TextRenderer.MeasureText(
internalTextBox.Text,
internalTextBox.Font,
new Size(internalTextBox.Width, 0),
TextFormatFlags.WordEllipsis
);
internalTextBox.Height = textSize.Height;
}
This time the returned Width is correct, as it does not exceed the one I passed, but the height is the same as the previous trial. So it doesn't work either. I tried different combinations for TextFormatFlags, but could not manage to find the winning one...
Is this a bug from the framework?
The real question here is, is there another thing I can try, or another to achieve what I want (i.e. auto-adapt the height when setting the TextToDisplay property)?
TextBox.GetPositionFromCharIndex returns the pixel position of a character. Position here means top/left so we need to add one more line..
This seems to work here:
textBox.Height = textBox.GetPositionFromCharIndex(textBox4.Text.Length - 1).Y + lineHeight;
I get the line height like this:
int lineHeight = -1;
using (TextBox t = new TextBox() { Font = textBox.Font }) lineHeight = t.Height;
I set the Height instead of the ClientSize.Height, which is slightly wrong unless BorderStyle is None. You can change to textBox.ClientSize = new Size(textBox.ClientSize.Width, l + lh);
Is it possible to add padding to a TextObj? I am looking to display a value on the chart, with a visible border, but the text is always too close to the border. Is it possible to extend the height/width of the box to always leave some space/padding?
I have tried updating the height/width of the box, but this doesn't appear to have any effect. I have also used empty string spaces and while this works on the left-hand side, it has no effect on the right side.
Is there a correct way to do this?
Try this,
public partial class Form1 : Form
{
GraphPane myPane;
public Form1()
{
InitializeComponent();
myPane = zedGraphControl1.GraphPane;
AddTxtObject();
}
private void AddTxtObject()
{
TextObj txtObj = new TextObj("ZedGraph Version 5.1.5.xxx", 0.7, 0.8, CoordType.PaneFraction, AlignH.Left, AlignV.Bottom);
txtObj.FontSpec.FontColor = Color.GreenYellow;
txtObj.FontSpec.Size = 10;
txtObj.FontSpec.Fill.Color = Color.Black;
txtObj.FontSpec.Border.Color = Color.Black;
txtObj.FontSpec.Border.Width = 25.0f;
myPane.GraphObjList.Add(txtObj);
zedGraphControl1.Refresh();
}
}
By changing font size & the border width, you may achieve padding effect with Zedgraph.
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 am trying to extend the System.Windows.Forms.Label class to support vertically drawn text. I do this by creating a new property called MyLabelOrientation that the user can set to Horizontal or Vertical. When the user changes this setting, the values for width and height are swapped to resize the control to its new orientation. Finally, I override the OnPaint function to draw my Label.
I would like to extend the AutoSize property for this control as well so that my Label will auto-size to the text it contains. For the horizontal orientation, the base functionality implements this for me. For the vertical orientation, I create a Graphics object and set the height of the control to the width of the SizeF object returned from Graphics.MeasureString(Text, Font). You can see an example of the code I'm using below.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel.Design;
using System.Windows.Forms.Design;
public class MyLabel : Label
{
public enum MyLabelOrientation {Horizontal, Vertical};
protected MyLabelOrientation m_orientation = MyLabelOrientation.Horizontal;
[Category("Appearance")]
public virtual MyLabelOrientation Orientation
{
get { return m_orientation; }
set
{
m_orientation = value;
int temp = Height;
Width = Height;
Height = temp;
Refresh();
}
}
private Size ResizeLabel()
{
Graphics g = Graphics.FromHwnd(this.Handle);
SizeF newSize = g.MeasureString(Text, Font);
if (m_orientation == MyLabelOrientation.Horizontal)
Width = (int)newSize.Width;
else
Height = (int)newSize.Width;
}
protected override void OnPaint(PaintEventArgs e)
{
Brush textBrush = new SolidBrush(this.ForeColor);
if (m_orientation == LabelOrientation.Vertical)
{
e.Graphics.TranslateTransform(Width, 0);
e.Graphics.RotateTransform(90);
e.Graphics.DrawString(Text, Font, textBrush, Padding.Left, Padding.Top);
}
else
{
base.OnPaint(e);
}
}
}
However, setting AutoSize to true seems to prevent and/or override any changes to the size of the control. This means that I can't change the width or height when I want to change the Label's orientation. I'm wondering if this behavior can be overridden, so that I can test whether AutoSize is set, and then adjust the size of the control according to it's orientation.
I know this a a pretty old question, but i stumbled across it today and was wondering how to do the same thing.
My solution to the problem was overriding the GetPreferredSize(Size proposedSize) method. I used a button class that houses an arrow in addition to the text which, of course, was not taken into account using the AutoSize property so i added additional space and it works fine for me.
Given the problem of changing orientation or switching width and height, you could completely change the way the preferred size is calculated.
public override Size GetPreferredSize(Size proposedSize)
{
Size s = base.GetPreferredSize(proposedSize);
if (AutoSize)
{
s.Width += 15;
}
return s;
}
I have not done this before, I believe you can theoretically override a property declaration (via the new keyword) and check the orientation before proceeding:
override public bool AutoSize
{
set
{
if( /* orientation is horizontal */ )
{
base.AutoSize = value;
}
else
{
// do what you need to do
}
}
}
If think a solution is to override OnResize itself :
protected override void OnResize(EventArgs e)
{
if (AutoSize)
{
// Perform your own resizing logic
}
else
OnResize(e);
}
I'm quite simply going totally bonkers with the omission of the AutoSize-property for the Label and TextBox controls in .NET Compact Framework. I have a simple app, that's supposed to list a bunch of text data (generally between one-liners to a few paragraphs of text) in a TabControl. Everything else works smoothly, but my attempts at dynamically resizing the Label / TextBox -controls I use to display the text are failing miserably.
Here's the way I've tried doing it:
/*
Variables:
s = The text intended for the TextBox
NewTB = TextBox object
width = Intended width
whiteSpaceAdjustment = amount of pixels per line to adjust "wasted" whitespace due to wrapping
*/
String[] linesArray = s.Replace(Environment.NewLine, "\n").Split(new char[] { '\n' });
int lines = 0;
int lineHeight = g.MeasureString(
s.Replace("\n", "").Replace("\r", ""),
LabelFont
).ToSize().Height;
foreach (String str in linesArray) {
if (str.Length == 0) {
lines++;
continue;
}
szz = g.MeasureString(str, LabelFont).ToSize();
lines += szz.Width / (width - whiteSpaceAdjustment);
lines += (szz.Width % width) != 0 ? 1 : 0;
}
NewTB.Height = lines * lineHeight;
NewTB.Width = width;
...but the problem is that the range needed for whiteSpaceAdjustment is too huge. When it's large enough to actually work on the most extreme cases (paragraphs made mostly up of really long words), most boxes end up being a line or two too tall.
I'm probably going to have to implement word wrapping myself, but before I go there, is there anybody with a nice clean solution ready for this?
I'd be forever grateful!
Try this article
www.mobilepractices.com/2007/12/multi-line-graphicsmeasurestring.html
Make sure you also look at the link at the bottom of the article to be able to use different fonts.
If you are using .Net CF 3.5 you may be able to turn their example into an extension method. Otherwise I'd suggest that you create a new control inheriting from the framework control.
This is what I developed for auto re-size width of label in WinCE.
/// <summary>
/// This class provides dynamic size labels, i.e. as the text grows lable width will grow with it.
/// </summary>
public partial class AutoSizeLabel : UserControl
{
private string _strText;
private const int padding = 10;
public AutoSizeLabel()
{
InitializeComponent();
}
public override string Text
{
get
{
return _strText;
}
set
{
_strText = value;
Refresh();
}
}
protected override void OnPaint(PaintEventArgs pe)
{
SizeF size = pe.Graphics.MeasureString(this.Text, this.Font);
this.Size = new Size((int)size.Width + padding, this.Height);
if (this.Text.Length > 0)
{
pe.Graphics.DrawString(this.Text,
this.Font,
new SolidBrush(this.ForeColor),
(this.ClientSize.Width - size.Width) / 2,
(this.ClientSize.Height - size.Height) / 2);
}
// Calling the base class OnPaint
base.OnPaint(pe);
}
}