Problem with using Paint event in c# - c#

I have used this below code before and was working perfectly. When I use the same
in one of my window form, the color of the form is not changing.
I mean after the page loads it shows the default color of the form. But when I try to debug the code below, it changes the color of the form perfectly. The problem is, after executing the last line of the code the color of the form goes back to the default color.
Am I missing something?
The form looks like a windows taskbar, and it has one tab control in it.
private void TaskBar_Paint(object sender, PaintEventArgs e)
{
Graphics mGraphics = e.Graphics;
Pen pen1 = new Pen(Color.FromArgb(96, 155, 173), 1);
Rectangle Area1 = new Rectangle(0, 0, this.Width - 2, this.Height - 2);
LinearGradientBrush LGB = new LinearGradientBrush(Area1,
Color.FromArgb(96, 155, 173),
Color.FromArgb(245, 251, 251),
LinearGradientMode.Vertical);
mGraphics.FillRectangle(LGB, Area1);
mGraphics.DrawRectangle(pen1, Area1);
}

There isn't much to go on here. What is this handler attached to? The Paint event of the Form? If so, you should override OnPaint() instead of attaching to the handler. My guess is that some other method is doing some painting too. You need to track that down. Without more code, it is not very likely that anyone here can help you. Sorry.

Related

C# WinForms, ToolTip with semi-transparent background

i'm working on this small WinForm app and decided to use custom drawn tooltip with semi-transparent background. So i started with setting the OwnerDraw property on ToolTip to True, created event handlers for Draw and Popup events (see the example code bellow. The commented version isn't working either).
private void toolTip_Popup(object sender, PopupEventArgs e)
{
e.ToolTipSize = new Size(400, 400);
}
private void toolTip_Draw(object sender, DrawToolTipEventArgs e)
{
//e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(0, 0, 400, 400));
}
Now when the ToolTip is shown for the first time for a specific control everything works as intended. See the picture bellow (400x400 tooltip with semi-transparent red background).
But when i hover over the same control for the second time ToolTip loses its semi-transparency. See the picture bellow. Why is that so?
Thank you all for your help. I'm pretty sure that Ben Voigts answer, or Jimis comments could solve this problem somehow too (i'll try them out later and update the answer if i'll be able to utilise them).
I based my solution on the first comment made by Hans Passant where he suggested to use Graphics.CopyFromScreen() in Popup event handler(toolTipDay_Popup), to capture the image underneath the ToolTip and then in Draw event handler(toolTipDay_Draw) i just drew the captured image.
(There is a problem with different DPI scalings as noted by Hans Passant, but that can be +- solved by Farshid T answer in How to get Windows Display settings?, i didn't include it in code bellow).
So the solution i'm using right now is as follows:
Bitmap dayToolTipBackground = new Bitmap(200, 200);
private void toolTipDay_Popup(object sender, PopupEventArgs e)
{
e.ToolTipSize = new Size(200, 200);
var backGraphics = Graphics.FromImage(dayToolTipBackground);
var cursorPosition = Cursor.Position;
backGraphics.CopyFromScreen(new Point(Cursor.Position.X, Cursor.Position.Y + 21), new Point(0, 0), new Size((200, 200)));
}
private void toolTipDay_Draw(object sender, DrawToolTipEventArgs e)
{
e.Graphics.DrawImage(dayToolTipBackground, new Point(0, 0));
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
}
The first time you hover over the control, a new instance of tooltip is created with the transparent color (ARGB.120). When you click outside the tooltip, whether the tooltip object is fully disposed or are you simply setting the instance as invisible?
When you are hovering over the instance for the second time, then, if the old tooltip object is not disposed, there is a chance that the same object is called again. So now when you do Graphics.FillRectangle() on an existing tooltip with color = ARGB.120, you are just overlaying another layer of ARGB.120 color on it, which will darken it further because the color levels are changed.
Therefore when you click outside the tooltip after you call it for the first time, you might need to dispose the tooltip object(or the e.Graphics object, if that doesn't affect other parts of your application) and create new tooltip objects every time you hover over the control.
Windows needs to be told that the windows beneath the popup need to be redrawn. This is done via a "layered window" style. With layering, the content gets drawn in z-order and transparency blending works. Without layering, only the top window gets sent a repaint and it draws on top of meaningless leftover data in the DC's screen buffer.
You can try p/invoking SetLayeredWindowAttributes
I strongly recommend reading the MSDN documentation on Layered Windows:
Here and here

Calling a function that involves PaintEventArgs when a button is clicked

Sorry if this has been asked before.
I was working on a simple connect 4 game in C# windows forms as I haven't done any work involving graphics before. To do this I need the program to draw circles when a button is pressed however I don't know how to call the function to do so.
public void printToken(PaintEventArgs pe, int x)
{
Graphics g = pe.Graphics;
Pen blue = new Pen(Color.Blue);
Pen red = new Pen(Color.Red);
Rectangle rect = new Rectangle(50 + ((x-1) * 100), 50, 50, 50);
g.DrawEllipse(blue, rect);
}
private void button1_Click(object sender, EventArgs e)
{
printToken(null, 1);
}
The null in place is just a placeholder as obviously that will not work.
Any and all help is appreciated.
Typically in a Windows Forms application where you want to do custom drawing, you either draw directly on a Form or PictureBox in the Paint event handler or create a subclass of Control in which you override the OnPaint method. In the Paint event handler or OnPaint, you draw everything (i.e. not just one circle, but all the circles). Then when your underlying data structure changes, indicating that you need a repaint, you call Invalidate() on the control, which marks it as needing redraw, and on the next pass through the event loop, your Paint event handler will run or your OnPaint method will be called. Within that method, you'll have the PaintEventArgs object you need to get a Graphics object with which to do your drawing. You don't want to "draw once and forget" (e.g. when a button is clicked) because there are all sorts of things that can cause your control to need to repaint itself. See this question and answer for more details on that.
Edit: here's some hand-holding in response to your comment. (It was going to be a comment but it got too long.)
If I assume for the moment that you're starting with a blank Form in Visual Studio's Windows Forms Designer, the quickest way to get going would just be to select the Form, and in VS's Properties pane, click the lightning bolt toolbar button to view the form's events. Scroll down to the Paint event and double-click anywhere in the blank space to the right of the label. That will wire up a Paint event handler for the form and take you to the newly added method in the form's code file. Use the PaintEventArgs object called e to do your drawing. Then, if you need to change what gets drawn upon some button click, in your click handler change the data that determine what gets drawn (e.g. positions and colors of of the playing pieces) and, when you're done, call Invalidate().
If you add a UserControl representing a single chip to your form, and override the OnPaint method and put the coloring code into that event, then the Graphics object will not be null when the Paint event fires.
To kick off the paint of the UserControl, either call the Refresh method or InvalidateRectangle method - either will fire the OnPaint method.
Make sure to call up the class hierarchy to the OnPaint first, to make sure that the background of the object is drawn first:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
Pen blue = new Pen(Color.Blue);
Pen red = new Pen(Color.Red);
Rectangle rect = new Rectangle(50 + ((x-1) * 100), 50, 50, 50);
g.DrawEllipse(blue, rect);
}

Refresh child controls in flowlayoutpanel when scrolling

In .NET 3.5 with winforms I'm making an image thumbnail viewer control.
The main control is derived from a FlowLayoutPanel which takes a list of images and displays them. The images which are displayed are made from a CustomControl on which I paint the and the accompanying label as well as the border of the control.
Images can be selected through clicking and yada yada as you would expect for that kind of control.
Here's a screenshote to illustrate:
That part works fine. The problem is then when I scroll the FlowLayoutPanel derived control the border doesn't redraw properly and there are lines remaining as shown in this screenshot:
I have set both the FlowLayoutPanel and the Images to double buffered. And the images and labels do not have the problem, so I suspect it is something else, but can't figure out what it is.
I think the method used to paint the border of the images might be at fault. Here's the code I use:
protected override void OnPaint(PaintEventArgs e)
{
Rectangle captionContainer;
captionContainer = new Rectangle();
if (!string.IsNullOrEmpty(this.Caption))
captionContainer = this.DrawCaption(e.Graphics);
if (this.Image != null)
this.DrawImage(e.Graphics, captionContainer);
this.Size = new Size(this.Padding.Horizontal + this.ImageSize.Width, this.Padding.Vertical + this.ImageSize.Height + captionContainer.Height);
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, this.currentBorderColor, ButtonBorderStyle.Solid);
base.OnPaint(e);
}
I'll post more code if needed, but it is pretty lengthy, so I do not want to put too much code unless it actually is necessary.
Can anybody see where this is going wrong?
I have solved by also drawing the border using the Graphics object. Replacing
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, this.currentBorderColor, ButtonBorderStyle.Solid);
with
e.Graphics.DrawRectangle(new Pen(this.currentBorderColor, 1F), new Rectangle(Point.Empty, new Size(this.Width - 1, this.Height - 1)));
does the trick. No idea why one works and not the other though...

How to delete a drawn circle in c# windows form?

I have drawn a circle in windows form
Pen pen = new Pen(Color.Black, 3);
Graphics gr = this.CreateGraphics();
gr.DrawEllipse(pen, 5,5,20,20);
How to delete it...
You have to clear your Graphic:
Graphics.Clear();
But all drawn figures will be cleared. Simply, you will then need to redraw all figures except that circle.
Also, you can use the Invalidate method:
Control.Invalidate()
It indicates a region to be redrawn inside your Graphics. But if you have intersecting figures you will have to redraw the figures you want visible inside the region except the circle.
This can become messy, you may want to check out how to design a control graph or use any graph layout library.
You can invalidate the draw region you want to refresh for example:
this.Invalidate();
on the form...
Assuming you're subscribing to the Paint event or overriding the protected OnPaint routine, then you will need to perform something like this:
bool paint = false;
protected override void OnPaint(object sender, PaintEventArgs e)
{
if (paint)
{
// Draw circle.
}
}
Then when you want to stop painting a circle:
paint = false;
this.Invalidate(); // Forces a redraw
You can make a figure of same dimensions using the backColor of your control in which you are drawing
use after your code to clear your figure.
Pen p = new Pen(this.BackColor);
gr.DrawEllipse(p, 5,5,20,20);
In fact, you can delete your circle and nothing but your circle.
Everything you need is something like a screenshot of the "before state" of the area you want to clear, to make a TextureBrush from it. You can achieve that step by something like this:
Bitmap _Background = new Bitmap(this.Width, this.Height);
Graphics.FromImage(_Background).CopyFromScreen(this.Left, this.Top, 0, 0, this.Size);
The first line will give you a bitmap in your windows forms size. The second line will save a screenshot of it in the _Background-bitmap.
Now you create a TextureBrush out of it:
Brush brsBackground = new TextureBrush(_Background);
The next thing you need are the dimensions of your circle, so you should save them into a variable, if they are not a fix value. When you got them at hand, you can clear the specific area like this:
Graphics gr = this.CreateGraphics();
gr.FillEllipse(brsBackground, 5, 5, 20, 20); // values referred to your example
Done!
Even complex figures are able to be deleted by this, like a GraphicsPath for example:
GraphicsPath gp = new GraphicsPath(); // any kind of GraphicsPath
gr.FillRegion(brsBackground, new Region(gp));
You don't "delete" it per se, there's nothing to delete. It's a drawing, you draw something else over it or you can call the Graphics.Clear() method.
If u are using Invalidate() and is not working, make a panel.Refresh().
That will work on you.
just make another control with the attributes etc. that you want, make the visibility to false and set the region of the control to the other control like this:
pen.Region = pen2.Region;
It is very simple to delete a drawn circle from c.
There is only four steps:-
Open turbo app
go to the command where you had drawn the circle
drag the command
click on delete button

c# - clear surface when resizing

I'm trying to build my own custom control for a windows forms application in C#.Net. Currently I paint some rectangles and other graphic elements using the paint event.
When I now resize the app form to fit the desktop size, all elements are repainted (which is exactly the behaviour I need) but the old one's are shown in the background.
Here's what I'm doing by now:
Pen penDefaultBorder = new Pen(Color.Wheat, 1);
int margin = 5;
private void CustomControl_Paint(object sender, PaintEventArgs e) {
CustomControl calendar = (CustomControl)sender;
Graphics graphics = e.Graphics;
graphics.Clear(Color.WhiteSmoke);
graphics.DrawRectangle(penDefaultBorder, margin, margin, calendar.Width - margin * 2, calendar.Height - margin * 2);
//...
}
Neither the graphics.Clear, nor adding a graphics.FillRectangle(...) will hide the old rectangle from the surface.
Ideas? Thank you all.
Paint events usually don't request an update for the entire canvas, just the area specified in the PaintEventArgs. I'm guessing what's happening is that only the newly-exposed regions of the canvas are being passed in the PaintEventArgs.
This one of the reasons that you shouldn't do any rendering in the Paint event. You should render to an offscreen bitmap - a buffer - and copy from that buffer to the control's canvas in the Paint event.
Searching for "double buffering" here or on Google will give you many examples of the technique.
Have you tried .Invalidate() to cause the form to redraw?

Categories