Add padding to a TextObj item in a ZedGraph chart - c#

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.

Related

Adapt the height of a TextBox

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);

Panel alignment error with respect to label

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.

How can I keep the RadioButton text and an additional Label in alignment?

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.

ToolStripMenuItem bigger vertical padding, or vertically centering text in a bigger ToolStripMenuItem

I'm trying to set a bigger vertical padding for ToolStripMenuItems in a ContextMenuStrip. However, changing the Padding.Top property adds padding to the bottom, instead of the top.
I also tried setting a larger Height for the ToolStripMenuItem, it works, however, the text always gets aligned on top, even if the TextAlign property is MiddleCenter. It should be vertical aligned to the center!
I've tried different settings for different properties, nothing works. The idea is that I cannot get the ToolStripMenuItem to have more space around its text, both to the top and to the bottom.
I'm using C#, Windows Forms, Net 2.0, Visual Studio 2010 Express, Windows 7.
You can get the same effect using Margin instead of Padding which will keep the Text of the ToolStripMenuItem aligned.
The drawback is that this wont modify the size of the highlight rectangle when the item is selected so it can look a little strange if you increase a lot the height.
In addition to InBetween's answer, you can fix the highlight rectangle by using a custom renderer and adjusting its "TextRectangle" property. Here's some sample code:
var itemHeight = 36;
var verticalPadding = 36 - TextRenderer.MeasureText("A", _DisplayNameFont).Height) / 2;
menu.Renderer = new MyRenderer { VerticalPadding = verticalPadding };
class MyRenderer : ToolStripSystemRenderer
{
public int VerticalPadding { get; set; }
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
if (null == e)
{ return; }
e.TextFormat &= ~TextFormatFlags.HidePrefix;
e.TextFormat |= TextFormatFlags.VerticalCenter;
var rect = e.TextRectangle;
rect.Offset(0, VerticalPadding);
e.TextRectangle = rect;
base.OnRenderItemText(e);
}
}
Adding a new line does the job.
It's not the best solution, but it's a quick way to add some padding.
Works well when adding support for small touch screens.
this.configToolStripMenuItem.Text = "\r\nSettings";

Winform not scrolling when drawing objects on it

C#3.0,.net framework 3.5
I am drawing ( using the draw method in the graphics class) a lot of solid rectangles on a windows form vertically. The form starts at 500 x 500 px and the rectangles are only drawn at runtime after data is downloaded from the net -and the number of rectangles depends on the download so I do not know it upfront.
So only a few rectangles are drawn as the size of the form is fixed.
So I googled/Binged ( lest someone suggest I do that) and found a few tips but they don't work in this case -like setting the forms AutoScroll property to true or trying double buffering.I also tried to draw on a listbox control and set it's scroll property etc...but no dice.
I'm guessing there is no way to display , say 200 rectangles vertically on a windows form using draw. I need some other solution... any ideas please.
Maybe a list of pictureboxes and then populate each picturebox with the solid color ?
Thanks
You are drawing GDI+ rectangles on a form during the paint event? The form would have no idea that you are creating objects outside of the clipping space and would therefore have no idea that you need to scroll.
You would need to add a scrollbar to the form and then calculate the value\position of the scrollbar and use that to determine what portion of your rectangles to draw upon the paint event. This would involve a bit of manual effort. You could draw them all to an in-memory bitmap of the appropriate size and then just copy the portions of that to the form upon draw.
Or:
If you wanted the form to do this for you, create a custom rectangle control and place 200 of those on the form. Since they are components and have a concrete height & width, the form would then know it needed to scroll, and would do so accordingly provided that autoscroll was set.
it can be as simple as this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
this.AutoScroll = true;
for (int i = 0; i < 100; i++)
this.Controls.Add(new Rectangle() { Top = i * 120, Left = 10 });
}
}
public class Rectangle : Control
{
public Rectangle()
{
this.Width = 100;
this.Height = 100;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Black, 5), 0, 0, 100, 100);
}
}

Categories

Resources