How to manually get instance of Graphics object in WinForms? - c#

I know how to work with object of type Graphics (at least I am able to render images) but I always do that by passing graphics object retrieved from OnPaint method.
I would like to display an image when the app is opened (ie in Form_Load method) but have no clue how to obtain the instance of Graphics object I could use?
Thanks

Using the e.Graphics object that OnPaint() supplies to you is the correct way of doing it. It will run right after the OnLoad() method. The form isn't visible yet in OnLoad.
Getting a Graphics object from Control.CreateGraphics() is supported. However, whatever you draw with this will be wiped out as soon as the form repaints itself. Which happens when the user moves another window across yours (pre-Aero) or when she minimizes and restores or otherwise resizes the window. Use CreateGraphics only ever when animating at a high rate.

If you're attempting to create a graphics object from the surface of your form, you can use this.CreateGraphics
If you are attempting to create a new Image, you can always initialize an Image and then call Graphics.CreateGraphics.FromImage(YourImage) e.g.
Bitmap b = new Bitmap(100,100);
var g = Graphics.CreateGraphics.FromImage(b);
At this point, any drawing performed to your Graphics object will be drawn onto your image.

None of the preceding answers worked for me. I found Rajnikant Rajwadi solution effective (see https://social.msdn.microsoft.com/Forums/vstudio/en-US/ce90eb80-3faf-4266-b6e3-0082191793f7/creation-of-graphics-object-in-wpf-user-control?forum=wpf)
Here is a horribly condensed call to Graphics.MeasureString(). (please code more responsibly)
SizeF sf = System.Drawing.Graphics.FromHwnd(new System.Windows.Interop.WindowInteropHelper(this).Handle).MeasureString("w", new Font(TheControl.FontFamily.ToString(), (float)TheControl.FontSize));

And how do you plan to use the Graphics object you got in the Load event?
If you want to paint something on the screen, you have to be in the Paint event, or it will be cleared on the next paint.
What you can do: load another (simple) form, with just a picture, and hide it when your main form is loaded.
Since your Load event will probably run on the UI thread. Call DoEvents to make the other form appear.

form.CreateGraphics();
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.creategraphics.aspx
http://msdn.microsoft.com/en-us/library/5y289054.aspx

Related

How can I draw a big image in WinForms?

I'm trying to make a floor for a top down style game, where I used to use pictureboxes
I was told that instead of using a Picturebox, I should be using the Graphics.DrawImage(); method, which seems to slightly help but still is very laggy.
My paint function looks like this to draw the background looks like this:
How should I make the drawing less laggy? The image is 2256 by 1504
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(PeanutToTheDungeon.Properties.Resources.tutorialFloor, 0, 0);
}
There are two points that I would make here.
Firstly, DO NOT use Properties.Resources like that. Every time you use a property of that object, the data for that resource gets extracted from the assembly. That means that you are creating a new Image object every time the Paint event is raised, which is often. You should use that resource property once and once only, assign the value to a field and then use that field repeatedly. That means that you will save the time of extracting that data and creating the Image object every time.
The second point may or may not be applicable but, if you are forcing the Paint event to be raised by calling Invalidate or Refresh then you need to make sure that you are invalidating the minimum area possible. If you call Refresh or Invalidate with no argument then you are invalidating the whole for, which means that the whole form will be repainted. The repainting of pixels on screen is actually the slow part of the process, so that's the part that you need to keep to a minimum. That means that, when something changes on your game board, you need to calculate the smallest area possible that will contain all the possible changes and specify that when calling Invalidate. That will reduce the amount of repainting and speed up the process.
Note that you don't need to change your drawing code. You always DRAW everything, then the system will PAINT the part of the form that you previously invalidated.

How to prevent ExternalException which occurs on graphics object

I'm working on a project where control updates and new image is drawn on a panel after every 10 seconds. Following code clears that panel first. Then draws a border to it.
private void DrawRectangle(Color color)
{
using (var graphics = CreateGraphics())
using (var pen = new Pen(color))
{
graphics.Clear(Color.Black); //External exception is thrown here.
graphics.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1);
}
}
Normally everything works fine but if I lock windows(press Win + L) then after 10 seconds when graphics.Clear(Color.Black) statement is executed, application crashes.
According to MSDN page: The Clear method clears the state of the graphics object and should not be called when the graphics object cannot be updated. For example, if the Clear method is called on a secure desktop in a terminal server session, an ExternalException may occur, leaving the Graphics object in an inconsistent state.
What should I do to prevent this crash? Should I check if windows is locked or not? and will that be the only case where this crash will occur?
Update: Same problem occurs when Screen saver is activated.
If you call CreateGraphics() when windows is locked or screensaver is shown, the graphics object it returns is in Inconsistent state and can not be used to paint. If we use such graphics object, it throws ExternalException.
Best way to get rid of this problem is suggested by user Hans Passant. Instead of using CreateGraphics() method to create graphics object, use panel's paint event. Paint event handler contains eventargs which contains graphics object. This graphics object should be used to do painting.
The reason this works because, panel's paint event does not get called when windows is locked or screensaver is shown. So no inconsistent graphics object is used for painting.

User Control on Active Report

I know I can draw on Active Reports, but let's leave that as a last resort.
I have a user control where all I do is draw graphics in the OnPaint method (Since that is the only way I can get the graphics to show). I know Active Reports is static, but I would have liked it to paint once. Instead I just get the gray box. Is there a way to accomplish this, or am I stuck converting my stuff to draw directly in Active Reports? I tried suspending the OnPaint method after running the code that draws the graphics, but that didn't help.
ActiveReports does support hosting a .net component. No need to paint directly on AR surface. It is not an ideal scenario but it should work. We would need more details to diagnose why your user control is not working. which section are you placing it in? is it bound or static graphics? you should know that AR paints the controls at processing time, it binds the controls then paints them to its own format (RDF) then the viewer only understands that format (think of it like a sheet of paper). This means that you need to place your control on the report and have it render it, rather than trying to render it in the viewer.
Anyway, more details about your control and the report you're using it in would help. please contact us at activereports.support#grapecity.com and we'll do our best to help.
thanks
http://activereports.grapecity.com
After working with GrapeCity I was able to get it working after making these 3 changes (I'm sure it's just the last that matters):
Switched to inherit from Control instead of UserControl
Overrode the OnPaint method instead of just subscribing to the Paint event
used the PaintEventArg e's e.Graphics instead of this.CreateGraphics()
Then declare it all in the report like so:
private void SectionReport1_ReportStart(object sender, EventArgs e)
{
GrapeCity.ActiveReports.SectionReportModel.CustomControl cc;
MyControl myc = new MyControl();
cc = new GrapeCity.ActiveReports.SectionReportModel.CustomControl(myc.GetType());
cc.Location = new PointF(1f, 1f);
cc.Size = new SizeF(4f, 4f);
this.detail.Controls.Add(cc);
}

How to prevent PictureBox internally refreshes itself?

I have a very fast loop which renders animation in a Bitmap buffer and adds filter to it (by using LockBits/UnlockBits to access to the raw data and Marshaling changes to it.) in an independent thread.
I wanted to figure out a way to display the render on the Form, real-time, so I created a PictureBox and linked its Image to the bitmap I created. Everytime immediately after the bitmap is unlocked, I refreshed the PictureBox (using delegate, to do cross-threading) so that the rendering is updated properly.
It's totally fine and works very fast, but one big problem came out as I tried dragging the form to the border of the screen, to see if any bug would appear, and oops, the app collapse..saying 'the bitmap is being locked' This happens when either there's other window moving above the PictureBox or the PictureBox is dragged partially out of the screen. I suspice it because PictureBox can refresh itself when redraw is neccessary, and it does when the bitmap is still being locked. So...any way to sovle this problem? Or anyother ways to render the my animation better?
One of possible solutions could be is create your custom MyPictureBox : PictureBox (say) class which override OnPaintBackground, like this:
protected override OnPaintBackground(...)
{
// nothing, an empty method
}
But I'm not very sure that this will work, you should to check this by yourself.
What I would do, personally, considering your comment:
I have a very fast loop which renders animation in a Bitmap buffer and
adds filter to it (by using LockBits/UnlockBits to access to the raw
data and Marshaling changes to it.) in an independent thread
just forget about PictureBox, cause I found it, personally, too generic and non suitable for high performance rendering. Just write a simple class that handles the drawing of specified bitmap on specified surface.
Imo, this is a best choice.
You can't do that.
Instead, you should copy the image (on the background thread) and put the copy in the PictureBox.
For better performance, you can swap between two images to avoid creating too many images.

C# Graphics object

Question: How to check if Graphics object is usable.
My Problem: I create Graphics object from form and give it to "object X" for drawing, when form closes and "object X" tries to draw into Graphics object, GDI+ error occurs 0x80004005.
So i need to check if Graphics is drawable only having that Graphics object.
Any ideas?
The best way to draw objects is that handle the Paint event of the form. in the Paint() will you get access to Graphics which is drawable always. So you can use it without any problems.
When your form is closed you should inform your "object X" of this fact...
Otherwise the only way to know if a Graphics object is accessible is to call a small method on it like GetHdc (with the correct ReleaseHdc after if it succeed) and catch the error that may happens.
Nice idea with GetHdc and ReleaseHdc, VirtualBlackFox, lucks like it worked.
Good job.

Categories

Resources