Home All Groups Group Topic Archive Search About

PictureBox Scale Confusion

Author
1 Jun 2009 12:21 AM
David
I'm confused.

1)  Why do I need to rescale the picturebox to its actual pixel values in
order for API MoveTo, LIneTo to show the line on the picturebox?

2) How do I rescale the picturebox such that I'm able to obtain the
actual array values for the X axis "i" value such that their is a 1 to 1
relationship between the X value obtained from "i" and the points actually
plotted in #1 below?

=======  info follows ===============

!)  I'm setting the scale to draw a line in a PictureBox as follows

            picGraph.Scale (0, .089000)-(530, .047376)

             and then drawing a line using

             picGraph.Line (i, dblYData1(i))-(i + 1, dblYData1(i + 1))

             The line appears to be drawn and scaled correctly.

2)  I then wanted to drag a vertical line across the picturebox with the
MouseMove event.  In order for the API: MoveTo, LineTo functions to show the
line I had to rescale the picture box to its actual pixel dimensions
other wise the vertical line did Not show.

         frmBestFit.picGraph.Scale (0, 0)-(580, 799)

         After rescaling to the above the vertical line shows and drags
across the picturebox.

3)  Lastly I wanted to obtain the values for "i" as the the vertical line is
being dragged across the picturebox.  Using the same Scale values of

         frmBestFit.picGraph.Scale (0, 0)-(580, 799)

I error with "No Subscript" about 1/3 across the picturebox from .ScaleLeft.
Obviously the pixel points are closer together than
the line points plotted in #1.

Author
1 Jun 2009 10:49 AM
Michael Williams
"David" <dw85745***@earthlink.net> wrote in message
news:%23M0Sq7k4JHA.1712@TK2MSFTNGP03.phx.gbl...

> I'm confused.
> I'm setting the scale to draw a line in a PictureBox as follows
> picGraph.Scale (0, .089000)-(530, .047376)
> 1)  Why do I need to rescale the picturebox to its actual pixel
> values in order for API MoveTo, LIneTo to show the line on
> the picturebox?

The various Scale settings of a PictureBox determine how VB interprets the
coordinates when you use native VB drawing methods. The API (GDI) drawing
functions on the other hand continue to use their default units of pixels.
So, if you want to draw a line using the native VB Line method then you
should use your VB Scale coordinates and if you want to draw a Line using
the GDI methods (MoveTo and LineTo) and you are using something other than
the standard vbPixels ScaleMode then you need to perform the conversion from
Scale units to pixels in your code, passing the converted values (the pixel
values) to the GDI functions.

In fact this is pretty much what VB itself does "under the hood" when you
use a native VB drawing method. VB always leaves the underlying DC of the
PictureBox set to its default map mode of pixels regardless of the VB
ScaleMode and the VB Line method (or other VB drawing method) performs the
conversion from Scale Units to pixels "under the hood" before itself using
the GDI function to draw the line, passing it the calculated pixel values.
When you use the GDI LineTo function directly yourself (instead if using the
native VB Line method) then your code needs to perfom this otherwise "under
the hood" conversion itself. For example, in your own code you have set the
various Scale properties of the PictureBox using the line:

    picGraph.Scale (0, 0.089)-(530, 0.047376)

In order to draw a pixel at the centre of such a PictureBox using the native
VB Pset you might use something like:

  x = 265
  y = 0.06818
  picGraph.PSet (x, y), vbBlue

If you want to draw a pixel at the centre of the same PictureBox using the
API (GDI) SetPixel method then you need to perform in code the conversion
that VB itself would otherwise perform "under the hood" to convert the scale
units to pixels. The VB ScaleX and ScaleY functions can help you to perform
such a conversion. For example, using the same values of x and y as shown
above the code would be:

    x = 265
    y = 0.06818
    j = picGraph.ScaleX(x - picGraph.ScaleLeft, picGraph.ScaleMode,
vbPixels)
    k = picGraph.ScaleY(y - picGraph.ScaleTop, picGraph.ScaleMode, vbPixels)
    SetPixel picGraph.hdc, j, k, vbRed

[Watch for any "newsgroup wrapping" on the above lines]. Note that the
conversion code (as shown above) should include the ScaleLeft and ScaleTop
properties respectively. You can often get away without using them,
depending on the specific scale settings of your PictureBox, but it is
always best to use them otherwise the code will "fall down" in many cases,
as indeed it would in your specific case where you have altered one of the
origins to something other than zero. The code as shown above will work in
cases. There may of course be the occasional "single pixel" difference in
the output in certain circumstances on the grounds that the two methods may
round their various conversions slightly differently, but in general
everything should work as expected.

Otherwise, as you have discovered, if you are responding the the X and Y
coordinates reported by the various PictureBox events (MouseMove etc) and if
you prefer not to perform such a conversion in code then you can temporarily
set the ScaleMode back to pixels just while you are peforming that task.
Personally though I don't like swapping from one ScaleMode to another "on
the fly" as a general rule because it is easy to get things wrong.

As I have said, VB always leaves the underlying DC of the PictureBox set to
its default mapmode value of pixels regardless of the various VB ScaleModes
you are using, and VB performs the necessary conversions "on the fly" from
VB Scale units to pixels when performing any native VB drawing functions so
that it can pass pixel values to the various API routines that the native VB
drawing methods themselves use "under the hood". This means that, as
explained above, you must pass pixel values to the API functions (SetPixel,
LineTo, etc) if you are using them directly.

I would suggest that you leave it that way and perform any required
conversions in your code as explained above. However, it is actually
possible to change the units that the API routines themselves use, although
I wouldn't recommend doing that for general purpose work on the grounds that
VB itself always expects the mapmode of the DC to be set to pixel units and
if it finds it set to some other units when it performs its various
operations on the PictureBox then it all goes wrong. So, if you do decide to
set the underlying "API scale units" to something other than pixels you need
to be very careful to change them back again before VB itself performs any
drawing operations (Paint events or whatever) in the PictureBox.

If you want to look into changing the "API scale units" of a DC (a
PictureBox for example) then check out the SetMapMode and SetViewportExtEx
and various other associated API functions. These will enable you to change
from pixels (MM_TEXT) to twips or to various English and Metric units
(MM_HIMETRIC, MM_HIENGLISH etc) and also to the API equivalent of a user
scale mode and different ScaleLeft and ScaleTop properties (MM_ANISOTROPIC)
although it is not quite as simple as it at first sounds and in the case of
the "GDI user scale mode" (MM_ANISOTROPIC) you need to bear in mind the fact
that all API (GDI) drawing functions use Longs and you cannot therefore pass
floating point values to them. This means that you cannot effectively set
the DC so that it has a vertical size extending from 0.089 to 0.047376 units
as you have done in your VB Scale setting. You would need to instead use
values such as 89000 to 47376 (although since, no matter what you do, you
cannot actually plot a pixel at anything other than a whole pixel value
anyway the values 8900 to 4738 would be more than enough on any display).
That is one of the ways in which VB ScaleModes make things very easy,
because VB allows you to use floating point values for such things on the
grounds that it converts them to the equivalent pixel values "under the
hood" and passes the pixel values onto the underlying API (GDI) functions
that the VB drawing functions use.

Mike
Author
1 Jun 2009 1:03 PM
David
Thanks for excellent response Mr. Williams.
Didn't know about ScaleX and ScaleY so learned something new today.

Interestingly after using ScaleX and ScaleY the GDI vertical line showed and
appeared to be scaled correctly.  Also,  I was able to drag the GDI vertical
line with MouseMove when NO underlying graphics were plotted.

However, when underlying graphics are plotted, and the MouseMove event is
used  -- multiple vertical lines are being placed instead of just one line
which can be dragged across the PBox (I use vbInvert).

Consequently, it appears ScaleX and ScaleY are somehow conflicting with GDI
(MoveTo, LineTo) since the GDI (MoveTo, LineTo) worked previously when
rescaling to actual PBox pixel dimensions.  FWIW heres my new code:

[code]

        'NOTE:  Scale in set in routine which plots the underlying graphics.

'Altered GDI Code in MouseMove Event:

         With objPBox

               iY1 = objPBox.ScaleY(.ScaleTop - .ScaleTop, .ScaleMode,
vbPixels)
               iY2 = .ScaleTop + .ScaleHeight
               iY2 = objPBox.ScaleY(iY2 - .ScaleTop, .ScaleMode, vbPixels)

               'Erase Old
                miOldX = objPBox.ScaleX(miOldX - .ScaleLeft, .ScaleMode,
vbPixels)
                lngDummy = MoveToEx(.hDC, miOldX, iY1, pt)
                lngDummy = LineTo(.hDC, miOldX, iY2)

                'New
                iX1 = CLng(x)
                iX1 = objPBox.ScaleX(iX1 - .ScaleLeft, .ScaleMode, vbPixels)
                lngDummy = MoveToEx(.hDC, iX1, iY1, pt)
                lngDummy = LineTo(.hDC, iX1, iY2)

         End With

        miOldX = iX1

[/code]

Know of any place that the conversion functions or calculations are
available for ScaleX and ScaleY so I can manually implement and see what's
going on?

David


Show quoteHide quote
"Michael Williams" <M***@WhiskeyAndCoke.com> wrote in message
news:Ou98Naq4JHA.2656@TK2MSFTNGP05.phx.gbl...
>
> "David" <dw85745***@earthlink.net> wrote in message
> news:%23M0Sq7k4JHA.1712@TK2MSFTNGP03.phx.gbl...
>
>> I'm confused.
>> I'm setting the scale to draw a line in a PictureBox as follows
>> picGraph.Scale (0, .089000)-(530, .047376)
>> 1)  Why do I need to rescale the picturebox to its actual pixel
>> values in order for API MoveTo, LIneTo to show the line on
>> the picturebox?
>
> The various Scale settings of a PictureBox determine how VB interprets the
> coordinates when you use native VB drawing methods. The API (GDI) drawing
> functions on the other hand continue to use their default units of pixels.
> So, if you want to draw a line using the native VB Line method then you
> should use your VB Scale coordinates and if you want to draw a Line using
> the GDI methods (MoveTo and LineTo) and you are using something other than
> the standard vbPixels ScaleMode then you need to perform the conversion
> from Scale units to pixels in your code, passing the converted values (the
> pixel values) to the GDI functions.
>
> In fact this is pretty much what VB itself does "under the hood" when you
> use a native VB drawing method. VB always leaves the underlying DC of the
> PictureBox set to its default map mode of pixels regardless of the VB
> ScaleMode and the VB Line method (or other VB drawing method) performs the
> conversion from Scale Units to pixels "under the hood" before itself using
> the GDI function to draw the line, passing it the calculated pixel values.
> When you use the GDI LineTo function directly yourself (instead if using
> the native VB Line method) then your code needs to perfom this otherwise
> "under the hood" conversion itself. For example, in your own code you have
> set the various Scale properties of the PictureBox using the line:
>
>    picGraph.Scale (0, 0.089)-(530, 0.047376)
>
> In order to draw a pixel at the centre of such a PictureBox using the
> native VB Pset you might use something like:
>
>  x = 265
>  y = 0.06818
>  picGraph.PSet (x, y), vbBlue
>
> If you want to draw a pixel at the centre of the same PictureBox using the
> API (GDI) SetPixel method then you need to perform in code the conversion
> that VB itself would otherwise perform "under the hood" to convert the
> scale units to pixels. The VB ScaleX and ScaleY functions can help you to
> perform such a conversion. For example, using the same values of x and y
> as shown above the code would be:
>
>    x = 265
>    y = 0.06818
>    j = picGraph.ScaleX(x - picGraph.ScaleLeft, picGraph.ScaleMode,
> vbPixels)
>    k = picGraph.ScaleY(y - picGraph.ScaleTop, picGraph.ScaleMode,
> vbPixels)
>    SetPixel picGraph.hdc, j, k, vbRed
>
> [Watch for any "newsgroup wrapping" on the above lines]. Note that the
> conversion code (as shown above) should include the ScaleLeft and ScaleTop
> properties respectively. You can often get away without using them,
> depending on the specific scale settings of your PictureBox, but it is
> always best to use them otherwise the code will "fall down" in many cases,
> as indeed it would in your specific case where you have altered one of the
> origins to something other than zero. The code as shown above will work in
> cases. There may of course be the occasional "single pixel" difference in
> the output in certain circumstances on the grounds that the two methods
> may round their various conversions slightly differently, but in general
> everything should work as expected.
>
> Otherwise, as you have discovered, if you are responding the the X and Y
> coordinates reported by the various PictureBox events (MouseMove etc) and
> if you prefer not to perform such a conversion in code then you can
> temporarily set the ScaleMode back to pixels just while you are peforming
> that task. Personally though I don't like swapping from one ScaleMode to
> another "on the fly" as a general rule because it is easy to get things
> wrong.
>
> As I have said, VB always leaves the underlying DC of the PictureBox set
> to its default mapmode value of pixels regardless of the various VB
> ScaleModes you are using, and VB performs the necessary conversions "on
> the fly" from VB Scale units to pixels when performing any native VB
> drawing functions so that it can pass pixel values to the various API
> routines that the native VB drawing methods themselves use "under the
> hood". This means that, as explained above, you must pass pixel values to
> the API functions (SetPixel, LineTo, etc) if you are using them directly.
>
> I would suggest that you leave it that way and perform any required
> conversions in your code as explained above. However, it is actually
> possible to change the units that the API routines themselves use,
> although I wouldn't recommend doing that for general purpose work on the
> grounds that VB itself always expects the mapmode of the DC to be set to
> pixel units and if it finds it set to some other units when it performs
> its various operations on the PictureBox then it all goes wrong. So, if
> you do decide to set the underlying "API scale units" to something other
> than pixels you need to be very careful to change them back again before
> VB itself performs any drawing operations (Paint events or whatever) in
> the PictureBox.
>
> If you want to look into changing the "API scale units" of a DC (a
> PictureBox for example) then check out the SetMapMode and SetViewportExtEx
> and various other associated API functions. These will enable you to
> change from pixels (MM_TEXT) to twips or to various English and Metric
> units (MM_HIMETRIC, MM_HIENGLISH etc) and also to the API equivalent of a
> user scale mode and different ScaleLeft and ScaleTop properties
> (MM_ANISOTROPIC) although it is not quite as simple as it at first sounds
> and in the case of the "GDI user scale mode" (MM_ANISOTROPIC) you need to
> bear in mind the fact that all API (GDI) drawing functions use Longs and
> you cannot therefore pass floating point values to them. This means that
> you cannot effectively set the DC so that it has a vertical size extending
> from 0.089 to 0.047376 units as you have done in your VB Scale setting.
> You would need to instead use values such as 89000 to 47376 (although
> since, no matter what you do, you cannot actually plot a pixel at anything
> other than a whole pixel value anyway the values 8900 to 4738 would be
> more than enough on any display). That is one of the ways in which VB
> ScaleModes make things very easy, because VB allows you to use floating
> point values for such things on the grounds that it converts them to the
> equivalent pixel values "under the hood" and passes the pixel values onto
> the underlying API (GDI) functions that the VB drawing functions use.
>
> Mike
>
>
>
Author
1 Jun 2009 5:03 PM
Michael Williams
"David" <dw85745***@earthlink.net> wrote in message
news:eg2zplr4JHA.4184@TK2MSFTNGP02.phx.gbl...

> Thanks for excellent response Mr. Williams.

Mike will be fine ;-)

> Didn't know about ScaleX and ScaleY so learned something new
> today. Interestingly after using ScaleX and ScaleY the GDI vertical
> line showed and appeared to be scaled correctly.

Yes. That's what I was saying in my previous response. If you use ScaleX and
ScaleY in the correct way to convert the coordinates from the VB Scale units
to pixels then you will be able to pass the calculated pixel values to the
various API (GDI) drawing functions which by default use pixels regardless
of the VB Scale units you are using.

> Also, I was able to drag the GDI vertical line with MouseMove
> when NO underlying graphics were plotted. However, when
> underlying graphics are plotted, and the MouseMove event is used  -- 
> multiple vertical lines are being placed instead of just
> one line which can be dragged across the PBox (I use vbInvert).
> Consequently, it appears ScaleX and ScaleY are somehow
> conflicting with GDI (MoveTo, LineTo) since the GDI (MoveTo,
> LineTo) worked previously when rescaling to actual PBox pixel
> dimensions.

Actually the problem is highly unlikely to be caused by your use of ScaleX
and ScaleY unless you are using them incorrectly or your code is failing in
some other way. With these sort of things it is very easy to have problems
caused by a specific part of your code and to erroneously connect those
problems with some other part of your code which is not in itself causing
the problem, especially when you are trying new things.

> FWIW heres my new code:

I've looked at that but to be honest I always have problems following code
unless I can see all the variable declarations and other relevant things so
that I know both their type and their scope and also so that I know things
like whether or not you are using or manipulating the Autredraw property
anywhere in your code. So, rather than ask lots of questions about the code
you have posted here is an example that I've just written using a PictureBox
with its Scale properties set up exactly the same as your own and which uses
the ScaleX and ScaleY functions to calculate the appropriate pixel values
from the PicBox MouseMove VB Scale coordinates so that it can draw the line
using the MoveToEx and LineTo GDI methods that you are using yourself. To
check it out paste the code into a VB Form containing a PictureBox and
change the hard coded path "c:\temp1jan1.jpg" to a picture that exists on
your own system. Then run the code and click the left Mouse Button on the
Picture Box. It should draw a line and then move that line in response to
horizontal movement of the mouse whilst you are still holding down the
button. When you release the button the line will be left at the last
position at which it was drawn and you will then be able to click the mouse
button elsewhere on the PictureBox to start and drag another line. It all
works fine on my own system. Check it out and let mw know how it behaves on
your own.

Mike

Option Explicit
Private Declare Function MoveToEx Lib "gdi32" _
  (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, _
  lpPoint As POINTAPI) As Long
Private Declare Function LineTo Lib "gdi32" _
  (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) _
  As Long
Private Type POINTAPI
  x As Long
  y As Long
End Type
Private oldX As Long
Private pt1 As POINTAPI
Private picTop As Long, picBottom As Long

Private Sub Form_Load()
Me.Move 0, 0
Me.Show: Me.Refresh
With picGraph
picGraph.Scale (0, 0.089)-(530, 0.047376)
picGraph.DrawMode = vbCopyPen
picGraph.PaintPicture LoadPicture("c:\temp\jan1.jpg"), _
    .ScaleLeft, .ScaleTop, _
    .ScaleWidth, .ScaleHeight
  .DrawMode = vbInvert
  ' store the top and bottom pixel coordinates, which
  ' will remain the same unless we resize the PicBox
  picTop = .ScaleY(0, .ScaleMode, vbPixels)
  picBottom = .ScaleY(.ScaleHeight, .ScaleMode, vbPixels)
End With
End Sub

Private Sub picGraph_MouseDown(Button As Integer, _
      Shift As Integer, x As Single, y As Single)
If (Button And 1) = 1 Then
  With picGraph
    ' set oldX to initial position of line
    oldX = .ScaleX(x - .ScaleLeft, .ScaleMode, vbPixels)
    ' draw the initial line
    MoveToEx .hdc, oldX, picTop, pt1
    LineTo .hdc, oldX, picBottom
  End With
End If
End Sub

Private Sub picGraph_MouseMove(Button As Integer, _
      Shift As Integer, x As Single, y As Single)
If (Button And 1) = 1 Then
  With picGraph
  ' redraw the line at its previous position
  ' in order to erase it (vbInvert drawing)
  MoveToEx .hdc, oldX, picTop, pt1
  LineTo .hdc, oldX, picBottom
  ' set oldX to the pixel value of the new position
  oldX = .ScaleX(x - .ScaleLeft, .ScaleMode, vbPixels)
  ' draw the line at its new position
  MoveToEx .hdc, oldX, picTop, pt1
  LineTo .hdc, oldX, picBottom
  End With
End If
End Sub
Author
1 Jun 2009 10:33 PM
David
Thanks Mike.

Got mine working using your example.
My code reset OldX at end of MouseMove which was re-setting the original X
value rather than reusing the scaled X.

I now need to do a takeoff of the X values using the scale used to plot one
of the lines.  Hopefully no problems.

=================================

I do have one more question if you don't mind..

In MSChart you can click on the plotted line and it will be identified by
squares.

Only thing I can think of how this may be done is:

1)  Enum all plotted data sets, or
2)  Have a table of plotted dataset colors and use API: GetPixel to find
which dataset then reloop the dataset at a larger step increment to ID the
line.  This will work unless there are multiple lines of the same color.

Any ideas how this is done?



Show quoteHide quote
"Michael Williams" <M***@WhiskeyAndCoke.com> wrote in message
news:u9rfsrt4JHA.4632@TK2MSFTNGP02.phx.gbl...
> "David" <dw85745***@earthlink.net> wrote in message
> news:eg2zplr4JHA.4184@TK2MSFTNGP02.phx.gbl...
>
>> Thanks for excellent response Mr. Williams.
>
> Mike will be fine ;-)
>
>> Didn't know about ScaleX and ScaleY so learned something new
>> today. Interestingly after using ScaleX and ScaleY the GDI vertical
>> line showed and appeared to be scaled correctly.
>
> Yes. That's what I was saying in my previous response. If you use ScaleX
> and ScaleY in the correct way to convert the coordinates from the VB Scale
> units to pixels then you will be able to pass the calculated pixel values
> to the various API (GDI) drawing functions which by default use pixels
> regardless of the VB Scale units you are using.
>
>> Also, I was able to drag the GDI vertical line with MouseMove
>> when NO underlying graphics were plotted. However, when
>> underlying graphics are plotted, and the MouseMove event is used  -- 
>> multiple vertical lines are being placed instead of just
>> one line which can be dragged across the PBox (I use vbInvert).
>> Consequently, it appears ScaleX and ScaleY are somehow
>> conflicting with GDI (MoveTo, LineTo) since the GDI (MoveTo,
>> LineTo) worked previously when rescaling to actual PBox pixel
>> dimensions.
>
> Actually the problem is highly unlikely to be caused by your use of ScaleX
> and ScaleY unless you are using them incorrectly or your code is failing
> in some other way. With these sort of things it is very easy to have
> problems caused by a specific part of your code and to erroneously connect
> those problems with some other part of your code which is not in itself
> causing the problem, especially when you are trying new things.
>
>> FWIW heres my new code:
>
> I've looked at that but to be honest I always have problems following code
> unless I can see all the variable declarations and other relevant things
> so that I know both their type and their scope and also so that I know
> things like whether or not you are using or manipulating the Autredraw
> property anywhere in your code. So, rather than ask lots of questions
> about the code you have posted here is an example that I've just written
> using a PictureBox with its Scale properties set up exactly the same as
> your own and which uses the ScaleX and ScaleY functions to calculate the
> appropriate pixel values from the PicBox MouseMove VB Scale coordinates so
> that it can draw the line using the MoveToEx and LineTo GDI methods that
> you are using yourself. To check it out paste the code into a VB Form
> containing a PictureBox and change the hard coded path "c:\temp1jan1.jpg"
> to a picture that exists on your own system. Then run the code and click
> the left Mouse Button on the Picture Box. It should draw a line and then
> move that line in response to horizontal movement of the mouse whilst you
> are still holding down the button. When you release the button the line
> will be left at the last position at which it was drawn and you will then
> be able to click the mouse button elsewhere on the PictureBox to start and
> drag another line. It all works fine on my own system. Check it out and
> let mw know how it behaves on your own.
>
> Mike
>
> Option Explicit
> Private Declare Function MoveToEx Lib "gdi32" _
>  (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, _
>  lpPoint As POINTAPI) As Long
> Private Declare Function LineTo Lib "gdi32" _
>  (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) _
>  As Long
> Private Type POINTAPI
>  x As Long
>  y As Long
> End Type
> Private oldX As Long
> Private pt1 As POINTAPI
> Private picTop As Long, picBottom As Long
>
> Private Sub Form_Load()
> Me.Move 0, 0
> Me.Show: Me.Refresh
> With picGraph
> picGraph.Scale (0, 0.089)-(530, 0.047376)
> picGraph.DrawMode = vbCopyPen
> picGraph.PaintPicture LoadPicture("c:\temp\jan1.jpg"), _
>    .ScaleLeft, .ScaleTop, _
>    .ScaleWidth, .ScaleHeight
>  .DrawMode = vbInvert
>  ' store the top and bottom pixel coordinates, which
>  ' will remain the same unless we resize the PicBox
>  picTop = .ScaleY(0, .ScaleMode, vbPixels)
>  picBottom = .ScaleY(.ScaleHeight, .ScaleMode, vbPixels)
> End With
> End Sub
>
> Private Sub picGraph_MouseDown(Button As Integer, _
>      Shift As Integer, x As Single, y As Single)
> If (Button And 1) = 1 Then
>  With picGraph
>    ' set oldX to initial position of line
>    oldX = .ScaleX(x - .ScaleLeft, .ScaleMode, vbPixels)
>    ' draw the initial line
>    MoveToEx .hdc, oldX, picTop, pt1
>    LineTo .hdc, oldX, picBottom
>  End With
> End If
> End Sub
>
> Private Sub picGraph_MouseMove(Button As Integer, _
>      Shift As Integer, x As Single, y As Single)
> If (Button And 1) = 1 Then
>  With picGraph
>  ' redraw the line at its previous position
>  ' in order to erase it (vbInvert drawing)
>  MoveToEx .hdc, oldX, picTop, pt1
>  LineTo .hdc, oldX, picBottom
>  ' set oldX to the pixel value of the new position
>  oldX = .ScaleX(x - .ScaleLeft, .ScaleMode, vbPixels)
>  ' draw the line at its new position
>  MoveToEx .hdc, oldX, picTop, pt1
>  LineTo .hdc, oldX, picBottom
>  End With
> End If
> End Sub
>
>
>
Author
2 Jun 2009 11:13 AM
Michael Williams
"David" <dw85745***@earthlink.net> wrote in message
news:ezdj5jw4JHA.1380@TK2MSFTNGP05.phx.gbl...

> Thanks Mike. I do have one more question if you
> don't mind. In MSChart you can click on the plotted
> line and it will be identified by squares. Only thing I
> can think of how this may be done is:
> 1)  Enum all plotted data sets, or
> 2)  Have a table of plotted dataset colors and use
> API: GetPixel to find which dataset then reloop the
> dataset at a larger step increment to ID the line.
> This will work unless there are multiple lines of the
> same color. Any ideas how this is done?

Whatever you do you need to maintain the actual data that each plotted line
or other drawn chart element represents because the lines themselves are
only an approximation of the data due to the "whole pixel" limitation of
anything you draw on the screen and you cannot therefore accurately
extrapolate the actual data merely by examining the drawn line. But I'm sure
you are doing that anyway and that your current problem is actually
identifying which data set the user intended to select by examining the
point that he clicked on the displayed chart. There are all sorts of
different ways of doing this depending on the type of graph or chart you are
drawing. In some cases you can create enclosed regions and use the
PtInRegion API to determine whether the user has clicked in a particular
region and in other cases doing it that way causes more problems than it
solves. There are of course all sorts of other ways.

To be perfectly honest I don't often get involved in drawing or maintaining
graphs of data and so I'm not really the best person to ask. There are
others here who know much more about it than I do and who will probably
respond soon. There is one simple method that I would suggest which can be
easily applied to all sorts of drawn graphs and charts and that is to
maintain a second copy of the chart in memory and to draw your graph or
chart both into the copy that gets displayed to the screen and into the
second copy in memory. I don't mean a "second copy" as in a backbuffer or
Autoredraw buffer of the displayed copy, I mean a completely separate copy
in memory which never gets displayed.

Any background stuff (background grids or pictures or whatever) and any
foreground grids (if there are any) that might overlay the actual chart data
are drawn only into the "display" copy of the chart. The actual chart data
itself (your drawn lines or whatever) is drawn into both the "display" (in
whatever colour you wish to display) copy and into the "second copy in
memory" (in a colour that is unique for each element). In that way the
displayed chart shows the background and grids and the chart data and any
foreground grids etc whereas the hidden "second copy in memory" contains
/only/ the actual drawn chart data. You should use a DIBSection instead of a
Screen Compatible bitmap for the "second copy in memory" because a
DIBSection can contain the full range of 24 bit colour values (16 million
RGB values) regardless of the colour depth of the display on which your code
is running, whereas a Screen Compatible bitmap (such as the Screen or a VB
Form or a VB PictureBox) is limited in its range of available colours by the
colour depth of the machine on which it is running.

For each element of your drawing (for each drawn data line or whatever) you
select a unique colour value (a Long in the range 1 to 16777216, reserving
zero (black) for the background) and you associate that specific colour
value with that specific data element in your main data. You then drawn your
data line (or whatever) into the hidden "second copy in memory" using the
selected unique colour. You also draw the same data line (or whatever) into
the display copy of the chart using whatever colour you wish (whatever
colour suits your purposes and looks nice on the display). In that way you
can draw the individual data lines or whatever in the displayed chart using
any colour you wish. You can even draw them all in the same colour if you
wish (although that of course is an unlikely requirement) and your code will
in all cases still be able to tell them apart because each data element is
drawn in a unique colour in the hidden DIBSection.

Then when the user clicks the chart on the display you determine the pixel
x,y coordinates of the point he has clicked in the way you are currently
doing but instead of examining the displayed chart image at that point you
instead examine the colour at that point in the hidden "second copy" in
memory (the DIBSection). Since the DBSection contains /only/ the drawn data
elements and /none/ of the background image or grids or anything else, and
since each individual data element is associated with its own unique colour,
you can determine the data element that the user intended to select simply
by the colour of the clicked point in the DIBSection. This method also
automatically takes into account the "ZOrder" of the drawn elements, just as
is the case in the MS Chart control. This method is useful in all sorts of
circumstances, although as I have said I don't usually get involved in
drawing graphs of data and there are probably half a dozen or more different
ways of doing it that I don't know about and I'm sure that others here will
post their own suggested methods if you would prefer not to use the method I
have suggested.

Mike
Author
2 Jun 2009 11:54 PM
David
Again -- Thank you for your time and effort on my behalf.

Interesting idea using a hidden DC with lines of different colors but IMHO
seems a long way around the block to ID a line as well as memory
intensive -- that's my first impression -- and I don't have a better
solution at this time.

David

Show quoteHide quote
"Michael Williams" <M***@WhiskeyAndCoke.com> wrote in message
news:uG7EjM34JHA.1092@TK2MSFTNGP06.phx.gbl...
> "David" <dw85745***@earthlink.net> wrote in message
> news:ezdj5jw4JHA.1380@TK2MSFTNGP05.phx.gbl...
>
>> Thanks Mike. I do have one more question if you
>> don't mind. In MSChart you can click on the plotted
>> line and it will be identified by squares. Only thing I
>> can think of how this may be done is:
>> 1)  Enum all plotted data sets, or
>> 2)  Have a table of plotted dataset colors and use
>> API: GetPixel to find which dataset then reloop the
>> dataset at a larger step increment to ID the line.
>> This will work unless there are multiple lines of the
>> same color. Any ideas how this is done?
>
> Whatever you do you need to maintain the actual data that each plotted
> line or other drawn chart element represents because the lines themselves
> are only an approximation of the data due to the "whole pixel" limitation
> of anything you draw on the screen and you cannot therefore accurately
> extrapolate the actual data merely by examining the drawn line. But I'm
> sure you are doing that anyway and that your current problem is actually
> identifying which data set the user intended to select by examining the
> point that he clicked on the displayed chart. There are all sorts of
> different ways of doing this depending on the type of graph or chart you
> are drawing. In some cases you can create enclosed regions and use the
> PtInRegion API to determine whether the user has clicked in a particular
> region and in other cases doing it that way causes more problems than it
> solves. There are of course all sorts of other ways.
>
> To be perfectly honest I don't often get involved in drawing or
> maintaining graphs of data and so I'm not really the best person to ask.
> There are others here who know much more about it than I do and who will
> probably respond soon. There is one simple method that I would suggest
> which can be easily applied to all sorts of drawn graphs and charts and
> that is to maintain a second copy of the chart in memory and to draw your
> graph or chart both into the copy that gets displayed to the screen and
> into the second copy in memory. I don't mean a "second copy" as in a
> backbuffer or Autoredraw buffer of the displayed copy, I mean a completely
> separate copy in memory which never gets displayed.
>
> Any background stuff (background grids or pictures or whatever) and any
> foreground grids (if there are any) that might overlay the actual chart
> data are drawn only into the "display" copy of the chart. The actual chart
> data itself (your drawn lines or whatever) is drawn into both the
> "display" (in whatever colour you wish to display) copy and into the
> "second copy in memory" (in a colour that is unique for each element). In
> that way the displayed chart shows the background and grids and the chart
> data and any foreground grids etc whereas the hidden "second copy in
> memory" contains /only/ the actual drawn chart data. You should use a
> DIBSection instead of a Screen Compatible bitmap for the "second copy in
> memory" because a DIBSection can contain the full range of 24 bit colour
> values (16 million RGB values) regardless of the colour depth of the
> display on which your code is running, whereas a Screen Compatible bitmap
> (such as the Screen or a VB Form or a VB PictureBox) is limited in its
> range of available colours by the colour depth of the machine on which it
> is running.
>
> For each element of your drawing (for each drawn data line or whatever)
> you select a unique colour value (a Long in the range 1 to 16777216,
> reserving zero (black) for the background) and you associate that specific
> colour value with that specific data element in your main data. You then
> drawn your data line (or whatever) into the hidden "second copy in memory"
> using the selected unique colour. You also draw the same data line (or
> whatever) into the display copy of the chart using whatever colour you
> wish (whatever colour suits your purposes and looks nice on the display).
> In that way you can draw the individual data lines or whatever in the
> displayed chart using any colour you wish. You can even draw them all in
> the same colour if you wish (although that of course is an unlikely
> requirement) and your code will in all cases still be able to tell them
> apart because each data element is drawn in a unique colour in the hidden
> DIBSection.
>
> Then when the user clicks the chart on the display you determine the pixel
> x,y coordinates of the point he has clicked in the way you are currently
> doing but instead of examining the displayed chart image at that point you
> instead examine the colour at that point in the hidden "second copy" in
> memory (the DIBSection). Since the DBSection contains /only/ the drawn
> data elements and /none/ of the background image or grids or anything
> else, and since each individual data element is associated with its own
> unique colour, you can determine the data element that the user intended
> to select simply by the colour of the clicked point in the DIBSection.
> This method also automatically takes into account the "ZOrder" of the
> drawn elements, just as is the case in the MS Chart control. This method
> is useful in all sorts of circumstances, although as I have said I don't
> usually get involved in drawing graphs of data and there are probably half
> a dozen or more different ways of doing it that I don't know about and I'm
> sure that others here will post their own suggested methods if you would
> prefer not to use the method I have suggested.
>
> Mike
>
>
>
>
Author
3 Jun 2009 8:57 AM
Michael Williams
"David" <dw85745***@earthlink.net> wrote in message
news:OK7zw194JHA.4272@TK2MSFTNGP04.phx.gbl...

> Again -- Thank you for your time and effort on my behalf.
> Interesting idea using a hidden DC with lines of different
> colors but IMHO seems a long way around the block to
> ID a line as well as memory intensive -- that's my first
> impression -- and I don't have a better solution at this time.

It certainly is overkill just to ID some lines, but I have no real idea of
what sort of charts or graphs you are drawing and so I offered it as a
solution because of its flexibility.

Mike
Author
5 Jun 2009 8:26 AM
James Hahn
Show quote Hide quote
"David" <dw85745***@earthlink.net> wrote in message
news:ezdj5jw4JHA.1380@TK2MSFTNGP05.phx.gbl...
> snip I do have one more question if you don't mind..
>
> In MSChart you can click on the plotted line and it will be identified by
> squares.
>
> Only thing I can think of how this may be done is:
>
> 1)  Enum all plotted data sets, or
> 2)  Have a table of plotted dataset colors and use API: GetPixel to find
> which dataset then reloop the dataset at a larger step increment to ID the
> line.  This will work unless there are multiple lines of the same color.
>
> Any ideas how this is done?

There's a good description of how to create a line selecting routine here:
http://msdn.microsoft.com/en-us/library/ms969920.aspx
Author
6 Jun 2009 4:03 PM
David
Thanks Mr. Hahn will review.

David

Show quoteHide quote
"James Hahn" <jh***@yahoo.com> wrote in message
news:OyJuOdb5JHA.4404@TK2MSFTNGP04.phx.gbl...
> "David" <dw85745***@earthlink.net> wrote in message
> news:ezdj5jw4JHA.1380@TK2MSFTNGP05.phx.gbl...
>> snip I do have one more question if you don't mind..
>>
>> In MSChart you can click on the plotted line and it will be identified by
>> squares.
>>
>> Only thing I can think of how this may be done is:
>>
>> 1)  Enum all plotted data sets, or
>> 2)  Have a table of plotted dataset colors and use API: GetPixel to find
>> which dataset then reloop the dataset at a larger step increment to ID
>> the line.  This will work unless there are multiple lines of the same
>> color.
>>
>> Any ideas how this is done?
>
> There's a good description of how to create a line selecting routine here:
> http://msdn.microsoft.com/en-us/library/ms969920.aspx