I am having trouble drawing a line on a TabPage.
I actually have a TabControl inside a TabControl. I have drawn a number of labels which I am using as boxes. I want to draw lines to join them together.
I have tried:
Pen P = new Pen(System.Drawing.Color.Black, 10);
tabname.CreateGraphics().DrawLine(P, 10, 10, 100, 100);
and
Pen P = new Pen(System.Drawing.Color.Black, 10);
tabcontrolname.TabPages[0].CreateGraphics().DrawLine(P, 10, 10, 100, 100);
Neither are displaying the line. I assume the line is being placed somewhere as there are no errors.
Any ideas how I can get it to display on the correct TabPage?
Thank you!
You probably need to override the OnPaint method (or handle the Paint event) to get this to work properly. If you don't your controls will just end up drawing over your lines.
Here's a link to the relevant docs.
Where do you try these codes, in which function? If you are doing it once in the initialization or construction, they will not be displayed as you expect. Whenever the control needs to be redrawn, you need to draw this line too, again. Either override the OnPaint method of the control or register for the Paint event and do the line drawing there.
I was able to get the arrow to show up using the following code:
TabPage.Paint += new PaintEventHandler(TabPage_Paint);
and
protected void TabPage_Paint(object sender, PaintEventArgs e)
{
base.OnPaint(e);
Pen arrow = new Pen(Brushes.Black, 4);
arrow.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
e.Graphics.DrawLine(arrow, 10, 10, 100, 100);
arrow.Dispose();
}
However, when scrolling is initiated the Paint messes up :(
Related
I have the problem that I want to draw on a pictureBox with an image loaded before the user gets to see it. The drawing in principle works, but if I do it in the constructor it doesn't show up. It does work if I call the same function with a button.
public Form1()
{
InitializeComponent();
draw();
}
public void draw()
{
pictureBox1.Refresh();
Graphics g = pictureBox1.CreateGraphics();
Rectangle rect = new Rectangle(new Point(20, 20), new Size(40, 40));
rect.Offset(8, 8);
g.DrawEllipse(new Pen(Brushes.Black, 2), rect);
g.Dispose();
}
private void button1_Click(object sender, EventArgs e)
{
draw();
}
I would have assumed that the circle would show up when the Form loads but it only does when I press the button. The code itself gets executed, as tested with a Message box.
The control paints itself in its Paint event, so as soon as it's shown on screen (ie, after your code), it will paint itself in dull gray as normal.
If you want to custom paint a control, any control, you either override its OnPaint function or subscribe to its exposed Paint event.
A hint should be the fact that you had to create your own Graphics object, as opposed to using the object constructed during normal painting operations, that also includes proper clipping handling.
I've been trying to draw a line between two objects for a long time now, but it still won't work.
My program is supposed to make two picture boxes (Already made, called PB1 and PB2) and connect them with a line on the form.
I have this:
public void DrawStuff(object sender, PaintEventArgs e)
{
Pen blackPen = new Pen(Color.Black, 3);
Point point1 = new Point(PB[0].Location.X, PB[0].Location.Y);
Point point2 = new Point(PB[1].Location.X, PB[1].Location.Y);
e.Graphics.DrawLine(blackPen, point1, point2);
CreateGraphics();
}
But I can't call the function! Also, the Boxes are being created with a button, so it can't draw from the start, it has to do it after I push that button. If anyone has a working code, please let me know, I'm about to break down.
Do not (read NEVER EVER) call CreateGraphics() explicitly. This is a crime against humanity, except for very rare situations.
Handle Paint event (or override OnPaint()) of your Form. Write your line drawing code in there.
Something like this:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using(var blackPen = new Pen(Color.Black, 3))
e.Graphics.DrawLine(blackPen, PB[0].Location, PB[1].Location);
}
Whenever you need to refresh screen manually, call this.Invalidate().
I have created a button using the code below. Adding the start.Text="Start"; statement does nothing. How do I add a label to my button?
public MainForm()
{
InitializeComponent();
myButtonObject start = new myButtonObject();
EventHandler myHandler = new EventHandler(start_Click);
start.Click += myHandler;
start.Location = new System.Drawing.Point(200, 500);
start.Size = new System.Drawing.Size(101, 101);
start.Text="Start";
// start.TextAlign = new System.Drawing.ContentAlignment.MiddleCenter;
this.Controls.Add(start);
}
public class myButtonObject : UserControl
{
// Draw the new button.
protected override void OnPaint(PaintEventArgs e)
{
Graphics graphics = e.Graphics;
Pen myPen = new Pen(Color.Black);
// Draw the button in the form of a circle
graphics.FillEllipse(Brushes.Goldenrod, 0, 0, 100, 100);
graphics.DrawEllipse(myPen, 0, 0, 100, 100);
myPen.Dispose();
}
}
You have implemented your own button as a user control. Since you are implementing OnPaint to provide your own draw functionality, you need to implement all the functionality like drawing the text too.
If you want to go down this route then you also need to add the logic to draw the text on the control in your OnPaint method. This can be done using the graphics.DrawString method.
See http://msdn.microsoft.com/en-us/library/system.drawing.graphics.drawstring.aspx
You also need to call graphics.dispose.
If you aren't familiar with this, then it might be simpler to use a UserControl and add a label to it, or something similar, and then to draw your circle shape on the top of that.
You should draw the button's text yourself in the OnPaint method:
TextRenderer.DrawText(graphics, Text, Font, new Point(5, 5), SystemColors.ControlText);
Where new Point(5, 5) - is a top left position of the text.
You paint the button ok but you don't draw the text.
A usercontrol doesn't do this by itself.
Just add something like this to the paint commands:
graphics.DrawString(Text, yourfont, yourBrush, x, y);
you must and may have to decide freely on x, y font and brush.
Hey people I have a problem I am writing a custom control. My control inherits from Windows.Forms.Control and I am trying to override the OnPaint method. The problem is kind of weird because it works only if I include one control in my form if I add another control then the second one does not get draw, however the OnPaint method gets called for all the controls. So what I want is that all my custom controls get draw not only one here is my code:
If you run the code you will see that only one red rectangle appears in the screen.
public partial class Form1 : Form
{
myControl one = new myControl(0, 0);
myControl two = new myControl(100, 0);
public Form1()
{
InitializeComponent();
Controls.Add(one);
Controls.Add(two);
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
public class myControl:Control
{
public myControl(int x, int y)
{
Location = new Point(x, y);
Size = new Size(100, 20);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Pen myPen = new Pen(Color.Red);
e.Graphics.DrawRectangle(myPen, new Rectangle(Location, new Size(Size.Width - 1, Size.Height - 1)));
}
}
I'm guessing you are looking for something like this:
e.Graphics.DrawRectangle(Pens.Red, new Rectangle(0, 0,
this.ClientSize.Width - 1,
this.ClientSize.Height - 1));
Your Graphic object is for the interior of your control, so using Location isn't really effective here. The coordinate system starts at 0,0 from the upper-left corner of the client area of the control.
Also, you can just use the built-in Pens for colors, otherwise, if you are creating your own "new" pen, be sure to dispose of them.
LarsTech beat me to it, but you should understand why:
All drawing inside of a control is made to a "canvas" (properly called a Device Context in Windows) who coordinates are self-relative. The upper-left corner is always 0, 0.
The Width and Height are found in ClientSize or ClientRectangle. This is because a window (a control is a window in Windows), has two areas: Client area and non-client area. For your borderless/titlebar-less control those areas are one and the same, but for future-proofing you always want to paint in the client area (unless the rare occasion occurs where you want to paint non-client bits that the OS normally paints for you).
This is a C# desktop application. The DrawStyle property of my ListBox is set to OwnerDrawFixed.
The problem: I override DrawItem to draw text in different fonts, and it works. But when I start resizing the form at the runtime, the selected item is drawn correctly, but the rest of them are not redrawn, causing text looking corrupt for unselected items.
Here's my code:
private void listDevices_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
string textDevice = ((ListBox)sender).Items[e.Index].ToString();
e.Graphics.DrawString(textDevice,
new Font("Ariel", 15, FontStyle.Bold), new SolidBrush(Color.Black),
e.Bounds, StringFormat.GenericDefault);
// Figure out where to draw IP
StringFormat copy = new StringFormat(
StringFormatFlags.NoWrap |
StringFormatFlags.MeasureTrailingSpaces
);
copy.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, textDevice.Length)});
Region[] regions = e.Graphics.MeasureCharacterRanges(
textDevice, new Font("Ariel", 15, FontStyle.Bold), e.Bounds, copy);
int width = (int)(regions[0].GetBounds(e.Graphics).Width);
Rectangle rect = e.Bounds;
rect.X += width;
rect.Width -= width;
// draw IP
e.Graphics.DrawString(" 255.255.255.255",
new Font("Courier New", 10), new SolidBrush(Color.DarkBlue),
rect, copy);
e.DrawFocusRectangle();
}
listDevices.Items.Add("Device001");
listDevices.Items.Add("Device002");
Also, the item that is drawn correctly (the selected one) is flickering on form resizing. No biggie, but if anyone know why.... tnx
Put the following code in the Resize event:
private void listDevices_Resize(object sender, EventArgs e) {
listDevices.Invalidate();
}
This should cause everything to be redrawn.
To stop the flickering, you need double buffering.
To do this, make a new class, derived from ListBox, and put the following in the constructor:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Or just paste this into a code file:
using System.Windows.Forms;
namespace Whatever {
public class DBListBox : ListBox {
public DBListBox(): base() {
this.DoubleBuffered = true;
// OR
// this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}
}
Replace "Whatever" with the namespace your project uses, or make it something more useful. AFter compiling, you should be able to add a DBListBox in the form designer.
I repro the problem. There are several mistakes in the code, the font name is "Arial", you should not adjust rect.Width, you forget to call Dispose() on the fonts, brushes and regions. But they don't explain the behavior. There's something wrong with the clipping area that prevents the text from being properly updated. I don't see where that occurs, the Graphics object state is okay.
Graphics.DrawString() is a very troubled method, you should really avoid it. All Windows Forms controls, including ListBox, use TextRenderer.DrawText(). That solves the problem when I use it. I know measuring is more difficult, you could work around that by displaying the IP address at a fixed offset. Looks better too, they'll line up in a column that way.
It flickers because you use e.DrawBackground(). That erases the existing text, you draw the text right back on it. I don't think double-buffering is going to fix that, you'd have to draw the entire item so you don't have to draw the background. Tricky if you can't get the exact size of the text with the large font, a workaround is to draw into a bitmap first.