# Visual Basic > Visual Basic 6 and Earlier >  Need Suggestions on Smooth Scrolling Method

## webbiz

I have an old VB6 charting program that I'd like to juice up a little.  I've been tweaking it for 20 years.

Anyway, as a stock trader (not a professional programmer) I have several charting applications I subscribe to or get from my brokers.

Of all my charting apps, my TradingView charting program has a very nice way of moving a price chart around.

You simply left-click on it and move it in any direction and all the drawn price bars smoothly move around until you let go of the mouse button.

Now, in my VB6 charting program, I have a DrawChart routine that draws/redraws the price bars and anything that was drawn on it.

When I use my arrow keys to scroll the chart, it fires off the DrawChart and any redraw methods for drawn items on the chart every single click of the keyboard.

It's just clunky that way.

How is it possible to move the price bars and drawn items around with a left-mouse hold and move so smoothly as TradingView does?

What's the secret?

Here is a video that demonstrates what I'm talking about.

https://youtu.be/VceKN1likys

I know that we have a MOUSE_DOWN that would set some flag, so when you move the mouse the MOUSE_MOVE event would see the flag is set and then do the chart moving/redraw routines.  But how are these draw/redraws happening so cleanly and smoothly?  

TIA

----------


## Eduardo-

First thing to try is AutoRedraw = True

----------


## Elroy

And second is to try and make your redraw stuff as fast as possible.

----------


## Elroy

Be sure to compile to "Binary" and not "p-code".  And also, try compiling with all the "Advanced Optimizations" checked.  Those "Advanced Optimizations" make a very significant difference.

----------


## VanGoghGaming

If you redraw everything from scratch every time you move the mouse you will never get smooth scrolling. In the video you linked above that program is not calculating the bars over and over again. It's simply using BitBlt to shift around the relevant portions of the graph which is already on screen.

----------


## Elroy

> If you redraw everything from scratch every time you move the mouse you will never get smooth scrolling. In the video you linked above that program is not calculating the bars over and over again. It's simply using BitBlt to shift around the relevant portions of the graph which is already on screen.


VanGogh, you'd be surprised as to how much "speed up" you can get by using the optimizations.  I have a "picture smoothing colorizing" algorithm that works dynamically (real-time with sliders).  It was unacceptably slow until I peeled it out and put it into its own DLL which was compiled with all the optimizations.  It was quite acceptable after doing that.



You don't have to use a DLL, but I didn't want to use all of those for my entire project, so I isolated the "slow" code to a DLL.

----------


## VanGoghGaming

That warning at the top of your screenshot should be in big bold red letters. Good thing you didn't enable all of those for the entire project because it would have produced spectacularly random crashes. That being said, the "Remove Array Bounds Checks" has the biggest impact of them all and it's pretty safe to use. The rest of them can produce wildly unexpected results for minimal benefits (except the last one which stands as a testimonial of Intel's monumental screwup from the last millennium).

----------


## ColinE66

I've done this kind of thing quite a lot, and I'm with VanGogh: 'Salvage' what you can from the previous update every time you perform a new one. No amount of Advanced Optimisations will compete with the gains you'll get from that.

For example, you have an image 1000 pixels wide, and the user scrolls 20 pixels. No way should you draw 1000 pixels all over again. Salvage the 980 you already have and just add the new 20.

----------


## Elroy

Okey dokey.   :Smilie:   Just tellin' ya my empirical experience.

Also, that Pentium bug was fixed decades ago.  There's no reason whatsoever that we shouldn't always check that one.

----------


## VanGoghGaming

Yes mate, I know exactly what you're talking about. I have also played with those optimizations and after a lot of grief I have settled only for these in all my projects:



The division check doesn't do much unless you like dividing numbers by the thousands in tight loops but nowadays it's always safe to have it enabled since those bugged CPUs can only be found in museums. The "Array Bounds Checks" can be enabled only after you've triple checked your array loops and made sure they never produce an "index out of bounds" error. Even then it can come back to bite you when you pass arrays incorrectly to API functions or use "CopyMemory" recklessly. However the risk is well worth it for an almost 40% speed increase (tested with "QueryPerformanceCounter").

----------


## webbiz

> I've done this kind of thing quite a lot, and I'm with VanGogh: 'Salvage' what you can from the previous update every time you perform a new one. No amount of Advanced Optimisations will compete with the gains you'll get from that.
> 
> For example, you have an image 1000 pixels wide, and the user scrolls 20 pixels. No way should you draw 1000 pixels all over again. Salvage the 980 you already have and just add the new 20.



After reading all the responses (thank you everyone who responded!), I agree with you and VanGogh for my particular needs. I'm sure optimizing might give me gains, but it won't fix my lousy programming short-comings.

What I'd like to do is SALVAGE by doing a rework of my routines.  But I really don't know where to start.  If you know where there is an example of how one would draw price bars and others things (like lines, text) and then move it around nice and smooth, I'd appreciate it.  If I can master this, it would really help me in sprucing up this old poorly written dinosaur of an app.

:-)

----------


## georgekar

I made a special list box, before some years, which used as the base for other controls, and one of them was a text editor which colorize code (for my M2000 Interpreter) and handle millions of lines. So when you scroll the listbox (you didn't see it as listbox but as text editor), there is a timer which get the command to "redraw" some millisecond later, so I can scroll on thousand of lines very fast, because the control actual skip a large amount of redraw (also the colorize routine skip a large amount of lines too). 
So this is the secret, do not redraw immediately, but trough a timer. Each time you need redraw just disable, and enable the timer, and each time the timer actual redraw, the redraw routine must disable the timer. Having a good "delay" you get the data without notice the difference.  Always set AutoRedraw on, so when a Refresh method apply the operating system take the back buffer and display it (so you didn't display before the image draw be completed).

----------


## The trick

Use ScrollDC, ScrollWindow and redraw only the changed region. For example https://github.com/thetrik/VbVst/blo...anoRollExt.ctl

----------


## webbiz

> Use ScrollDC, ScrollWindow and redraw only the changed region. For example https://github.com/thetrik/VbVst/blo...anoRollExt.ctl


How does one test this code in VB6?  I noticed it listed as a 'control'.  Not sure what to do with it.

Sorry for the noob response, but my vb6 experience has been a work in progress for decades as I have only used it to tackle 'tasks' and then move on to other things.  

TIA

----------


## The trick

> How does one test this code in VB6?  I noticed it listed as a 'control'.  Not sure what to do with it.
> 
> Sorry for the noob response, but my vb6 experience has been a work in progress for decades as I have only used it to tackle 'tasks' and then move on to other things.  
> 
> TIA


Add a standard module with this declarations:


```
Public Type tKey
    lValue          As Long
    dVelocity       As Double
    dPos            As Double
    dLength         As Double
End Type

Public Type tPattern
    lLengthPerBeats As Long
    lNumOfKeys      As Long
    tKeys()         As tKey
End Type
```

Add this control. Add the type library (vbvst2x/TypeLib/vbvst2x.tlb).

Place ctlPianoRollExt to a form.

----------


## wqweto

What you need as an off-screen Canvas class. This is something called Surface in cairo or MemDC in standard GDI.

Usually this Canvas has a large 2D array to hold all the pixels of its output bitmap and has a lot of utility methods to draw primitives in various fore/back colors incl. drawing another Canvas at certain coordinates. These primitives (rects, circles, texts, gradients, bezier shapes) are usually impl. by forwarding calls to GDI/GDI+. 

Next you have to borrow techniques from gamedev. Notice how the chart diagram is constructed from several layers z-order along the depth of view. You have to precompute the candle-bars into a caching Canvas then composite final chart view from such precomputed Canvases and more text-only layers because you have a sub 35 ms time-slot to achieve 30FPS animation and even less if targeting at syncing with modern monitors vertical refresh at 60-300Hz

Notice that you have to forget about using built-in Image controls or PictureBoxes for anything but output placeholders. You cannot animate anything with Shapes and/or Image controls.

----------


## georgekar

Using picturebox over picturebox he can do animation. I think Windows use hardware acceleration for those controls. I did that in M2000 Interpreter, to use it as hardware sprites, with rotation, scale and transparent (100%) pixels, using a fRegionFromBitmap function (is very fast).

I use some other functions like this:
Public Function RotateRegion(hRgn As Long, Angle As Single, ByVal piw As Long, ByVal pih As Long, ByVal size As Single) As Long

Look here: https://github.com/M2000Interpreter/...b/main/pic.bas


The moving parts here are pictureboxes over picturebox. See the rotating square.

Is an old version!

----------


## webbiz

> What you need as an off-screen Canvas class. This is something called Surface in cairo or MemDC in standard GDI.
> 
> Usually this Canvas has a large 2D array to hold all the pixels of its output bitmap and has a lot of utility methods to draw primitives in various fore/back colors incl. drawing another Canvas at certain coordinates. These primitives (rects, circles, texts, gradients, bezier shapes) are usually impl. by forwarding calls to GDI/GDI+. 
> 
> Next you have to borrow techniques from gamedev. Notice how the chart diagram is constructed from several layers z-order along the depth of view. You have to precompute the candle-bars into a caching Canvas then composite final chart view from such precomputed Canvases and more text-only layers because you have a sub 35 ms time-slot to achieve 30FPS animation and even less if targeting at syncing with modern monitors vertical refresh at 60-300Hz
> 
> Notice that you have to forget about using built-in Image controls or PictureBoxes for anything but output placeholders. You cannot animate anything with Shapes and/or Image controls.


A bit of this is above my mental pay grade.  But I did understand about not using shape and image controls.  And I guess you are saying that Pictureboxes are to be used as simple containers, not using its built-in drawing routines.

Thanks.

----------


## wqweto

Hes been doing the PictureBox over PictureBox aproach and it just doesnt cut it anymore. He wants to upgrade his game from Pong to Mortal Kombat now and the fatalities are coming out a bit too chunky using Shapes, Images and PictureBoxes.

I know that M2000 can interpret this task spectacularly but probably OP needs some less sofisticated gamedev techniques as a beginning.

----------


## webbiz

> Add a standard module with this declarations:
> 
> 
> ```
> Public Type tKey
>     lValue          As Long
>     dVelocity       As Double
>     dPos            As Double
>     dLength         As Double
> ...



Please, what, where or how? ---> (Add this control. Add the type library (vbvst2x/TypeLib/vbvst2x.tlb)

I'm still not sure what to do with the code ctlPianoRollExt.ctl.

There is text that look like settings.  Where does this go?



```
VERSION 5.00
Begin VB.UserControl ctlPianoRollExt 
   AutoRedraw      =   -1  'True
   BackColor       =   &H00404040&
   ClientHeight    =   4785
   ClientLeft      =   0
   ClientTop       =   0
   ClientWidth     =   5985
   ClipControls    =   0   'False
   FillColor       =   &H00404040&
   BeginProperty Font 
      Name            =   "Arial"
      Size            =   8.25
      Charset         =   204
      Weight          =   400
      Underline       =   0   'False
      Italic          =   0   'False
      Strikethrough   =   0   'False
   EndProperty
   ForeColor       =   &H00E0E0E0&
   ScaleHeight     =   319
   ScaleMode       =   3  'Pixel
   ScaleWidth      =   399
End
Attribute VB_Name = "ctlPianoRollExt"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
```

And do I place the rest of the code that starts with 



```
' //
' // ctlPianoRollExt.ctl - polyphonic piano-roll control
' // by The trick, 2022
' //
```

...in a form?  That creates the control?

If I had an emoji for a contorted twisted facial expression I'd post that here.

TIA

----------


## webbiz

If no one has an answer to my question above, perhaps some pseudo-code to help me understand the logic/steps I need to research in order to achieve the scrolling effect?

Big problem with VB6 is that it is so old that no one is making training videos on YouTube like the other languages.  It is really hard to learn some needed concepts from my books as they don't touch on the subjects/problems I come across, like creating a really robust charting app.

I'm a professional Stock/Options analyst/trader and that's how I make my living. Programming is something I learned a long time ago from books but there is a huge learning curve for advanced subjects.  So I get it, I'm not part of the programming club per se.

All help is appreciated (along with patience for a long-time noob).

Thanks.

----------


## Elroy

> If no one has an answer to my question above, perhaps some pseudo-code to help me understand the logic/steps I need to research in order to achieve the scrolling effect?
> 
> Big problem with VB6 is that it is so old that no one is making training videos on YouTube like the other languages.  It is really hard to learn some needed concepts from my books as they don't touch on the subjects/problems I come across, like creating a really robust charting app.
> 
> I'm a professional Stock/Options analyst/trader and that's how I make my living. Programming is something I learned a long time ago from books but there is a huge learning curve for advanced subjects.  So I get it, I'm not part of the programming club per se.
> 
> All help is appreciated (along with patience for a long-time noob).
> 
> Thanks.


Webbiz, if you outline in detail specifically what you're trying to do (possibly with examples of code you've tried), you'll almost certainly get answers here.

Your original question is so general (with no code) that I don't believe anyone understands exactly what you're doing.  Also, even though you've provided a video, we're (at least me) not sure of precisely what you're trying to do.

Also, I don't see where you've actually tried the optimizations options for the VB6 compiler.  You could easily just turn them all on and re-compile, and then report back what kind of results you got.  That would take 10 minutes.

Now, if you're determined to rewrite your code, then you need to give a specific outline of precisely what you're trying to do.

----------


## georgekar

He has to read all above solutions and find what suits for him. I think to rethink about using the timer as I suggest, to narrow the demand for output. So the control of timer has to be done in the "Redraw" function of his control or whatever it is. The redraw function split in two parts, the front part just reset the timer and enable it again, so if any demand for redraw happen this would suspend for some milliseconds, so in reality the last one who pass the '"top" value for the frame to draw (the top is the scroll value, say 0 is the top most position for the frame), alter the values need from the second part of redraw (the one which actually redraw the frame to be scrolled in a new position). Doing that with a "skip redraw" fasion, the user never notice the difference, and the scrolling happen instantly, because less resources demands means speed (because the last one actually do whatever with databases). So at the end of the second redraw part a Refresh statement show to the user the final- and only one- redraw. 
I do this for program which read database and for each row display image which retrieve from database. So you can move the scroll bar as you like and the images between not retrieved from database because the "second redraw" never happen as the scroll bar moving. The user always see the moving scroll bar, and when leave it to desire position he gets the redraw (after some milliseconds, a very tiny delay which he never understood).

So I suggest to place a timer and split redraw to two parts, the first one has to do two things only, lets say we have Timer1. First Timer1.enable=false and then timer1.enable= true. The first just reset the timer, and the second start the delay from zero. At the second part there is two options. If he use a call to a function passing by value the "top scroll value" nothing special, but if he use a wide scope top scroll variable, he has to use a buffer, a second variable where the first part leave the argument for the redraw, and the redraw when happen has to read that and clear it, and enable a guard, a variable named once which stop a second call when the first is at redraw stage. Because some time we use a doevents somewhere in the event service function and that give the window for passing a second call, like a reentrance to where we don't want that.  So doevents has to not used in the event service function (is bad habbit). Actually the once variable (a boolean type) may be used to skip the timer1.enabled=true, but can store to the "buffer" variable the next value for redraw. So the second part the actual redraw, when finish, check this "buffer" variable if a new demand for redraw waiting and just take it and clear the demand putting a -1 or something a "null" demand.

That works for anything we want a redraw, scroll bar or keyboard arrows or something which an event want to show. For slow machines is a must, because not only has less redraw (guarantee to have one for all minimum delay - the timer1 setting), but also for the use of slow disk/internet  or even memory access.

Think about the mouse message from windows. You move the mouse pointer all over in your form. So how many messages need to get from the system and when. The windows use a queue of messages which are time stamped. So we never get a newer if an old aren't serviced. So if we draw something we get all the discrete mouse positions (as the system generate events), and make them a line draw or a curve one. But think something, if we move mouse very fast the discrete positions are not in equal spaces! So we need to adjust the message queue in a second stage, generating equal spacing for graphic cursor, we need another queue. And a second problem. We have to refresh the drawing canvas in equal time spaces. So we have a moving of mouse pointer in any acceleration/speed, and we have to draw equal space dots and refresh the canvas in equal time spaces.

The OP has a similar problem, he wants at random changes of a "top of form/frame" or "scroll value", random at time, random at discrete values, to get a SMOOTH redraw, showing info at equal time space (or that the user think about). That can be happen using the method of  "skip redraw because a new demand happen in less than..."  we say 30ms, if this is the timer's delay of our system. So here is more easy from the double queue for drawing, because if the user scroll for a long time we can make some redraws between (handling the second part to start a redraw using a counter of the number of demands, but without using a queue of size larger than one, so we always accept the last loading to queue demand). This is the difference with pixel drawing, where not only do not skip the user input but we embed pixels to do equal spacing draw (or if is less a minimum we have to wait for something better).

----------


## Elroy

> I think to rethink about using the timer as I suggest, to narrow the demand for output.


That's not a bad idea at all.  Or, a similar approach would be to get the QueryPerformanceCounter going, and don't refresh unless a certain amount of time has lapsed.  It'll still be somewhat laggy, but it may be better than it is.  You'd also have to monitor Form_MouseUp, and do a final refresh (regardless of elapsed time) when the user has stopped resizing.

Here's just an idea on how to do the resize (no testing done):



```

Option Explicit
'
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
Private cFreq As Currency
'


Private Sub Form_Load()
    QueryPerformanceFrequency cFreq
End Sub

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    DoFormRedraw

    ' May need to check Shift if different mouse buttons do different things.

End Sub

Private Sub Form_Resize()
    Const dInterval As Double = 0.1     ' Seconds.  Tweak on this until it looks good.
    Static dLastResize As Double
    Dim dNow As Double
    Dim cValue As Currency
    QueryPerformanceCounter cValue
    dNow = cValue / cFreq               ' Seconds.
    If (dNow - dLastResize) > dInterval Then
        dLastResize = dNow
        DoFormRedraw
    End If
End Sub

Private Sub DoFormRedraw()

    ' Put your stuff in here to redraw the form.

End Sub


```


ADDED:  Actually, I guess it's not the Form_Resize that you're using, but it should still be the same idea.

----------


## webbiz

> Webbiz, if you outline in detail specifically what you're trying to do (possibly with examples of code you've tried), you'll almost certainly get answers here.
> 
> Your original question is so general (with no code) that I don't believe anyone understands exactly what you're doing.  Also, even though you've provided a video, we're (at least me) not sure of precisely what you're trying to do.
> 
> Also, I don't see where you've actually tried the optimizations options for the VB6 compiler.  You could easily just turn them all on and re-compile, and then report back what kind of results you got.  That would take 10 minutes.
> 
> Now, if you're determined to rewrite your code, then you need to give a specific outline of precisely what you're trying to do.


 The problem I have is NOT about optimization.  My charting code is clunky and novice, using PictureBox drawing routines and other methods that if you call them for every single keypress (or mouse_move) it isn't going to be a smooth scroll.

The video shows exactly what I'd like to do.  I don't know how else to demonstrate drawing a chart, left-mouse and drag to move the chart while the bars on the chart are smoothly scrolling in any direction you move the mouse.

And since I don't know where to start, I don't have any code to show progress because I haven't started tearing up my clunking code that has to be deleted.  It cannot be used, it must be replaced.

So I'm asking anyone who has experience in this kind of VB6 graphics to tell me the STEPS.  No need to write the code for me, but a set of pseudo-code steps so I can research how each step is accomplished in code.

It is my guess that I'm going to have to use API code to achieve this.  I have not been able to find anything that matches what I demonstrated in the video.

Thanks.

----------


## webbiz

Here is the Pseudo-code of what my application does right now in its barbaric state.


Call Load_Data routine to load in price data.

Call Draw_Chart routine to take price data and plot it the pctChart PictureBox control using the Line() method.

Call the RedrawTools routine to draw the lines, text and any other 'saved' drawing for the chart.


Here is a small clip of how this is being done to draw chart:



```
  For i = gFirstBarRecNum To gLastBarRecNum
        If MarketData(i).fHigh <> 0 And MarketData(i).fLow <> 0 Then
            dblLowPoint = GetYFromPrice(MarketData(i).fLow)
            
            dblHighPoint = GetYFromPrice(MarketData(i).fHigh)
            dblOpenPoint = GetYFromPrice(MarketData(i).fOpen)
            dblClosePoint = GetYFromPrice(MarketData(i).fClose)
            
            If dblHighPoint < -300000 Or dblLowPoint > 300000 Then: Exit Sub
            
            'we now draw the Candlesticks using the info we just calculated for the bar
            'each bar is separated horizontally by gHSpace in screen coordinates
            'last two lines draw the Open and Close horizontal lines with different colors
            
            frmChart.pctChart.DrawWidth = 1
            
            
            'First draw the candlestick SHADOW
            If dblOpenPoint > dblClosePoint Then 'Up day
                frmChart.pctChart.Line ((i - (gFirstBarRecNum)) * gHSpace, dblLowPoint)-((i - (gFirstBarRecNum)) * gHSpace, dblOpenPoint), lBullBarColor 'The bar SHADOW
                frmChart.pctChart.Line ((i - (gFirstBarRecNum)) * gHSpace, dblHighPoint)-((i - (gFirstBarRecNum)) * gHSpace, dblClosePoint), lBullBarColor 'The bar SHADOW
            Else
                frmChart.pctChart.Line ((i - (gFirstBarRecNum)) * gHSpace, dblLowPoint)-((i - (gFirstBarRecNum)) * gHSpace, dblClosePoint), lBearBarColor 'The bar
                frmChart.pctChart.Line ((i - (gFirstBarRecNum)) * gHSpace, dblHighPoint)-((i - (gFirstBarRecNum)) * gHSpace, dblOpenPoint), lBearBarColor 'The bar
            End If
```



Now, if you press the left or right arrow key, it will do the following:

pctChart_KeyDown is called.

Here is what you get when it is the Right key:



```
            Case vbKeyRight
                
                 gbKeyScrolling = True  'scrolling flag
                
                If Shift = 0 Then
                    
                    ScrollPriceChart -1   'Send -1 to ScrollPriceChart routine.
                
                

                ElseIf Shift = 1 Then 'Shift Key or Home Key
                    gFirstBarRecNum = 1
                    gLastBarRecNum = iDispRecs
                ElseIf Shift = 2 Then 'Cntrl Key
                    If gLastBarRecNum > iDispRecs Then
                        gFirstBarRecNum = gFirstBarRecNum - iDispRecs
                    Else
                        gFirstBarRecNum = 1
                    End If
                    gLastBarRecNum = gFirstBarRecNum + iDispRecs
                End If
```


And here is ScrollPriceChart routine:



```
Public Sub ScrollPriceChart(ByVal BarsToShift As Long)
Dim iDispRecs As Integer

    iDispRecs = GetDispRecs  'number of bars to display
    
    gFirstBarRecNum = gFirstBarRecNum - BarsToShift  'update the record number for the first price bar on chart (displayed far left in picturebox)
    
    'Adjust gFirstBarRecNum if too far in either direction
    If gFirstBarRecNum < 1 Then
        gFirstBarRecNum = 1
    ElseIf gFirstBarRecNum > gLastRecNum Then
        gFirstBarRecNum = gFirstBarRecNum - 5
    End If
    
    
    
    'Adjust other variables based on the new gFirstBarRecNum
    If gFirstBarRecNum + iDispRecs > gLastRecNum Then
        gLastBarRecNum = gLastRecNum
    Else
        gLastBarRecNum = gFirstBarRecNum + iDispRecs
    End If


End Sub
```

After these routines are run, the DrawChart and RedrawTools are called to clear the picturebox and then draw the price bars and drawing tools based on the new gFirstBarRecNum and gLastBarRecNum values.  These global variables keep track of the first and last price bar record number to draw on the chart.


So in short, the 'scroll' methods I am currently using simply changes the values of the first bar record number (increase or decrease by 1), clear the chart, then redraw the chart and drawings.

Now imagine holding down the right arrow key to scroll left or right several bars at a time.

You get a whole lot of 'clear picturebox', 'draw on picturebox' over and over again.  THE WHOLE CHART!

Clunky!!!

At this current time, I can click the mouse and drag, changing the value of the first record number by values greater than 1 and just do a single full redraw.

But I would like to do what that other app does, where the user can actually SEE the price bars gliding across the screen without stutters or waiting for the routines to catch up.

I'm not sure how else to describe this.

Now I'm sure this can be done with CAIRO.

Unfortunately, using CAIRO I'm very dependent on a single person hopefully answering my questions which is hit or miss.  I'd rather use whatever is the used by the larger number of VB6 programmers, even if it requires more coding.

:-)

----------


## VanGoghGaming

Depending how big is your "PriceChart" you could draw it all at once in a huge memory bitmap (not visible on screen) and then display only the parts that fit in the "PictureBox". Scrolling through the chart with this method would be trivial to implement.

If this isn't feasible, you're left with recalculating and redrawing only the parts that are not already visible when scrolling. This is much more difficult but the "ScrollDC" API mentioned by "TheTrick" should be the way to go.

----------


## The trick



----------


## wqweto

No, you cannot do smooth animations using Lines, Shapes, Image, Label controls or PictureBoxes no matter how many Timers you shove on your form.

What you need is an off-screen deawing Canvas class, multi-layer compositing of final output and caching of intermediary drawing artefacts. You need your chart precomputed in a bitmap/Canvas instance the moment ticks arrive, not redrawing it on each repaint request. On repaint you just composite all the layers which are precomputed in sub 30ms time-slot to achieve smooth motion of any kind.

Ill try to cobble some sample code of this aproach if no one comes up with satisfactory results before me.

----------


## webbiz

> Depending how big is your "PriceChart" you could draw it all at once in a huge memory bitmap (not visible on screen) and then display only the parts that fit in the "PictureBox". Scrolling through the chart with this method would be trivial to implement.
> 
> If this isn't feasible, you're left with recalculating and redrawing only the parts that are not already visible when scrolling. This is much more difficult but the "ScrollDC" API mentioned by "TheTrick" should be the way to go.


I really like the idea of drawing the whole price chart from the start, then moving it around.  

Last night I was thinking about that, and even posted a question about drawing "off-screen" in trying to test to learn how this might work.

I'm at the stage of not knowing where to look/start towards this goal.

But if I can draw the whole chart (it averages about 20 years worth of daily price data) and then just move it around into view, that would be great.  I guess it will slow down the chart load, but once loaded, it should be quicker than displaying piece by piece.

So we're talking about drawing in a big memory bitmap?  I'll see what I can find in search on that.

Thanks.

----------


## webbiz

> 


I understand the idea you convey, but don't know how to do this.

Wherever the chart is moved to, the app must keep track of the location of all these bars because the user should be able to move mouse over a bar and get price details, etc.

Curious how one would manage keeping track of this.  In some type of 'table'?  Guess this the kind of thing taught in programmer college.  ;-)

Anyway, the example you showed looks super-advanced. Is there a website or ? that actually teaches this advanced manipulation of coordinates, etc.?

Thanks.

----------


## webbiz

> No, you cannot do smooth animations using Lines, Shapes, Image, Label controls or PictureBoxes no matter how many Timers you shove on your form.
> 
> What you need is an off-screen deawing Canvas class, multi-layer compositing of final output and caching of intermediary drawing artefacts. You need your chart precomputed in a bitmap/Canvas instance the moment ticks arrive, not redrawing it on each repaint request. On repaint you just composite all the layers which are precomputed in sub 30ms time-slot to achieve smooth motion of any kind.
> 
> Ill try to cobble some sample code of this aproach if no one comes up with satisfactory results before me.



Exactly!  That was what I was saying. My code is just a beginner's hack style from years ago.  My first ever charting app.

I appreciate your comments on this and the possible 'sample code'.

Where can I go to learn this advanced stuff?  Looks like I need to become proficient in doing this.

Where are the best VB6 resources (I have books, but searching them does not seem to get me to a solution).

Thanks.

----------


## Elroy

Ok, if I were doing this, and I was super concerned about dynamic (real-time) speed, I'd probably resort to thoroughly learning Direct2D.  The Trick has provided TypeLibs for making direct calls into DirectX version 9, and I believe Direct2D is included in that.

Personally, I don't have any experience with Direct2D, but I do have experience with Direct3D, and using it with dynamic mouse dragging and scroll-wheel usage.  Above, a back-buffer was mentioned, and you more-or-less automatically get that when using DirectX.  And in my Direct3D application, everything is moved around absolutely flawlessly with no jitters nor delay.  In my application, there's also a "play" option (to dynamically "play through time" various Direct3D objects).  You can simultaneously "play" and reposition the camera with no jittering.

Here's a link to an example of my Direct3D work.

So, that's a place to start.  And the math should be quite a bit easier in 2D (rather than 3D), as there's only one plane for any rotations (rather than three), and no concern over things like Euler angles and gimbal lock.

If you search these forums for Direct2D, I suspect you'll find quite a few examples.

ADDED:  Ohh, and I'm on the wrong computer to check, but I don't believe I even used any of the compiler's Advanced Optimizations to do that work.

----------


## passel

If you want simple, and you're already drawing in a picturebox, just place the picturebox inside a container (like a frame, or another picturebox).
Then when you drag on the picturebox, just move its location within the container.

Simple example....
Start a new project and place a Frame on the form and size it to whatever you want your viewing area to be.
Next place a Picturebox inside the Frame.
Run this code and drag on the colored area of the picturebox.


```
Private Sub Form_Load()
  Randomize
  Dim X As Single, Y As Single
  Dim i As Integer
  
  Frame1.BorderStyle = vbBSNone
  With Picture1
    .AutoRedraw = True
    .ScaleMode = vbPixels
    .BorderStyle = vbBSNone
    .Move 0, 0, Frame1.Width * 1.5, Frame1.Height * 1.5
  
  
    For i = 1 To 200
      X = Rnd() * 0.8 * .ScaleWidth
      Y = Rnd() * 0.8 * .ScaleHeight
      Picture1.Line (X, Y)-Step(0.1 * Rnd() * .ScaleWidth, 0.1 * Rnd() * .ScaleHeight), QBColor(Rnd * 15), BF
    Next
  End With
End Sub

Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
  Static lx As Single, ly As Single
  With Picture1
    If Button = vbLeftButton Then
      .Move .Left + (X - lx), .Top + (Y - ly)
    Else
      lx = X: ly = Y
    End If
  End With
End Sub
```

p.s. Since you're moving the picturebox, the coordinates within the picturebox will stay the same so your mouse positioning reading code to correlate with your data should stay the same.
If you use the mousemove position for updating the data then that would likely go in the "Else" block along with the updating of lx and ly.
If you use mouse dragging for some other function, then perhaps use the Shift argument to choose between your use of the drag and the dragging of the chart capability, i.e. the user has to press the Shift key, or Ctrl key while dragging to move the chart.

----------


## Schmidt

> On repaint you just composite all the layers which are precomputed in sub 30ms time-slot to achieve smooth motion of any kind.


I'd like to point out, that even with "brute-force-redrawing of *everything*" from the ground up...
(meaning, the whole Chart, including Axes, Axes-Ticks *and* "complex, renderings per data-record") -
such an "x-Axis -Time-shifted refresh" will fulfill that requirement of around 30-40msec... (to scroll "fluently").

Here's a ScreenShot (the DB-Query + Render-Time for the entire Chart is 31.9msec - as reported in the Form-Caption):

The rendering of the above is based on a 200-Records returning-DB-query -
(which looks dynamically "backwards" from a "right-aligned time" defined in the TopRight-ComboBox).

It is based on the following CodeBank-entry
(which can be used also with RC6 instead of vbRichClient5 - by just switching the Reference)
https://www.vbforums.com/showthread....for-x-y-Plots)




> Ill try to cobble some sample code of this aproach if no one comes up with satisfactory results before me.


Ohh please, please... could you (just this one time) demonstrate to the "unbelievers",
how an experienced developer (without pre-existing, "deep cairo-knowledge") -
will put the RC6-Cairo-Classes to good use (without asking me any questions here)?  :Wink: 

Here's another Stock-Charting-demo (non-DB-based) you could borrow things from:
https://www.vbforums.com/showthread....6-cChart-Class

Olaf

----------


## webbiz

> If you want simple, and you're already drawing in a picturebox, just place the picturebox inside a container (like a frame, or another picturebox).
> Then when you drag on the picturebox, just move its location within the container.
> 
> Simple example....
> Start a new project and place a Frame on the form and size it to whatever you want your viewing area to be.
> Next place a Picturebox inside the Frame.
> Run this code and drag on the colored area of the picturebox.
> 
> 
> ...


Oh my.  This sounds like a great idea!  I'll have a play with that.

Thanks!

----------


## webbiz

> Ohh please, please... could you (just this one time) demonstrate to the "unbelievers",
> how an experienced developer (without pre-existing, "deep cairo-knowledge") -
> will put the RC6-Cairo-Classes to good use (without asking me any questions here)? 
> 
> 
> Olaf


I think the joke went over my head. Sorry.  

Experienced or not experienced, I think anyone w/o pre-existing "deep cairo-knowledge" will still have to ask you questions.

It's inescapable!!!  ;-)


Thanks for the response and examples. I'll look and play with it.  However, I'm now afraid to move deeper down the Cairo hole because as an "inexperienced" developer, I'd be way too insecure of being left in the dark if the small source of "cairo help" does not respond to my cries for help.  

Cheers!

----------


## Schmidt

> I think the joke went over my head. Sorry.


That's because you don't know what the term "Class-wrapper" really means -
(and BTW, it wasn't really meant as a joke...)




> Experienced or not experienced, 
> I think anyone w/o pre-existing "deep cairo-knowledge" will still have to ask you questions.


Absolutely not, ... for the very same reason as above:
You really don't seem to know, what the term "Class-wrapper" means.

A Class-wrapper is an (object-oriented) way, to encapsulate a (usually) "flat-API",
behind a "relatively thin hull" for more convenience (e.g. not requiring any "flat-API-Handle-cleanups").

And no, the VB-Cairo-Class-wrapper (in its RC6-representation) is not "the only one out there".
There's other (objectoriented) cairo-wrappers, also for C++, C#, Python, Java, JavaScript, etc...

All in all, the amount of developers who have experience with (class-based) cairo-wrappers,
should exceed even the amount of "*yet* active VB-developers".
And in 5-10 years this situation will only get worse than it is now...

The likelihood to find someone at that time, who knows how to work with e.g. an "HTML5-canvas-object" -
(which is just another cairo-like behaving object-wrapper for 2D-graphics), is far higher ...

Olaf

----------


## georgekar

I still say that using just a picture box and drawing using AutoRedraw=True, we use a double buffer system, which we do all the job before anyone see anything and when we are happy we send a refresh to show the results. It is so fast the PC today, so a scrolling problem is not a PC problem, or Vb6 problem, or a Picture box problem, but it is a bad programming. No one begun from the start and  knowing everything. So even the data are a small amount or the picture box is small, we need the timer which delay the refresh and maybe skip some frames, not only the drawing but also back to the preparation of data, to skip something which the user don't want to see, he scrolling the window....so lets show something when he is ready to see, also you may have a bigger screen and for small moving you use that until a new line need data.

Also trick say something good, you don't have to draw all the canvas, but the area which you don't have data.

----------

