I'm sure this is something very easy to figure out but I cannot do it. I have a winform with 3 Label inside a Panel. When the form loads, the first Label has a Paint event that draws a rectangle on it. I would like a backgroundWorker to go through each one, wait 5 seconds, restore the Label to normal (redrawing I'm guessing) and then draw a rectangle on the following Label.
public List<Label> GetLabelList()
{
return new List<List>()
{
label1,
label2,
label3,
label4
};
}
private void bgBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var getList = GetLabelList();
for (int i = 0; i < getList.Count; i++)
{
if ((bgBackgroundWorker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(5000);
getList [i].Paint += RemoveLabelHighlight;
getList [i].Invalidate();
if (i < 2)
{
getList [i + 1].Paint += AddLabelHighlight;
getList [i + 1].Invalidate();
}
bgBackgroundWorker.ReportProgress((i * 10));
}
}
}
private void AddLabelHighlight(object sender, PaintEventArgs e)
{
var label = sender as Label;
e.Graphics.DrawRectangle(new Pen(Color.DeepPink, 8), label.ClientRectangle);
}
private void RemoveLabelHighlight(object sender, PaintEventArgs e)
{
var label = sender as Label;
e.Graphics.DrawRectangle(new Pen(Color.Green, 8), label.ClientRectangle); // This should return the Label back to original state
}
This works but when the rectangle is drawn, the label is cut off all the way around. Any suggestions?
Also, I'm sure there is a much better and more efficient way to achieve this, maybe by an EventHandler or something. I'd like some suggestions, if possible.
This is actually being caused by your use of the pen width of 8 pixels, I believe. Try a different size and see if that changes the size of the rectangle not being drawn.
To fill the rectangle instead, use:
e.Graphics.FillRectangle(new SolidBrush(Color.DeepPink), e.ClipRectangle);
EDIT Since you're now completely responsible for drawing the control, the text can be redrawn with a DrawString call:
e.Graphics.DrawString(label.Text, label.Font, SystemBrushes.ControlText, new PointF(0,0));
EDIT Here's how to nest a panel and a label to achieve what you're looking for:
Add a new panel, set the padding to 8,8,8,8, and BackColor to whatever you like
Add a new label to this panel, set it's AutoSize property to false, Dock property to Fill, and TextAlign property to MiddleCenter
While I have always loved doing owner-drawn stuff, sometimes it's just easier to use what's there! For fun though, I would wrap this into a new Panel-derived control to make it easy to reuse.
Related
I was just wondering if its was easily possible to set the margins of a textbox a particular color? Im using winforms and in my validating event handlers i have a series of error providers, which if return false I want to set just the margins red and if successful green. is this possible with out having a any controls hidden behind ? I know how to set the foreground and panel colour but it just seems so sloppy to have to have this all hidden behind it. this is my validating event handler.
private void txt_LndLine_Validating(object sender, CancelEventArgs e)
{
if (utility.isNum,(txt_LndLine.Text))
{
epLandline.SetError(txt_LndLine, "Please use unknown Entity!!!");
return;
}
else
{
epLandline.Clear();
_IsValid = true;
}
}
Just a query, as the event hadnler works fine just wouldn't mind a smarter way of presenting the errors rather than the icon
Here is an alternative solution, which won't add any extra controls. It paints the border onto the TextBoxes' Parent.
Set it up by hooking the routine into the Paint event of your TextBox's Parent once, maybe like this:
textBox1.Parent.Paint += DrawMargins;
Now you can set the Tag to hold the Brush you want to use:
textBox1.Tag = Brushes.Red;
textBox2.Tag = Brushes.Green;
After changing the Brush you need to trigger the routine, by Invalidating the Parent:
textBox1.Parent.Invalidate();
To take one TextBox out of the painting reset the Tag to null:
textBox1.Tag = null;
You can also un-hook the whole event of course:
textBox1.Parent.Paint -= DrawMargins;
Here is the drawing method:
private void DrawMargins(object sender, PaintEventArgs e)
{
Control parent = sender as Control;
foreach ( Control ctl in parent.Controls)
{
SolidBrush brush = ctl.Tag as SolidBrush;
if (brush == null) continue;
e.Graphics.FillRectangle(brush, ctl.Left - ctl.Margin.Left,
ctl.Top - ctl.Margin.Top,
ctl.Width + ctl.Margin.Horizontal,
ctl.Height + ctl.Margin.Vertical);
}
}
Note that this will work for any control which has a SolidBrush in the Tag and is a child of the same parent. If some controls as nested, say in a Panel or a GroupBox, I guees you should replace the loop over the parent.Controls collection by a List<Control> of the participating controls..
I have enlarged the left margin of the 1st TextBox, as you can see..
May be the answer in comments is a good one (#RudyTheHunter).
I have a simple way of doing.
Put a panel control right below the text box and change the border color of the panel.
I tried it and looks as shown in below image.
Code:
if (utility.isNum,(txt_LndLine.Text))
{
epLandline.SetError(txt_LndLine, "Please use unknown Entity!!!");
//PANEL COLOR
this.panel1.BackColor = System.Drawing.Color.Green;
this.panel1.Padding = new System.Windows.Forms.Padding(5);
return;
}
else
{
epLandline.Clear();
//PANEL COLOR
this.panel1.BackColor = System.Drawing.Color.Red;
this.panel1.Padding = new System.Windows.Forms.Padding(5);
_IsValid = true;
}
I have some buttons inside a FLP and I want when the mouse is over a button, to increase the size of the button a little, like a zoom effect. The problem is that when the button size increases, all buttons next to it slide to the right and down.. Probably the issue is caused by the space added by the FLP between items, but how can I prevent this, so when the size is increasing to go over that space not to add more..?
My ZOOM class:
class zoom
{
public zoom(Control cst)
{
cst.MouseEnter += delegate(object sender, EventArgs e)
{
cst.Size = new Size(70, 75);
cst.Font = new Font(cst.Font.FontFamily, 9);
};
cst.MouseLeave += delegate(object sender, EventArgs e)
{
cst.Size = new Size(68, 73);
cst.Font = new Font(cst.Font.FontFamily, 8);
};
}
}
It's the way the FlowLayoutPanel works. It moves the rest of the controls to fit them all following the flow.
If you have some space between the buttons it's because of the Margin property, you could reduce the margin of the button as you zoom it.
This won't work if you want the button to show over the other ones. In that case, probably the only way to go is to use a simple Panel instead of the FlowLayoutPanel and do a BringToFront() with the Zoom.
I have a Form which has got a parent panel and it had got a child panel where I am drawing items using the drawing mechanism it works good as expected, but when I shrink my form from right to left it doesn't call child panels paint event while if I shrink a little from left to right and again spread it then it calls the paint event, how should I fix it?
Below is my code.
private void canvas_Paint(object sender, PaintEventArgs e)
{
drawString(e);
this.Invalidate();
//this.Refresh();
//this.Update();
}
private void drawString(PaintEventArgs e)
{
System.Drawing.Drawing2D.LinearGradientBrush myBrush = new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle, Color.Red, Color.Yellow, System.Drawing.Drawing2D.LinearGradientMode.Horizontal);
cBasketItemHelper objHelper = new cBasketItemHelper() { CanvasWidth = this.canvas.Width, CanvasHeight = this.canvas.Height, X = 3, Y = 3 };
objHelper.myBrush = myBrush;
objHelper.currOrder = Program.currOrder;
objHelper.g = e.Graphics;//this.canvas.();//this.canvas.Graphics;
objHelper.DrawBasketItems();
e.Dispose();
}
The Panel class was designed to be just a container for other controls, it is not expected to do any painting of its own beyond drawing the background. Somewhat heavy-handedly it optimizes the painting, a resize only paints the parts that were revealed, not the entire client area.
You however want OnPaint to always run when the size changes, even when you make it smaller. Derive your own class from Panel and set the ResizeRedraw property to true in the constructor:
class Canvas {
public Canvas() {
this.ResizeRedraw = true;
this.DoubleBuffered = true; // extra goodie
}
}
Build. Drop the new Canvas control from the top of the toolbox, replacing your existing panel control. If you don't need the scrolling support that Panel provides then using a PictureBox gets you both without needing to derive.
I have a ListView where I wish to tweak the drawing of items (for example highlighting certain strings in list view itmes), however I don't want to radically alter the way that items are displayed.
I have set the OwnerDraw to true and can get my head around how to draw my highlighting effect, however whenever I try to defer to the default implementation to draw the rest of the list view item things go wrong and I'm left with a whole load of graphical problems indicating that actually I've completely gone wrong.
Is there somewhere that I can see what the "Default" handlers for the DrawItem and DrawSubItem events do so that I can better my understanding and more easily tweak my code?
For reference here is a snippet showing what I'm currently doing:
public MyListView()
{
this.OwnerDraw = true;
this.DoubleBuffered = true;
this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader);
this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem);
this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem);
}
private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
// Not interested in changing the way columns are drawn - this works fine
e.DrawDefault = true;
}
private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
}
private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0)
{
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
}
e.DrawText();
}
I haven't got the time now to write up a complete answer so instead I'll put down some quick notes and come back to it later.
As LarsTech said, owner drawing a ListView control is a pain - the .Net ListView class is a wrapper around the underlying Win32 List View Control and the ability to "Owner draw" is provided by the NM_CUSTOMDRAW notification code. As such there is no "default .Net implementation" - the default is to use the underlying Win32 control.
To make life even more difficult there are a number of extra considerations to make:
As LarsTech pointed out, the first subitem in fact represents the parent item itself, and so if you handle rendering in both DrawItem and DrawSubItem you may well be drawing the contents of the first cell twice.
There is a bug in the underlying list view control (documented on the note on this page) that means that a DrawItem event will occur without corresponding DrawSubItem events, meaning that if you draw a background in the DrawItem event and then draw the text in the DrawSubItem event your item text will disappear when you mouse over.
Some of the rendering also appears to not be double-buffered by default
I also noticed that the ItemState property is not always correct, for example just after resizing a column. Consequently I've found its best not to rely on it.
You also need to make sure that your text doesn't split over multiple lines, else you will see the top few pixels of the lower line being rendered at the bottom of the cell.
Also special consideration needs to be given when rendering the first cell to take account of extra padding that the native list view uses.
Because the DrawItem event occurs first, anything you draw in the DrawItem handler (e.g. the selection effect) may well be overlayed by things you do in the DrawSubItem handler (e.g. having certain cells with a different background color).
All in all handling owner drawing is a fairly involved affair - I found it best to handle all drawing inside the DrawSubItem event, its also best to perform your own double-buffering by using the BufferedGraphics class.
I also found looking at the source code for ObjectListView very handy.
Finally, all of this is just to handle the details mode of the list view (the only mode I am using), if you want the other modes to work too then I believe that there are extra things to take account of.
When I get a chance I'll try and post my working example code.
I don't know if this will completely help you, but I'll add a few notes:
One thing to keep in mind is that DrawSubItem will draw the first item, too, and that's probably where you are getting the double-rendered look from.
Some things to try (not factored for speed):
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) {
e.DrawBackground();
if ((e.State & ListViewItemStates.Selected) == ListViewItemStates.Selected) {
Rectangle r = new Rectangle(e.Bounds.Left + 4, e.Bounds.Top, TextRenderer.MeasureText(e.Item.Text, e.Item.Font).Width, e.Bounds.Height);
e.Graphics.FillRectangle(SystemBrushes.Highlight, r);
e.Item.ForeColor = SystemColors.HighlightText;
} else {
e.Item.ForeColor = SystemColors.WindowText;
}
e.DrawText();
e.DrawFocusRectangle();
}
For your DrawSubItem routine, make sure you aren't drawing in the first column and I added the DrawBackground() routine. I added some clipping to the highlight rectangle so it wouldn't paint outside the column parameters.
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) {
if (e.ColumnIndex > 0) {
e.DrawBackground();
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0) {
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.SetClip(e.Bounds);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
e.Graphics.ResetClip();
}
e.DrawText();
}
}
In general, owner drawing a ListView control is welcoming in a world of hurt. You aren't drawing in Visual Styles anymore, you would have to do that yourself, too. Ugh.
Item selected back color changed. In by default blue in windows. This code will help for u in any colors:
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
if (e.Item.Selected)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds);
}
e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds);
}
private void Form1_Load(object sender, EventArgs e)
{
for (int ix = 0; ix < listView1.Items.Count; ++ix)
{
var item = listView1.Items[ix];
item.BackColor = (ix % 2 == 0) ? Color.Gray : Color.LightGray;
}
}
private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
}
}
}
ComponentOwl recently released a freeware component called Better ListView Express.
It looks and behaves exactly like the ListView, but has much more powerful owner drawing capabilities - you can draw accurately over all elements and even turn off some drawing (e.g. selection to make you on).
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
if (e.Item.Selected)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds);
}
e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds);
}
I'm writing a program where the user should be able to write text in a TextBox. I'd like the TextBox to resize itself, so it fits to the content.
I've tried the following:
private void textBoxTitle_TextChanged(object sender, TextChangedEventArgs e)
{
System.Drawing.Font myFont = new System.Drawing.Font("Verdana", 8);
System.Drawing.SizeF mySize = e.Graphics.MeasureString("This is a test", myFont);
this.textBoxTitle.Width = (int)Math.Round(mySize.Width, 0);
}
I get an error saying that Graphics doesn't work for TextChangedEventArgs. Is there another way I can resize the TextBox?
You should try a code something like below. It has worked for me well.
private void textBox1_TextChanged(object sender, EventArgs e)
{
Size size = TextRenderer.MeasureText(textBox1.Text, textBox1.Font);
textBox1.Width = size.Width;
textBox1.Height = size.Height;
}
For more information refer to TextRenderer.MeasureText()
I am adding this answer as I do not see the fixed width aspect of a textbox being discussed in any of the other. If you have a fixed width for your textbox, and you want to adjust only its height you can do something like the following:
Something like this gives the height of the text as how it is drawn in the multiline wordwrapped textbox itself:
SizeF MessageSize = MyTextBoxControl.CreateGraphics()
.MeasureString(MyTextBoxControl.Text,
MyTextBoxControl.Font,
MyTextBoxControl.Width,
new StringFormat(0));
I am not sure what StringFormat should be but the values StringFormatFlags do not seem to apply to a default TextBox make up.
Now with MessageSize.Height you know the height of the text in the textbox.
I had the same problem and I solved it in a simpler way.
I used the AutoSize property of a Label control.. I added an invisible label to my form, set its AutoSize property True. When the I need to change the size of my TextBox I use this code:
MyLabel.Text = MyTextBox.Text;
MyTextBox.Size = MyLabel.Size;
I set the Maximum and Minimum Size of the label for better results.
Have Fun
Your binding to the wrong event, and you cannot use the graphics object in the TextChangedEventArgs object.
Try using the TextChanged event. The following snippet is working:
public Form1()
{
InitializeComponent();
this.textBox1.TextChanged += new EventHandler(textBox1_TextChanged);
}
void textBox1_TextChanged(object sender, EventArgs e)
{
System.Drawing.SizeF mySize = new System.Drawing.SizeF();
// Use the textbox font
System.Drawing.Font myFont = textBox1.Font;
using (Graphics g = this.CreateGraphics())
{
// Get the size given the string and the font
mySize = g.MeasureString(textBox1.Text, myFont);
}
// Resize the textbox
this.textBox1.Width = (int)Math.Round(mySize.Width, 0);
}
}
first, create method to Make the TextBox fit its contents.
private void AutoSizeTextBox(TextBox txt)
{
const int x_margin = 0;
const int y_margin = 2;
Size size = TextRenderer.MeasureText(txt.Text, txt.Font);
txt.ClientSize =
new Size(size.Width + x_margin, size.Height + y_margin);
}
then with the TextChanged event handler calls AutoSizeTextBox() function to make the TextBox fit its text when the text changes.
private void txtContents_TextChanged(object sender, EventArgs e)
{
AutoSizeTextBox(sender as TextBox);
}
That’s all, for more info:
resize-a-textbox-to-fit-its-text
You will need to use the CreateGraphics() method of the form to create the Graphics instance to measure the string on.
The TextChangedEventArgs class does not have a Graphics property, that is a property of the PaintEventArgs class passed in to the Paint event handler
Try this:
using System.Drawing;
...
private void textBoxTitle_TextChanged(object sender, TextChangedEventArgs e)
{
// Determine the correct size for the text box based on its text length
// get the current text box safely
TextBox tb = sender as TextBox;
if (tb == null) return;
SizeF stringSize;
// create a graphics object for this form
using(Graphics gfx = this.CreateGraphics())
{
// Get the size given the string and the font
stringSize = gfx.MeasureString(tb.Text, tb.Font);
}
// Resize the textbox
tb.Width = (int)Math.Round(stringSize.Width, 0);
}
Essentially you create your own Graphics object for the form, then measure it based on the text and font of the TextBox. The using will properly dispose the Graphics object - your previous code would have leaked horribly!
Whatever the aim is.
If the size of the textbox should be dynamically set up based on the string, which should be the text inside this box, there is no nice option.
Reasons : MeasureString uses usual string formatters as delimiters for its own width and height.
Means, carriage return and line feed are parsed, too. Resulting in a sizeF.Width and sizeF.Height.
Depending on the string( and its font and number of lines ) these both variables can carry values, which are sometimes useless to be used as width/height values of a textbox ( because they can be bigger than the parentform's values and this would resize the textbox to a size, with left and bottom borders beyond those of the parent form).
Some solutions are still available, depending on the aim, one would like to achieve.
One idea would be :
Create a textbox in designer, size = 100 X 100. enable word-wrapping.
In the OnTextChanged event handler of the textbox, we just resize the textbox's width to a value, defined by ourself (e.g. parentform.Width or another hard value ).
This would cause the word wrap to recalculate the string in the textbox and this would rearrange all the characters inside the textbox, because word wrap is enabled.
The height of the textbox could can be set hard to parentform.Height, for example.
BUT,
better : set the height dynamically,based on the Y value of the ReturnValue (Point) of the method texbox.GetPositionFromCharIndex(textbox.TextLength -1 ).
Then, with Math.Min() determine, which is smaller ( either parentform.Height or Point.Y ) , and reset the textbox size to new Size(previousDeterminedWidth, nowDeterminedHeight).
Please keep in mind ( if scrollbars are enabled ) to add about 17 pixs to Your width calculation.
Best regards
Did you try to set yourTextBox.AutoSize = true;?
This property may be hidden in the GUI designer, but you can set it in the form constructor right after InitializeComponent(); call.
Graphics.Measure string you can do o PaintEventArgs, not on TextChangedEventArgs
What I think you want is this
System.Drawing.Font myFont = new System.Drawing.Font("Verdana", 8);
Graphics graphics = this.CreateGraphics();
SizeF textSize = graphics.MeasureString("This is a test", myFont);
The problem is that you just cannot create a Graphics object by simply allocating it since it has no public constructor, so you should better go and use TextRenderer.MeasureText, as done in http://msdn.microsoft.com/en-us/library/y4xdbe66.aspx
TextRenderer is less accurate because it uses GDI and Graphics uses GDI+, so maybe you should leave a little margin on the value you get from the Width property.
Hope this helps