More advanced question - how to form subroutines or classes which Ican use on several forms

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

C

Some of my program components will draw plots of various kinds for my
calculations. I have now been able to plot functions on to a Form by
using Dim g as Graphics = Graphics.FromImage(frmImage) in cmdPlot1 and
then by drawing on to g lines, rectangles, etc. Form_Paint contains
e.Graphics.DrawImage(frmImage, 0, 0). All this seems to work fine.

The problem is that I will want to do this on several forms. It would
be a bit stupid to copy the same code to all the forms where this will
be done. I will have the x coordinates in XX(501) and y coordinates in
Y1(501). The ranges of the X and Y have been determined, and plot area
is 600 by 300 pixels on most forms. First I join all the points with
DrawLine which will plot the function. Then another piece of code
(used to be a subroutine in VB6) will draw horizontal and vertical
lines to make it easier to read the values. I am trying to figure out
how I could write general enough routines which can sit in a Module
which I could call from several forms. I will appreciate your help. My
guess:

Public Sub curve_plot(ByRef g, XX(), Y1(), Xmin, Xmax, Ymin, Ymax)
then plot the curve on g

Public Sub horizontal_lines(ByRef g, Ymin, Ymax, DY)
then draw lines on g

Will this work or do I need to get the information to these two
routines about which Form is using the routines?
 
Some of my program components will draw plots of various kinds for my
calculations. I have now been able to plot functions on to a Form by
using Dim g as Graphics = Graphics.FromImage(frmImage) in cmdPlot1 and
then by drawing on to g lines, rectangles, etc. Form_Paint contains
e.Graphics.DrawImage(frmImage, 0, 0). All this seems to work fine.

The problem is that I will want to do this on several forms. It would
be a bit stupid to copy the same code to all the forms where this will
be done. I will have the x coordinates in XX(501) and y coordinates in
Y1(501). The ranges of the X and Y have been determined, and plot area
is 600 by 300 pixels on most forms. First I join all the points with
DrawLine which will plot the function. Then another piece of code
(used to be a subroutine in VB6) will draw horizontal and vertical
lines to make it easier to read the values. I am trying to figure out
how I could write general enough routines which can sit in a Module
which I could call from several forms. I will appreciate your help. My
guess:

Public Sub curve_plot(ByRef g, XX(), Y1(), Xmin, Xmax, Ymin, Ymax)
       then plot the curve on g

Public Sub horizontal_lines(ByRef g, Ymin, Ymax, DY)
       then draw lines on g

Will this work or do I need to get the information to these two
routines about which Form is using the routines?

Or do I need g as well as frmImage in the subroutine arguments? As you
can see, I have not been able to understand these concepts well.

Several of you have been very helpful, and I hope this will feel less
like a beginner's question to you. Thanks.
 
Am 02.08.2010 22:19, schrieb C:
Some of my program components will draw plots of various kinds for my
calculations. I have now been able to plot functions on to a Form by
using Dim g as Graphics = Graphics.FromImage(frmImage) in cmdPlot1 and
then by drawing on to g lines, rectangles, etc. Form_Paint contains
e.Graphics.DrawImage(frmImage, 0, 0). All this seems to work fine.

The problem is that I will want to do this on several forms. It would
be a bit stupid to copy the same code to all the forms where this will
be done. I will have the x coordinates in XX(501) and y coordinates in
Y1(501). The ranges of the X and Y have been determined, and plot area
is 600 by 300 pixels on most forms. First I join all the points with
DrawLine which will plot the function. Then another piece of code
(used to be a subroutine in VB6) will draw horizontal and vertical
lines to make it easier to read the values. I am trying to figure out
how I could write general enough routines which can sit in a Module
which I could call from several forms. I will appreciate your help. My
guess:

Public Sub curve_plot(ByRef g, XX(), Y1(), Xmin, Xmax, Ymin, Ymax)
then plot the curve on g

Public Sub horizontal_lines(ByRef g, Ymin, Ymax, DY)
then draw lines on g

Will this work or do I need to get the information to these two
routines about which Form is using the routines?

Should work. Any problem found? (BTW, you should pass the values ByVal)
(I prefer classes with shared methods because the auto-import of all
Module members makes the project lose some structure).

But I'd probably write a class "Chart" with a "Render" method that renders
on the internal Bitmap. Then you can render that Bitmap on the Form.
Example:

Public Class Chart
Public ReadOnly Bitmap As Bitmap

Public Sub New(ByVal Size As Size)
Bitmap = New Bitmap(Size.Width, Size.Height)
End Sub

Public Sub Render(ByVal Values As Point())

Using g = Graphics.FromImage(Bitmap)
g.Clear(Color.Black)

For Each Value In Values
g.DrawLine(Pens.White, Value.X, Value.Y, Value.X + 1, Value.Y)
Next
End Using

End Sub
End Class
 
Am 02.08.2010 22:24, schrieb C:
Or do I need g as well as frmImage in the subroutine arguments?

If you don't need frmImage in the sub it not needs to be passed.
As you
can see, I have not been able to understand these concepts well.

It's simple:
The graphics object is the painter.
The Form or Bitmap is the painting.

What you get in e.Graphics is an already employed painter painting on a Form/Control.
Outside the Paint event, if you need a painter painting on a Control, call the Control's
CreateGraphics method.

To get a painter painting on an Image, call graphics.FromImage.

Don't forget to dispose the Graphics objects that you created yourself
by calling it's dispose method. Or use the Using statement:

using g=graphics.fromimage(myimage)
g. ...
end using
 
The problem is that I will want to do this on several forms. It would
be a bit stupid to copy the same code to all the forms where this will
be done.

You want to do one of three things: create a sub in a module that does
what you want, create a class that forms can call that does what you
want, or create a baseform from which your several forms inherit and
have the baseform do what you want.

All will work, which works best is partly aesthetics and partly
dependent upon your usage. So we can't really tell you which way to do
it.

What I will tell you is that at some time or another you should use
each, just so you understand them. Whether this is the time to use a
method you haven't used before or not...well that's up to you.
 
Public Sub curve_plot(ByRef g, XX(), Y1(), Xmin, Xmax, Ymin, Ymax)
then plot the curve on g

Public Sub horizontal_lines(ByRef g, Ymin, Ymax, DY)
then draw lines on g


Don't use ByRef, use ByVal, and you need to declare a type for each of
the input parameters.
 
Am 02.08.2010 22:19, schrieb C:












Should work. Any problem found?  

It did not. This is what I did.

Sub draw_h_lines(ByRef g1 As Graphics, ByVal IYmin As Single,
ByVal IYmax As Single, ByVal DY As Single)

Dim yscale As Single
Dim nl As Short, k As Short
Dim Ylowest As Single, yk As Single, yy3 As Single

yscale = 300.0 / (IYmax - IYmin)

nl = Int((IYmax - IYmin) / DY + 0.0001)

Ylowest = Int(IYmin / DY) * DY

For k = 1 To nl + 1 ' but no plotting too near the edges
yk = Ylowest + CSng(k) * DY
' yy3 = plate_Height - (yk - Ymin) * y2scl
yy3 = 300 - (yk - IYmin) * yscale
If yy3 > 240 And yy3 < 300 - 240 Then ' else lines will be
too close to the edges
g1.DrawLine(Pens.Green, 100, yy3, 700, yy3)
End If
Next k

End Sub

(BTW, you should pass the values ByVal)

That will not allow me to write or draw on g, so I passed it ByRef.
(I prefer classes with shared methods because the auto-import of all
Module members makes the project lose some structure).

I am hoping to form better habits and have good practices, which is
why I ask, but many of these things still seem too advanced for me.
But I'd probably write a class "Chart" with a "Render" method that renders
on the internal Bitmap. Then you can render that Bitmap on the Form.
Example:

   Public Class Chart
      Public ReadOnly Bitmap As Bitmap

      Public Sub New(ByVal Size As Size)
         Bitmap = New Bitmap(Size.Width, Size.Height)
      End Sub

      Public Sub Render(ByVal Values As Point())

         Using g = Graphics.FromImage(Bitmap)
            g.Clear(Color.Black)

            For Each Value In Values
               g.DrawLine(Pens.White, Value.X, Value.Y, Value.X + 1, Value.Y)
            Next
         End Using

      End Sub
   End Class

This will just draw things on the Bitmap. Should I then stick it on
the Form in Paint with e.Graphics.DrawImage(Bitmap)?
 
Don't use ByRef, use ByVal, and you need to declare a type for each of
the input parameters.

If I use ByVal, I won't be able to draw on g. Perhaps this is where I
am mistaken: g is not something to be drawn on, but something to be
drawn with. In that case, I should pass the frmImage as a parameter.
But then how do I draw on frmImage with g in this subroutine?
 
C explained on 8/3/2010 :
If I use ByVal, I won't be able to draw on g.

Why would you say that? A graphics object is a reference type, so
unless you are actually going to assign a new graphics object to g,
passing ByVal is the better option.

I know your probably still thinking VB6 where ByRef was the default,
but that was to avoid the perforamnce hit of a call to AddRef and
Release everytime you passed the object around :) We aren't in the
reference counted com world anymore :)
 
Am 03.08.2010 16:54, schrieb C:
It did not. This is what I did.

In what way did it not work?

Sub draw_h_lines(ByRef g1 As Graphics, ByVal IYmin As Single,
ByVal IYmax As Single, ByVal DY As Single)

Dim yscale As Single
Dim nl As Short, k As Short
Dim Ylowest As Single, yk As Single, yy3 As Single

yscale = 300.0 / (IYmax - IYmin)

nl = Int((IYmax - IYmin) / DY + 0.0001)

Ylowest = Int(IYmin / DY) * DY

For k = 1 To nl + 1 ' but no plotting too near the edges
yk = Ylowest + CSng(k) * DY
' yy3 = plate_Height - (yk - Ymin) * y2scl
yy3 = 300 - (yk - IYmin) * yscale
If yy3 > 240 And yy3 < 300 - 240 Then ' else lines will be
too close to the edges
g1.DrawLine(Pens.Green, 100, yy3, 700, yy3)
End If
Next k

End Sub

I can't compile it. You didn't enable Option Strict. You should start
getting aware of data types. That's what programming is all about. You can't
move data around blindly and hope that conversions will be done automatically
saving you from thinking about what your doing. (sorry for speaking so intensely ;-) )
So...:

Dim nl As Short, k As Short

No need to save memory. ;) Changed to Integer.

yscale = 300.0 / (IYmax - IYmin)

300.0 is a Double literal. This makes the expression a Double value.
You can't assign a Double to a Single variable. Changed it to 300.0F.

nl = Int((IYmax - IYmin) / DY + 0.0001)

The Int function rounds but does not change the data type. Therefore
you can't assign the return value to an integer variable. => added CInt()
If yy3 > 240 And yy3 < 300 - 240 Then

"And" changed to "AndAlso".


Final sub:

Shared Sub draw_h_lines(ByVal g1 As Graphics, ByVal IYmin As Single, _
ByVal IYmax As Single, ByVal DY As Single)

Dim yscale As Single
Dim nl, k As Integer
Dim Ylowest, yk, yy3 As Single

yscale = 300.0F / (IYmax - IYmin)

nl = CInt(Int((IYmax - IYmin) / DY + 0.0001F))

Ylowest = Int(IYmin / DY) * DY

For k = 1 To nl + 1 ' but no plotting too near the edges
yk = Ylowest + CSng(k) * DY
' yy3 = plate_Height - (yk - Ymin) * y2scl
yy3 = 300 - (yk - IYmin) * yscale
If yy3 > 240 AndAlso yy3 < 300 - 240 Then ' else lines will be too close to the edges
g1.DrawLine(Pens.Green, 100, yy3, 700, yy3)
End If
Next k

End Sub


That will not allow me to write or draw on g, so I passed it ByRef.

See Tom's reply.

But I'd probably write a class "Chart" with a "Render" method that renders
on the internal Bitmap. Then you can render that Bitmap on the Form.
Example:

Public Class Chart
Public ReadOnly Bitmap As Bitmap

[...]
End Class

This will just draw things on the Bitmap. Should I then stick it on
the Form in Paint with e.Graphics.DrawImage(Bitmap)?

Yep, as I wrote above: "Then you can render that Bitmap on the Form."

dim chart as new chart(clientsize)

chart.render(....)

e.graphics.drawimageunscaled(chart.bitmap, ....)

You don't have to create the Chart object in each Paint event.
You can create it whenever the Form is resized. Depends on the application.
 
C said:
If I use ByVal, I won't be able to draw on g. Perhaps this is where I
am mistaken: g is not something to be drawn on, but something to be
drawn with. In that case, I should pass the frmImage as a parameter.
But then how do I draw on frmImage with g in this subroutine?

No g is just a reference (address) of the object.

If you use byref it has an extra reference to make it able also to contain a
new reference to an object.

The object stays all the time until it is released by the Garbage Collector
in the same place.
 
Am 03.08.2010 16:54, schrieb C:



In what way did it not work?

It did not draw the lines. I am appending the corrected code below -
subroutine and the calling routine. I have several small questions -
this VB.net is crazy. I guess I could have learnt this faster if I did
not know VB6.
I can't compile it. You didn't enable Option Strict.

Where should I have Option Strict? At the top of the module or each
module and each Form?

You should start
getting aware of data types. That's what programming is all about. You can't
move data around blindly and hope that conversions will be done automatically

That is how it was before VB.net..
saving you from thinking about what your doing. (sorry for speaking so intensely ;-) )
So...:


No need to save memory. ;) Changed to Integer.


300.0 is a Double literal. This makes the expression a Double value.
You can't assign a Double to a Single variable. Changed it to 300.0F.

This is also news to me. Nowhere did I read that now we will have to
write numbers with a F at the end. Or perhaps we should be using only
double precision.
The Int function rounds but does not change the data type. Therefore
you can't assign the return value to an integer variable. => added CInt()

Would CInt have been enough instead of CInt(Int())?

"And" changed to "AndAlso".
Why?


Final sub:

   Shared Sub draw_h_lines(ByVal g1 As Graphics, ByVal IYmin As Single, _
      ByVal IYmax As Single, ByVal DY As Single)

      Dim yscale As Single
      Dim nl, k As Integer
      Dim Ylowest, yk, yy3 As Single

      yscale = 300.0F / (IYmax - IYmin)

      nl = CInt(Int((IYmax - IYmin) / DY + 0.0001F))

      Ylowest = Int(IYmin / DY) * DY

      For k = 1 To nl + 1 ' but no plotting too near the edges
         yk = Ylowest + CSng(k) * DY
         '            yy3 = plate_Height - (yk - Ymin) * y2scl
         yy3 = 300 - (yk - IYmin) * yscale
         If yy3 > 240 AndAlso yy3 < 300 - 240 Then ' else lineswill be too close to the edges
            g1.DrawLine(Pens.Green, 100, yy3, 700, yy3)
         End If
      Next k

   End Sub

Now I have

Sub draw_h_lines(ByVal g1 As Graphics, ByVal IYmin As Single,
ByVal IYmax As Single, ByVal DY As Single)

Dim yscale As Single
Dim nl As Short, k As Short
Dim Ylowest As Single, yk As Single, yy3 As Single

yscale = 300.0F / (IYmax - IYmin)

nl = CInt(Int((IYmax - IYmin) / DY + 0.0001F))

' Ymin may not be at a multiple of DY if the user has set
it.
' In that case, lines should be drawn from the lowest
multiple of DY
' if that is not too close to the bottom of the plate

Ylowest = CInt(Int(IYmin / DY)) * DY

For k = 1 To nl + 1 ' but no plotting too near the edges
yk = Ylowest + CSng(k) * DY
yy3 = 300 - (yk - IYmin) * yscale
If yy3 > 240 AndAlso yy3 < 300 - 240 Then ' else lines
will be too close to the edges
g1.DrawLine(Pens.Green, 100, yy3, 700, yy3)
End If
Next k

End Sub

and calling it is this routine from Form3 (call is close to the end of
this routine):

Private Sub cmdPlot_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles cmdPlot.Click

Dim g As Graphics = Graphics.FromImage(frmImage)
Dim XX(501) As Single, Y1(501) As Single
Dim i As Short, xxx1 As Single, yyy1 As Single
Dim k As Short, xxx2 As Single, yyy2 As Single
Dim xmin As Single, xmax As Single, ymin As Single, ymax As
Single
Dim IXmin As Single, IXmax As Single, IXmid As Single
Dim IYmin As Single, IYmax As Single, IYmid As Single
Dim IIN As Single, IOUT As Single
Dim Xsave As Single
Dim DX As Single, DY As Single
Dim xscale As Single, yscale As Single

IIN = List1.SelectedIndex + 1
IOUT = List2.SelectedIndex + 1
Xsave = X(IIN)
ymin = 1.0E+30
ymax = -1.0E+30
If Trim(TextBox1.Text) = "" Then
xmin = Xlim(IIN, 1)
Else
xmin = CSng(TextBox1.Text)
End If
If Trim(TextBox2.Text) = "" Then
xmax = Xlim(IIN, 2)
Else
xmax = CSng(TextBox2.Text)
End If
Call find_range(xmin, xmax, IXmin, IXmax, DX)
IXmid = 0.5 * (IXmin + IXmax)
lblLeft.Text = IXmin
lblRight.Text = IXmax
lblMiddle.Text = IXmid
Debug.Print("xmin = " & xmin)
Debug.Print("IXmin = " & IXmin)
For i = 1 To 501
XX(i) = xmin + CSng(i - 1) * (xmax - xmin) / 500.0
X(IIN) = XX(i)
Call calculatingfunction()
Y1(i) = Y(IOUT)
If Y(IOUT) < ymin Then ymin = Y(IOUT)
If Y(IOUT) > ymax Then ymax = Y(IOUT)
Next i
X(IIN) = Xsave
Call find_range(ymin, ymax, IYmin, IYmax, DY)
IYmid = 0.5 * (IYmin + IYmax)
lblTop.Text = IYmax
lblBottom.Text = IYmin
lblMid.Text = IYmid
Debug.Print("ymin = " & ymin)
Debug.Print("IYmin = " & IYmin)
xscale = 600.0 / (IXmax - IXmin)
yscale = 300.0 / (IYmax - IYmin)

' This part I would like to put in another general subroutine,
and later a class

For i = 1 To 500
xxx1 = (XX(i) - IXmin) * xscale + 100
yyy1 = 300 - (Y1(i) - IYmin) * yscale + 90
xxx2 = (XX(i + 1) - IXmin) * xscale + 100
yyy2 = 300 - (Y1(i + 1) - IYmin) * yscale + 90
g.DrawLine(Pens.Firebrick, xxx1, yyy1, xxx2, yyy2)
Next i
k = 1
Call draw_h_lines(g, IYmin, IYmax, DY)

Me.Invalidate() : g.Dispose()
End Sub



See Tom's reply.

Yes. I did.
But I'd probably write a class "Chart" with a "Render" method that renders
on the internal Bitmap. Then you can render that Bitmap on the Form.
Example:
   Public Class Chart
      Public ReadOnly Bitmap As Bitmap
[...]
   End Class
This will just draw things on the Bitmap. Should I then stick it on
the Form in Paint with e.Graphics.DrawImage(Bitmap)?

Yep, as I wrote above: "Then you can render that Bitmap on the Form."

     dim chart as new chart(clientsize)

     chart.render(....)

     e.graphics.drawimageunscaled(chart.bitmap, ....)

You don't have to create the Chart object in each Paint event.
You can create it whenever the Form is resized. Depends on the application.

Thanks. I hope to learn a few things from this plotting code.
 
C explained on 8/3/2010 :



Why would you say that?  A graphics object is a reference type, so
unless you are actually going to assign a new graphics object to g,
passing ByVal is the better option.
OK.


I know your probably still thinking VB6 where ByRef was the default,
but that was to avoid the perforamnce hit of a call to AddRef and
Release everytime you passed the object around :)  We aren't in the
reference counted com world anymore :)

It takes time and effort to wean away from VB6.

Thanks.
 
No g is just a reference (address) of the object.
OK.


If you use byref it has an extra reference to make it able also to contain a
new reference to an object.

...which is unnecessary in my case.
The object stays all the time until it is released by the Garbage Collector
in the same place.

Yes, I guess you mean g.Dispose().

Thanks.
 
Am 03.08.2010 20:18, schrieb C:
Where should I have Option Strict? At the top of the module or each
module and each Form?

Menu Tools -> Options: projects and solutions -> VB standard: Option Strict: ON
That's the default setting for projects created *in future*.

For existing projects, you can set it in the project's properties on the "compile"
tab. This setting can be overwritten on a per-file basis by putting "Option Strict..."
at the top of a source code file.
This is also news to me. Nowhere did I read that now we will have to
write numbers with a F at the end. Or perhaps we should be using only
double precision.


See topic 2.4.3 in the language specification. Unfortunatelly it's not available
in the online MSDN library for VB 2008, but for VB 2003:
http://msdn.microsoft.com/en-us/library/aa711650(VS.71).aspx

Here's the download for VB 2008:
http://www.microsoft.com/downloads/...9F5A95EF500&amp;displaylang=en&displaylang=en

You see, "F" (=float) is the suffix for Single literals and "R" (=real) for
Double literals. It's equal to "!" resp. "#" as you know it from VB6, but as
for some of the other data types there's only a _letter_ available as a suffix
(eg. "US" for unsigned short), I prefer the letter for all literals.

Would CInt have been enough instead of CInt(Int())?

CInt(1.7) = 2
Int(1.7) = 1

I didn't intend to change the meaning of your code, therefore I
didn't remove Int().

The "And" operator always evaluates both operands whereas the
AndAlso operator evaluates the 2nd operand only if the first operand
is True. So, AndAlso saves execution time. In this case, "AndAlso" is
equal to

If yy3 > 240 Then
If yy3 < 300 - 240 Then
End If
End If

That's how you would have written it in VB6, too, if you optimize the line.
Code:
[/QUOTE]

If you draw "frmImage" in the paint event, it should work. Does it now?

After enabling Option Strict, you will be pointed to some necessary
conversions/corrections. For example, you can't assign a Single value to a
property of type String. There are numerous number formats that could be
used for conversion. Have a look at Single.ToString, for example

lblLeft.Text = IXmin.ToString("0.00")


"Numeric Format Strings":
http://msdn.microsoft.com/en-us/library/427bttx3(VS.90).aspx

As your intention was to reuse the code in several Forms, I still suggest
a "Chart" class that encapsulates the whole painting procedure.
 
Yes, I guess you mean g.Dispose().
No and I've seen Armin writing this.

g.dispose removes only the unmanaged resources from an object, which is in
the case of a graphic object important.

g.dispose does not release an object, that does the Garbage Collector.

Although the dispose method is beside the acceptchanges probably the most
misunderstood method.
 
No and I've seen Armin writing this.

g.dispose removes only the unmanaged resources from an object, which is in
the case of a graphic object important.

g.dispose does not release an object, that does the Garbage Collector.

OK. There is plenty to learn. VB.net does have too many new things.
 
Am 03.08.2010 20:18, schrieb C:


Menu Tools -> Options: projects and solutions -> VB standard: Option Strict: ON
That's the default setting for projects created *in future*.

For existing projects, you can set it in the project's properties on the "compile"
tab. This setting can be overwritten on a per-file basis by putting "Option Strict..."
at the top of a source code file.

Thanks.


The subroutine still does not draw the lines. What am I still doing
wrong?


Sub draw_h_lines(ByVal g1 As Graphics, ByVal frmImage As Bitmap,
ByVal IYmin As Single, ByVal IYmax As Single, ByVal DY As Single) '
Should I have frmImage here? *****************

Dim yscale As Single
Dim nl As Short, k As Short
Dim Ylowest As Single, yk As Single, yy3 As Single

g1 = Graphics.FromImage(frmImage) ' Is this necessary here?
****************
yscale = 300.0F / (IYmax - IYmin)

nl = CInt(Int((IYmax - IYmin) / DY + 0.0001F))

Ylowest = CInt(Int(IYmin / DY)) * DY

For k = 1 To nl + 1 ' but no plotting too near the edges
yk = Ylowest + CSng(k) * DY
yy3 = 300 - (yk - IYmin) * yscale
If yy3 > 240 AndAlso yy3 < 300 - 240 Then ' else lines
will be too close to the edges
g1.DrawLine(Pens.Green, 100, yy3, 700, yy3)
End If
Next k

End Sub

Then in the calling routine, I have

Private Sub cmdPlot_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles cmdPlot.Click

Dim g As Graphics = Graphics.FromImage(frmImage)

dozens of lines here deleted

For i = 1 To 500
xxx1 = (XX(i) - IXmin) * xscale + 100
yyy1 = 300 - (Y1(i) - IYmin) * yscale + 90
xxx2 = (XX(i + 1) - IXmin) * xscale + 100
yyy2 = 300 - (Y1(i + 1) - IYmin) * yscale + 90
g.DrawLine(Pens.Firebrick, xxx1, yyy1, xxx2, yyy2)
Next i

Call draw_h_lines(g, frmImage, IYmin, IYmax, DY) ' Should I
have g as well as frmImage passed to draw_h_lines? ***************

Me.Invalidate() : g.Dispose()

End Sub
This is also news to me. Nowhere did I read that now we will have to
write numbers with a F at the end. Or perhaps we should be using only
double precision.

See topic 2.4.3 in the language specification. Unfortunatelly it's not available
in the online MSDN library for VB 2008, but for VB 2003:http://msdn.microsoft.com/en-us/library/aa711650(VS.71).aspx

Here's the download for VB 2008:http://www.microsoft.com/downloads/details.aspx?FamilyID=39DE1DD0-F77...

You see, "F" (=float) is the suffix for Single literals and "R" (=real) for
Double literals. It's equal to "!" resp. "#" as you know it from VB6, butas
for some of the other data types there's only a _letter_ available as a suffix
(eg. "US" for unsigned short), I prefer the letter for all literals.
Would CInt have been enough instead of CInt(Int())?

   CInt(1.7) = 2
   Int(1.7) = 1

I didn't intend to change the meaning of your code, therefore I
didn't remove Int().

The "And" operator always evaluates both operands whereas the
AndAlso operator evaluates the 2nd operand only if the first operand
is True. So, AndAlso saves execution time. In this case, "AndAlso" is
equal to

   If yy3 > 240  Then
      If yy3 < 300 - 240 Then
      End If
   End If

That's how you would have written it in VB6, too, if you optimize the line.
Code:
[/QUOTE]

If you draw "frmImage" in the paint event, it should work. Does it now?[/QUOTE]

It does not. I have the following in Paint.


Private Sub Form3_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles Me.Paint

Dim rect As New Rectangle(100, 90, 600, 300)
e.Graphics.FillRectangle(Brushes.LightCyan, rect)
e.Graphics.DrawImage(frmImage, 0, 0)
e.Graphics.DrawRectangle(Pens.LightGreen, rect)

End Sub
[QUOTE]
After enabling Option Strict, you will be pointed to some necessary
conversions/corrections. For example, you can't assign a Single value to a
property of type String. There are numerous number formats that could be
used for conversion. Have a look at Single.ToString, for example

  lblLeft.Text = IXmin.ToString("0.00")

"Numeric Format Strings":http://msdn.microsoft.com/en-us/library/427bttx3(VS.90).aspx

As your intention was to reuse the code in several Forms, I still suggest
a "Chart" class that encapsulates the whole painting procedure.[/QUOTE]

This will be nice to learn. I first need to learn to crawl (which too
seems like a huge task), then may be learn to walk, but I definitely
intend to learn to run some day, not far in future. Thanks for your
help.
 
Am 04.08.2010 10:11, schrieb C:
The subroutine still does not draw the lines. What am I still doing
wrong?


Sub draw_h_lines(ByVal g1 As Graphics, ByVal frmImage As Bitmap,
ByVal IYmin As Single, ByVal IYmax As Single, ByVal DY As Single) '
Should I have frmImage here? *****************

Dim yscale As Single
Dim nl As Short, k As Short
Dim Ylowest As Single, yk As Single, yy3 As Single

g1 = Graphics.FromImage(frmImage) ' Is this necessary here?
****************

No, this overwrites the passed graphics object! You'd better remove the line.


Then in the calling routine, I have

Private Sub cmdPlot_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles cmdPlot.Click

Dim g As Graphics = Graphics.FromImage(frmImage)

dozens of lines here deleted

For i = 1 To 500
xxx1 = (XX(i) - IXmin) * xscale + 100
yyy1 = 300 - (Y1(i) - IYmin) * yscale + 90
xxx2 = (XX(i + 1) - IXmin) * xscale + 100
yyy2 = 300 - (Y1(i + 1) - IYmin) * yscale + 90
g.DrawLine(Pens.Firebrick, xxx1, yyy1, xxx2, yyy2)
Next i

Call draw_h_lines(g, frmImage, IYmin, IYmax, DY) ' Should I
have g as well as frmImage passed to draw_h_lines? ***************

Why should you pass frmImage? You don't need it inside the method (after removing
the line as written above).

Code:
[/QUOTE]

If you draw "frmImage" in the paint event, it should work. Does it now?[/QUOTE]

It does not. I have the following in Paint.


Private Sub Form3_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles Me.Paint

Dim rect As New Rectangle(100, 90, 600, 300)
e.Graphics.FillRectangle(Brushes.LightCyan, rect)
e.Graphics.DrawImage(frmImage, 0, 0)
e.Graphics.DrawRectangle(Pens.LightGreen, rect)

End Sub[/QUOTE]

I don't see a flaw in your code. Don't know why it doesn't work.
Q: Do you see anything at all when frmImage is painted? Is it a
black rectangle? Do you only see the lines that you draw
directly in cmdPlot_Click (in color Pens.Firebrick), or do
you only not see the lines drawn in draw_h_lines?

[QUOTE]
This will be nice to learn. I first need to learn to crawl (which too
seems like a huge task), then may be learn to walk, but I definitely
intend to learn to run some day, not far in future. Thanks for your
help.[/QUOTE]

There is nothing more basic than minding the data types.
You are also doing yourself a favor if you put the right code in the right
classes, right from the start. It's not advanced, it's fundamental.
 
Am 04.08.2010 10:02, schrieb C:
Yep. :-) We had this one or two times.



C, let's develop this magic "Dispose" thing step by step. Then it's not
as complicated as it appears at first sight:

Step #1:
In general, it's good programming practice to free unneeded ressources ASAP.
I think we agree.

Objects, like instances of classes, occupy memory. They are called "managed"
ressources because the memory manager of the .Net Framework knows them all.
The garbage collector (GC) destroys unusable objects from time to
time. So, we don't have to care about the memory management.

Some of the managed objects reserve unmanaged ressources, for example
window handles. The managed object must free these ressources because
the GC does not know of their existence. At the latest, this must be done
straight before the object is destroyed. Therefore, add a method named
"Finalize" to your class. This method is called by the GC straight before
the object is destroyed, so it's a good place to free the unmanaged ressources
reserved by the object.

For now, that's actually everything you need to know. So far, nothing about "Dispose".


Step #2:
If you look at what we have now, you may wonder: Isn't it bad that these
unmanaged ressources are reserved til the object, that isn't needed anymore,
will be destroyed one day by the GC? Shouldn't we clean up ressources *ASAP*?
You're right!
The solution is: Add a clean-up method freeing unmanaged ressources to the
class. Call the method as soon as you know that you don't need the object
anymore. This does not destroy the managed object, but at least the unmanaged
ressources previously reserved by it. This is an improvement because we now
have a *deterministic* way to free *unmanaged* ressources (in opposite to the
*non-determinist* way to free *managed* ressources by the GC).

As there is no guarantee that the user of our class explicitly called the
clean-up method before the GC destroys the object, we should also
call the clean-up method from the Finalize method. (we use a Boolean
switch to see if the method has already been called before, so there
is no need to clean up anything if it has already been done)


Step #3:
An Interface, as you may have learned already, describes a bunch of
members. It's a mean for standardization. A class implementing an Interface
has all the Interface's members. This is to be able to "plug-in" different
types of objects into the same socket as long as they meet the standard.

Back to our memory management topic: Sometimes it is required to
call the clean-up method of an object or a list of objects. The problem
is: The name of the clean-up method is not yet standardized! It could
be named different in every class. For example, if we have a list of objects
that should all be cleand up, how can we handle the situation without
knowing the name of their clean-up methods?

For these situations, the "IDisposable" Interface has been
introduced. It has only one method called "Dispose". By implementing
the Interface in a class, you make the object "disposable". Now
you are able to store different types of objects in the same list.
The type of the items in the list is just IDisposable. Later, you
can write a loop iterating over all the items in the list and call
their "Dispose" method.

Another example is the "Using" statement:

using g = graphics.fromimage(...)
'...
end using

At "End using", g.Dispose is called internally. The Using statement specifies
that the type of g *must* implement the IDisposable interface. By dictating
this standard, there is a way to handle the situation independent from the
instance type of the object.

At the end, I once more point out to the fact that IDisposable is about
*determinstic* clean-up of *unmanaged* ressources and there is zero relation
to the *non-deterministic* work of the GC.
 
Back
Top