Different ways for persistent graphics in VB.net

  • Thread starter Thread starter C
  • Start date Start date
C

C

I have confused myself by reading too much. There are too many ways of
AutoReDraw = True in VB.net.

New Graphics object is said to be expensive in terms of memory. Why
not dispose after use?

On Paint does not seem to get updated.

I tried Graphics.FromImage way but it does not persist. Obviously, I
have a lot to learn in VB.net.

I have a lot of forms which plot either points or curves depending on
what the user asks for (clicks). I am trying to figure out a simple
way of modifying the VB6 code. The part I am not sure about is (1) how
best to create persistent graphics, and (2) whether I should create
public classes of plots with two methods - for points and for curves
from an array of points. Any comments will be appreciated.
 
C said:
I have confused myself by reading too much. There are too many ways of
AutoReDraw = True in VB.net.

New Graphics object is said to be expensive in terms of memory. Why
not dispose after use?

On Paint does not seem to get updated.

I tried Graphics.FromImage way but it does not persist. Obviously, I
have a lot to learn in VB.net.

I have a lot of forms which plot either points or curves depending on
what the user asks for (clicks). I am trying to figure out a simple
way of modifying the VB6 code. The part I am not sure about is (1) how
best to create persistent graphics, and (2) whether I should create
public classes of plots with two methods - for points and for curves
from an array of points. Any comments will be appreciated.

All painting must happen in the Paint event. But in practice this should
simply involve restoring to the screen the image that has already been
constructed.

Eg, in Paint you would have:
e.Graphics.DrawImage(myImage, 0, 0)

Where myImage has previously been created as the completed image of the
correct size. Eg,

at Form level
Dim myImage as Bitmap

and, in the method that does the drawing,
myImage = New Bitmap(myWidth, myHeight)
Dim G as Graphics = Graphics.FromImage(myImage)
G.Draw<whatever>

Depending on how your images are displayed, events such as resize may
require a call to the drawing method. You can dispose of the graphics object
if you want - it's a personal choice that makes little difference to the
procedure.in this case. I don't know whether or not the graphics object
really is expensive in terms of memory, but I can't think of a reason why it
should be. Note that the graphics object is not the image - it's the bitmap
that is the image. The graphics object just gives you something with which
to manipulate the image.

Whether or not your plots should be in classes and whether or not your
classes should be public cannot be determined without much more knowledge of
what you are trying to do. But moving a Sub into a Class is a trivial
exercise, and whether it's public or not does not effect your current
application, so I would start by coding that first plot as a Sub.
 
All painting must happen in the Paint event. But in practice
this should simply involve restoring to the screen the image
that has already been constructed.
Eg, in Paint you would have:
e.Graphics.DrawImage(myImage, 0, 0)
Where myImage has previously been created as the
completed image of the correct size.

I'm a VB6 programmer myself and I'm just "nosing around" in here but surely
your suggestion would waste a lot of processor time redrawing the entire
image at every Paint event, even if only a very small portion of it had been
invalidated, in which case it is /not/ the equivalent of the VB6 Autoredraw
method which in fact repaints only the "dirty" rectangle, something that is
usually very small by comparison. To achieve the same effect as VB6
Autoredraw in VB.Net, and to avoid wasting a lot of time (which is important
in all cases but which is especially important in Vista where the video
card's 2D hardware acceleration has in almost all cases been deliberately
disabled), you would want to redraw only the "dirty rectangle" in the Paint
event, details of which which I assume are provided in the VB.Net Paint
event's System.Windows.Forms.PaintEventArgs parameter.

Mike
 
All painting must happen in the Paint event. But in practice this should
simply involve restoring to the screen the image that has already been
constructed.

Eg, in Paint you would have:
e.Graphics.DrawImage(myImage, 0, 0)

Where myImage has previously been created as the completed image of the
correct size. Eg,

at Form level
Dim myImage as Bitmap

and, in the method that does the drawing,
myImage = New Bitmap(myWidth, myHeight)
Dim G as Graphics = Graphics.FromImage(myImage)
G.Draw<whatever>

Depending on how your images are displayed, events such as resize may
require a call to the drawing method. You can dispose of the graphics object
if you want - it's a personal choice that makes little difference to the
procedure.in this case.  I don't know whether or not the graphics object
really is expensive in terms of memory, but I can't think of a reason whyit
should be.  Note that the graphics object is not the image - it's the bitmap
that is the image.  The graphics object just gives you something with which
to manipulate the image.

Whether or not your plots should be in classes and whether or not your
classes should be public cannot be determined without much more knowledgeof
what you are trying to do. But moving a Sub into a Class is a trivial
exercise, and whether it's public or not does not effect your current
application, so I would start by coding that first plot as a Sub.
--
Jeff Richards
MS MVP (Windows - Shell/User)- Piilota siteerattu teksti -

- Näytä siteerattu teksti -

Thanks for your clear and concise reply. Things are still not that
clear for me however, so please let me ask you a couple of related
questions.

With what you suggested, I get graphics to persist. However, when I
plot the next line, the previous line gets erased. How is that
happening? (Code appended) I had to add Me.Refresh() or Me.Invalidate
(), otherwise the line would not appear on the visible part of the
Form at all.

I expected Me.Height (no ScaleHeight in VB.net, I guess) to mean the
height without the header, since (0, 0) is at the upper left corner of
the usable area of the Form. I don't quite figure out where the lower
right corner is.

How large can my myImage bitmap be? I guess that is not limited to the
size of the screen, in which case, I could draw in a larger area at
times and then store the myImage as a bitmap or jpg.

This is what I did:

Public Class Form1
Dim myImage As Bitmap = New Bitmap(100, 100) ' will resize later

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
' nothing yet
End Sub

Private Sub Form1_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles Me.Paint
e.Graphics.DrawImage(myImage, 0, 0)
End Sub

Private Sub Button1_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Button1.Click
myImage = New Bitmap(600, 600)
Dim g As Graphics = Graphics.FromImage(myImage)
g.DrawLine(Pens.Blue, 10, 10, Me.Width + 100, Me.Height + 100)
Me.Invalidate()
End Sub
End Class

I have still not completely understood how FromImage function writes
to Graphics object g and then Form_Paint shows that on refreshing.
Could you recommend any site on Internet where I can read up? Thank
you very much for your help.
 
I'm a VB6 programmer myself and I'm just "nosing around" in here but surely
your suggestion would waste a lot of processor time redrawing the entire
image at every Paint event, even if only a very small portion of it had been
invalidated, in which case it is /not/ the equivalent of the VB6 Autoredraw
method which in fact repaints only the "dirty" rectangle, something that is
usually very small by comparison. To achieve the same effect as VB6
Autoredraw in VB.Net, and to avoid wasting a lot of time (which is important
in all cases but which is especially important in Vista where the video
card's 2D hardware acceleration has in almost all cases been deliberately
disabled), you would want to redraw only the "dirty rectangle" in the Paint
event, details of which which I assume are provided in the VB.Net Paint
event's System.Windows.Forms.PaintEventArgs parameter.

Mike

I too don't like MS (except Bill Gates, who seems to be a very good
fellow), but they have really led the development quite effectively.
Those who know .net have told me that it is a clearly superior
technology and it would be better to learn it rather than go for
Delphi or something to avoid learning the new framework.

After having read .net stuff for about a week or little more, I can
see a lot of plus points - you have access to practically everything
with less effort than APIs; object orientation makes a lot of code
more efficient and reuse easier. I have been advised to learn it, even
though programming is not even expected of people like me.

Since you are a good VB6 programmer, you will probably be able to
learn .net faster than most others, and the only thing you have to
tolerate is that idiots at MS have foisted upon us a partly new
language in a misleading manner, attempting to fool us to believe that
it was some kind of an upgrade which it is not. Once you have learnt
it, there will be no looking back. Remember, Basic is still the same;
only the Visual part is somewhat different in VB.net. What's better,
VB Express 2008 is free and very few people will need the extra
features which are in VB.net. VB Express is another misnomer. It
should have been called VB UltraSuperSlow.
 
C said:
With what you suggested, I get graphics to persist. However, when I
plot the next line, the previous line gets erased. How is that
happening?

Because you create a new bitmap each time inside button1_click. Use the
one you've declared at class level.
Code appended) I had to add Me.Refresh() or Me.Invalidate
(), otherwise the line would not appear on the visible part of the
Form at all.

Yes. Nobody knows what you've painted in the Paint event. Graphics are
not persistent (that's why there's this event /OnPaint method). If
you've changed what you've displayed and what to display the latest
image, you have to calling Invalidate.
(Or you can call the Form's CreateGraphics method and paint on the
returned Graphics object.)
I expected Me.Height (no ScaleHeight in VB.net, I guess) to mean the
height without the header, since (0, 0) is at the upper left corner of
the usable area of the Form. I don't quite figure out where the lower
right corner is.

See ClientSize/ClientRectangle properties.
How large can my myImage bitmap be?

Try it.
I guess that is not limited to the
size of the screen,

I think so, too.
in which case, I could draw in a larger area at
times and then store the myImage as a bitmap or jpg.
...
I have still not completely understood how FromImage function writes
to Graphics object g and then Form_Paint shows that on refreshing.
Could you recommend any site on Internet where I can read up? Thank
you very much for your help.


A Graphics object paints on whatever, a Form, a printer, a Bitmap.
There's no magic. FromImage doesn't write anything. It just creates a
Graphics object you can use to paint on the Image; like
Form.CreateGraphics creates a Graphics object to draw on the Form.

Armin
 
I too don't like MS (except Bill Gates, who seems to be a very good
fellow [lots of stuff snipped] . . . [but] after having read .net stuff
for
about a week or little more, I can see a lot of plus points - you have
access to practically everything with less effort than APIs . . . [etc]

Hang on a minute! What has any of that got to do with the post of mine to
which you are responding? I wasn't "knocking" VB.Net in that post (although
I do admit that I have knocked it in the past in some other threads and I
prefer not to use it myself). I was merely suggesting that if you are going
to use the VB.Net Paint event to achieve a persistent graphic then it would
be wise to do so in an efficient manner by making use of the "dirty
rectangle" details that I assume are provided in the Paint event's
System.Windows.Forms.PaintEventArgs parameter rather than simply drawing the
entire image every time. In fact I've just had a quick look to confirm what
I suspected and the VB.Net PaintEventArgs parameter does indeed pass you the
size and location of the "dirty rectangle" [check out the value returned by
e.ClipRectangle.Left (and Right and Top and Bottom) in some VB.Net Paint
event code]. That should enable your code to use much less processing time
by drawing /only/ the normally very small "dirty rectangle" portion of your
memory bitmap to the Form or whatever in response to each Paint event, and
in doing so your code should work in much the same way as the VB6 Autoredraw
mechanism, which is what you asked for in your original post. Do you have
something against me making such a suggestion?

Mike
 
I too don't like MS (except Bill Gates, who seems to be a very good
fellow [lots of stuff snipped] . . . [but] after having read .net stuff
for
about a week or little more, I can see a lot of plus points - you have
access to practically everything with less effort than APIs . . . [etc]

Hang on a minute! What has any of that got to do with the post of mine to
which you are responding?

It is easy to smell your dislike for the impostors. So I wrote some
comments.
I wasn't "knocking" VB.Net in that post (although
I do admit that I have knocked it in the past in some other threads and I
prefer not to use it myself). I was merely suggesting that if you are going
to use the VB.Net Paint event to achieve a persistent graphic then it would
be wise to do so in an efficient manner by making use of the "dirty
rectangle" details that I assume are provided in the Paint event's
System.Windows.Forms.PaintEventArgs parameter rather than simply drawing the
entire image every time. In fact I've just had a quick look to confirm what
I suspected and the VB.Net PaintEventArgs parameter does indeed pass you the
size and location of the "dirty rectangle" [check out the value returned by
e.ClipRectangle.Left (and Right and Top and Bottom) in some VB.Net Paint
event code]. That should enable your code to use much less processing time
by drawing /only/ the normally very small "dirty rectangle" portion of your
memory bitmap to the Form or whatever in response to each Paint event, and
in doing so your code should work in much the same way as the VB6 Autoredraw
mechanism, which is what you asked for in your original post.
Thanks.

Do you have
something against me making such a suggestion?

I have nothing against it. In fact, it is a good suggestion and I am
going to try to understand how it works.
 
C,

For a good overview of GDI+, try:
http://www.bobpowell.net/faqmain.htm
With what you suggested, I get graphics to persist. However, when I
plot the next line, the previous line gets erased. How is that
happening?
This is because, in your button_click event you're creating a new bitmap
object every time. You need to remove the line:
myImage = New Bitmap(600, 600)
.... and just use the bitmap you created initially. Every time you create a
new bitmap, the old one (complete with all the drawing you did on it) is
lost.

Also, on this line:
Dim g As Graphics = Graphics.FromImage(myImage)

You're creating a new Graphics object, but you're not releasing it. You
either need to call "g.Dispose" at the end of that routine, or enclose your
code in a Using block. Otherwise you'll leak memory.
(Code appended) I had to add Me.Refresh() or Me.Invalidate
(), otherwise the line would not appear on the visible part of the
Form at all.
Which is correct behaviour. You have more control over when & how your form
repaints itself that way, which allows you to perform more off-screen
drawing (thus reducing flicker).
How large can my myImage bitmap be? I guess that is not limited to the
size of the screen, in which case, I could draw in a larger area at
times and then store the myImage as a bitmap or jpg.
Absolutely correct. Your form can be 100x100 and your image can be ten
times that size. The limit is memory.

If you want some decent redraw performance though, I'd recommend against
using JPEG - that would require compression/decompression during each
creation/paint event, which will reduce drawing performance (and likely
induce flickering).
I have still not completely understood how FromImage function writes
to Graphics object g and then Form_Paint shows that on refreshing.
Well, in simple terms:

You have a form-level variable, myImage, that you want to paint onto your
form.

When you click your button, your code creates a graphics object with which
to work on that image. Think of this as temporarily taking a canvas down
off the wall, setting up your paints and putting on your smock.

You perform your drawing onto that "canvas" (myImage)

You dispose of your Graphics object (put away your paintbrushes and smock,
tidy up after yourself)

In the Paint event of your form, you then place that canvas back up on the
wall for the world to see (you draw the image onto your form).

Using a form level bitmap (like you've done) is a fairly good way to emulate
persistent graphics for your form. The nice thing is, you get to control
how you want to cache your bitmap and when you want to redraw it.

Hope this helps,
Alex
 
C,

For a good overview of GDI+, try:http://www.bobpowell.net/faqmain.htm

I will. Thanks.
With what you suggested, I get graphics to persist. However, when I
plot the next line, the previous line gets erased. How is that
happening?



This is because, in your button_click event you're creating a new bitmap
object every time.  You need to remove the line:
myImage = New Bitmap(600, 600)
... and just use the bitmap you created initially.  Every time you create a
new bitmap, the old one (complete with all the drawing you did on it) is
lost.

Yes, it was silly of me not to notice it. I took this in from the few
lines you wrote, found that myImage had to be defined before, so I
created a New bitmap in the Form class, and then forgotten what I had
written in button_click..
Also, on this line:
Dim g As Graphics = Graphics.FromImage(myImage)

You're creating a new Graphics object, but you're not releasing it.  You
either need to call "g.Dispose" at the end of that routine, or enclose your
code in a Using block.  Otherwise you'll leak memory.

Yes, I have read that and will make it a habit to dispose unnecessary
stuff or use "Using"
(Code appended) I had to add Me.Refresh() or Me.Invalidate
(), otherwise the line would not appear on the visible part of the
Form at all.



Which is correct behaviour.  You have more control over when & how yourform
repaints itself that way, which allows you to perform more off-screen
drawing (thus reducing flicker).



How large can my myImage bitmap be? I guess that is not limited to the
size of the screen, in which case, I could draw in a larger area at
times and then store the myImage as a bitmap or jpg.



Absolutely correct.  Your form can be 100x100 and your image can be ten
times that size.  The limit is memory.
Great.


If you want some decent redraw performance though, I'd recommend against
using JPEG - that would require compression/decompression during each
creation/paint event, which will reduce drawing performance (and likely
induce flickering).

There might be situations when I store a lot of plots, say curves of
results at each iteration. In such cases, bitmaps might consume too
much memory, but OK. In general, I will avoid JPEGs.
I have still not completely understood how FromImage function writes
to Graphics object g and then Form_Paint shows that on refreshing.



Well, in simple terms:

You have a form-level variable, myImage, that you want to paint onto your
form.

When you click your button, your code creates a graphics object with which
to work on that image.  Think of this as temporarily taking a canvas down
off the wall, setting up your paints and putting on your smock.

What I don't understand is how writing on to a Graphics object changes
the myImage. myImage is a bitmap. myImage is neither a property nor a
subset of the Graphics object. But any change in the Graphics object
ends up as a change in the myImage bitmap without my asking for it.

When I do G.DrawLine, the Graphics object gets modified. What has that
to do with myImage bitmap? How does that bitmap know that I intend to
change something in it? This Graphics.FromImage(myImage) is probably
what is doing that but this is precisely what I don't understand. We
did that FromImage operation at some other point when Graphics was
operated by the function FromImage (or the other way round?). How does
it know to update itself later on? Or is this function FromImage
saying that the Graphics and bitmap objects are permanently related
and any change in one is reflected as a change in the other? Like the
Equivalence declaration in Fortran?
You perform your drawing onto that "canvas" (myImage)

You dispose of your Graphics object (put away your paintbrushes and smock,
tidy up after yourself)

In the Paint event of your form, you then place that canvas back up on the
wall for the world to see (you draw the image onto your form).

Yes, but we are drawing myImage bitmap there. How did myImage get
updated with the line I drew on g?
Using a form level bitmap (like you've done) is a fairly good way to emulate
persistent graphics for your form.  The nice thing is, you get to control
how you want to cache your bitmap and when you want to redraw it.

Hope this helps,
Alex

Thanks for your help.
 
I guess that is [my Image bitmap] is not limited to the
size of the screen, in which case, I could draw in a larger
area at times and then store the myImage as a bitmap or
jpg. . . . There might be situations when I store a lot of
plots, say curves of results at each iteration. In such cases,
bitmaps might consume too much memory, but OK. In
general, I will avoid JPEGs.

If you are concerned mostly with drawing real time graphics purely for
display purposes then bitmaps are usually the best way to go, but if you are
drawing mostly lines and plots and curves and fills of various kinds and if,
as you have suggested, you want to save the result as an image file then you
would probably be better off drawing them to a metafile and saving them as a
metafile (.emf for example) instead of a bitmap or a jpg. Metafiles usually
have a very small file size and they automatically take full advantage of
the resolution of the selected output device when they are drawn.

Mike
 
Yes, but we are drawing myImage bitmap there. How did myImage get
updated with the line I drew on g?

Don't think of the Graphics object, "g", as the image itself. Instead think
of it as an interface to the bitmap which allows you to perform drawing
functions on that object.

If you have a painting on the wall (your image on your form) that you want
to modify, you need paints, brushes, pencils etc. Think of these things as
the Graphics object itself.

You create a Graphics object to interface with your image
(Graphics.FromImage(myImage)) which gives you all your paints, stencils and
brushes with which to modify the image in question.

So when you call "g.DrawLine", you're telling the Graphics object itself to
draw a line onto the device (in this case your "myImage" variable) with
which it is associated.

Does that help any?

Cheers,
Alex
 
Don't think of the Graphics object, "g", as the image itself.  Instead think
of it as an interface to the bitmap which allows you to perform drawing
functions on that object.

If you have a painting on the wall (your image on your form) that you want
to modify, you need paints, brushes, pencils etc.  Think of these things as
the Graphics object itself.

You create a Graphics object to interface with your image
(Graphics.FromImage(myImage)) which gives you all your paints, stencils and
brushes with which to modify the image in question.

So when you call "g.DrawLine", you're telling the Graphics object itself to
draw a line onto the device (in this case your "myImage" variable) with
which it is associated.

Does that help any?

Cheers,
Alex


It helps a little bit. I don't see the logic in it, however.
Graphics.FromImage creates a graphics object and we can draw lines on
it, but how does the image get updated? Unless the image "contains"
the graphics object with all its methods and properties, or the other
way round, it should be necessary to give some instruction to myImage
to update itself from the graphics object. This is not being done, and
I don't see this as very logical. Graphics.DrawImage in Form_Paint is
simply displaying on the Form what it has in the image. So where does
myImage get updated when I draw in g?
 
I guess that is [my Image bitmap] is not limited to the
size of the screen, in which case, I could draw in a larger
area at times and then store the myImage as a bitmap or
jpg. . . . There might be situations when I store a lot of
plots, say curves of results at each iteration. In such cases,
bitmaps might consume too much memory, but OK. In
general, I will avoid JPEGs.

If you are concerned mostly with drawing real time graphics purely for
display purposes then bitmaps are usually the best way to go, but if you are
drawing mostly lines and plots and curves and fills of various kinds and if,
as you have suggested, you want to save the result as an image file then you
would probably be better off drawing them to a metafile and saving them as a
metafile (.emf for example) instead of a bitmap or a jpg. Metafiles usually
have a very small file size and they automatically take full advantage of
the resolution of the selected output device when they are drawn.

I am doing both. Some plots like surface plots take even a minute to
appear on the screen - calculations take most of the time.

Metafiles would be nice, but there do not seem to be any easy ways of
writing them from VB. Any information on this would be welcome.

What I thought is that I would produce another plot (one smaller one
to show on the screen and another for printing) with a huge bitmap to
allow 300 dpi for about 15 cm by 8 cm (about 1800 pixels by 1000
pixels) which can be saved as a bitmap and still be printed with
fairly good clarity. If 600 dpi is required, that too would be
feasible with 4 times as many pixels.
 
Because you create a new bitmap each time inside button1_click. Use the
one you've declared at class level.


Yes. Nobody knows what you've painted in the Paint event. Graphics are
not persistent (that's why there's this event /OnPaint method). If
you've changed what you've displayed and what to display the latest
image, you have to calling Invalidate.
(Or you can call the Form's CreateGraphics method and paint on the
returned Graphics object.)


See ClientSize/ClientRectangle properties.


Try it.


I think so, too.


A Graphics object paints on whatever, a Form, a printer, a Bitmap.
There's no magic. FromImage doesn't write anything. It just creates a
Graphics object you can use to paint on the Image; like
Form.CreateGraphics creates a Graphics object to draw on the Form.

Armin

--https://epetitionen.bundestag.de/index.php?action=petition;sa=details...

I thought you need a paint command to put your graphics on the Form or
other device. You seem to be saying that whatever I draw on the
graphics object (if it is produced by a FromImage) gets written on the
image. In that case, is the graphics object a subset of the image, or
is the image a property of the graphics object created by FromImage?
Do all graphics objects (including Form.CreateGraphics) have an image
in them?

Thanks.
 
C said:
I thought you need a paint command to put your graphics on the Form or
other device.

Right, the paint command's are the methods of the Graphics object.
You seem to be saying that whatever I draw on the
graphics object (if it is produced by a FromImage) gets written on the
image.
Correct.

In that case, is the graphics object a subset of the image, or
is the image a property of the graphics object created by FromImage?
Do all graphics objects (including Form.CreateGraphics) have an image
in them?

I'm not sure about the internals. However, consider the Graphics object
being a tool to draw on something (Image, Form, Printer...). That
something is the canvas, the destination. Usually output is done on
devices like the screen (to a window) or a printer. An image is just an
alternative output destination.

IOW: The Graphics object is the painter. The device or an image is the
painting. You can have the painter paint on the painting by calling one
of the painter's Draw* methods.


Armin
 
What I thought is that I would produce another plot (one
smaller one to show on the screen and another for printing)
with a huge bitmap to allow 300 dpi for about 15 cm by
8 cm (about 1800 pixels by 1000 pixels) which can be
saved as a bitmap and still be printed with fairly good
clarity. If 600 dpi is required, that too would be
feasible with 4 times as many pixels [3600 x 2000].

A standard full colour (24 bits per pixel) bitmap file of a size 3600 x 2000
pixels would indeed be feasible, but it would be extremely large (about 20
MB) for what is effectively a copy of a small 15 x 8 cm drawing. Also you
would need to carefully consider the different line widths etc that would be
required for creating the bitmap so that they are suitable for drawing onto
a high resolution device. Converting such a bitmap to a jpeg would reduce
the file size somewhat, but at the expense of quality. On the other hand a
metafile (.emf) would be very small by comparison (up to a thousand times
smaller or even more, depending on the complexity of the drawing), and would
contain exactly the same drawing but in a way that allows it to be printed
at any size you desire to any output device whilst maintaining the full
output quality of the device to which you are drawing it.
Metafiles would be nice, but there do not seem to be
any easy ways of writing them from VB. Any information
on this would be welcome.

As I mentioned in my previous posts I am a VB6 programmer myself and I am
really just "nosing about" in here, so I have never written any code to
create metafiles in VB.Net. I have written such code in VB6 though, but
because VB6 does not have any native metafile functions it is necessary to
create one using the CreateEnhMetafile API function and draw into the handle
it returns using pens and brushes that need to be created using the various
CreatePen, CreateBrush and CreateFont etc API functions (due to the fact
that standard VB6 drawing methods cannot be used on a metafile handle
because VB6 knows nothing about them). In VB6 this is a little bit complex
but it is actually much simpler than it sounds once you have got the main
basic routine off the ground. However I'm pretty sure that VB.Net has native
functions to create and draw into metafiles and so VB.Net should do most of
that work for you. In fact I've just "Googled" for information on the
subject and this is the first hit I got. It looks like it could be exactly
what you need:

http://www.vb-helper.com/howto_net_make_show_metafile.html

Mike
 
C said:
snip <

I thought you need a paint command to put your graphics on the Form or
other device. You seem to be saying that whatever I draw on the
graphics object (if it is produced by a FromImage) gets written on the
image. In that case, is the graphics object a subset of the image, or
is the image a property of the graphics object created by FromImage?
Do all graphics objects (including Form.CreateGraphics) have an image
in them?

Thanks.

The graphics object is a collection of drawing (and other) methods gathered
together into a single object. When you create the graphics object you
'connect' it to a drawing surface - for instance, a bitmap. When you
execute a drawing method using the graphics object, that method is executed
using the drawing surface associated with the object. The graphics object
is not an image or a portion of an image. All graphics objects must be
associated with a drawing surface (such as an image, a screen, a printer)
because you can only create a graphics object by using a method that
connects it to the drawing surface you want to use it with.

I like to think of the graphics object as being a bit like the
keyboard/display unit that the motor mechanic plugs into the socket under my
vehicle dash panel when I take it in for service. It gives the mechanic an
ability to find out all sorts of things about the vehicle which I didn't
even know existed, and to make all sorts of changes to things that are
important to the operation of the vehicle, but which are otherwise
unavailable. The graphics object does the same sort of thing for the bitmap
or display or whatever that you are using it with.
 
What I thought is that I would produce another plot (one
smaller one to show on the screen and another for printing)
with a huge bitmap to allow 300 dpi for about 15 cm by
8 cm (about 1800 pixels by 1000 pixels) which can be
saved as a bitmap and still be printed with fairly good
clarity. If 600 dpi is required, that too would be
feasible with 4 times as many pixels [3600 x 2000].

A standard full colour (24 bits per pixel) bitmap file of a size 3600 x 2000
pixels would indeed be feasible, but it would be extremely large (about 20
MB) for what is effectively a copy of a small 15 x 8 cm drawing. Also you
would need to carefully consider the different line widths etc that would be
required for creating the bitmap so that they are suitable for drawing onto
a high resolution device. Converting such a bitmap to a jpeg would reduce
the file size somewhat, but at the expense of quality. On the other hand a
metafile (.emf) would be very small by comparison (up to a thousand times
smaller or even more, depending on the complexity of the drawing), and would
contain exactly the same drawing but in a way that allows it to be printed
at any size you desire to any output device whilst maintaining the full
output quality of the device to which you are drawing it.
Metafiles would be nice, but there do not seem to be
any easy ways of writing them from VB. Any information
on this would be welcome.

As I mentioned in my previous posts I am a VB6 programmer myself and I am
really just "nosing about" in here, so I have never written any code to
create metafiles in VB.Net. I have written such code in VB6 though, but
because VB6 does not have any native metafile functions it is necessary to
create one using the CreateEnhMetafile API function and draw into the handle
it returns using pens and brushes that need to be created using the various
CreatePen, CreateBrush and CreateFont etc API functions (due to the fact
that standard VB6 drawing methods cannot be used on a metafile handle
because VB6 knows nothing about them). In VB6 this is a little bit complex
but it is actually much simpler than it sounds once you have got the main
basic routine off the ground. However I'm pretty sure that VB.Net has native
functions to create and draw into metafiles and so VB.Net should do most of
that work for you. In fact I've just "Googled" for information on the
subject and this is the first hit I got. It looks like it could be exactly
what you need:

http://www.vb-helper.com/howto_net_make_show_metafile.html

Mike

It seems to be as simple as this:

' Make a metafile that can work with the hDC.
Dim mf As New Metafile(txtFile.Text, hdc, _
New Rectangle(0, 0, 210, 210), _
MetafileFrameUnit.Pixel)

' Make a Graphics object to work with the metafile.
Dim mf_gr As Graphics = Graphics.FromImage(mf)

' Draw on the metafile.
Dim the_pen As New Pen(Color.Blue, 5)
mf_gr.DrawRectangle(the_pen, 5, 5, 190, 190)

The key is to create Graphics.FromImage(metafilename)
and then draw on it. Finally, it is saved as gr.DrawImage(mf, 0, 0)

See, VB.net does have some added value. They might be fooling us with
the name, but this new thing is worthwhile learning.
 
A standard full colour (24 bits per pixel) bitmap file of a size 3600 x 2000
pixels would indeed be feasible, but it would be extremely large (about 20
MB) for what is effectively a copy of a small 15 x 8 cm drawing. Also you
would need to carefully consider the different line widths etc that would be
required for creating the bitmap so that they are suitable for drawing onto
a high resolution device. Converting such a bitmap to a jpeg would reduce
the file size somewhat, but at the expense of quality. On the other hand a
metafile (.emf) would be very small by comparison (up to a thousand times
smaller or even more, depending on the complexity of the drawing), and would
contain exactly the same drawing but in a way that allows it to be printed
at any size you desire to any output device whilst maintaining the full
output quality of the device to which you are drawing it.

If I want to create an animation of results (plots) at each iteration,
metafiles could be difficult to include in an AVI file. On the other
hand, I can use BMP2AVI to convert a series of bitmaps to an AVI.

As you notice, there are several things I do with the plots in
different situations. Metafiles will be nice for including in reports/
presentations written in Word or PowerPoint or OpenOffice. For
animations, I should find a good way. By the way, all this stuff I
have on VB6, but sooner or later, I will have to shift this to VB.net
 
Back
Top