I'm making a program in vb6 that requires me to be able to scroll a certain area of the screen to the right. It consists solely of lines and picture boxes, is there a way to only scroll that area? Thanks!
You can take advantage from the fact that some visual controls can act as container of other visual controls.
Just an example:
In the VBIDE, place a Frame over a VB Form. Then - inside this Frame place a PictureBox. Pay attention that the PictureBox shall be fully contained inside this Frame.
Now, if you drag the Frame around the Form, you will see that the PictureBox inside is moving together, while keeping the position inside the container Frame, i.e. it will keep the original top & left coordinates relative to the container control.
To find out which visual controls have this capability, simply retry the test. You will see, for example, that a Label can't act as a container.
That said, you need following:
one visual control (preferably a PictureBox) which act as container (the viewport)
one visual control (preferably a PictureBox) which act as scrollable area
a HScrollBar (and, optionally, a VScrollBar) to scroll the
view-able area
Now, inside the second PictureBox (the scrollable area) you can place your controls, the Lines and PictureBoxes you mentioned in your question.
Why is a PictureBox preferable? Because you can profit from the ScaleMode property, set it to 3-Pixeland use pixel-exact scrolling. With Frames you can't do that, you are limited to just Twips.
By using a contained control you have two advantages:
you can visually place and reposition the controls ypu need inside the IDE
you need to scroll just only one control - all other hosted controls will
move together
The boring thing you must code is the synchronization of the container with the ScrollBars.
But luckily, as VB6 has been going a long way, you will find enough cut-and-paste code examples of such a task, one of which is on VBForums: Scroll bar in picturebox
Some final notes:
PictureBoxes in VB6 are constrained to a maximum size of 16,383 x 16,383 pixels. If your scrollable area should be bigger, you may implement a kind of custom "infinite scroller", and manage the position of your controls by grouping them, and you will need some additional coding.
ScrollBars in VB6 can range from a minimum value of -32,768 to a maximum value of 32,767. If you need more, you will end up with some other additional coding tasks.
If you stick to Twips, you can have a bigger logical area available - for example: until 245,745 with the typical 15 TwipsPerPixel - but you can't use such a big value with ScrollBars.
Here is a simple example illustrating what you requested. The key is that the scrollable area must be a container control hosting the controls you wish to scroll.
Option Explicit
Private oldPos As Integer
Private Sub Form_Load()
HScroll1.Min = 0
HScroll1.Max = 1000
HScroll1.SmallChange = Screen.TwipsPerPixelX * 10
HScroll1.LargeChange = HScroll1.SmallChange
End Sub
Private Sub HScroll1_Change()
ScrollPictureBox
End Sub
Private Sub HScroll1_Scroll()
ScrollPictureBox
End Sub
Private Sub ScrollPictureBox()
Dim c As Control
For Each c In Me.Controls
If c.Container.Name = "Picture1" And Not TypeOf c Is HScrollBar Then
c.Left = c.Left - (oldPos - HScroll1.Value)
End If
Next
oldPos = HScroll1.Value
End Sub
In this code, Picture1 is a PictureBox (the scrollable area) containing HScroll1 (a horizontal scrollbar) and the other controls you wish to scroll.
Related
I have a form (MS Access) which manages image attachments and a couple of reports which display those attachments based on various selection criteria. All of the image files (.jpg ) are kept to a maximum size of 1.4Mb. The form shows only one image at a time and as I move to each record, the new image flickers - usually displaying, clearing then displaying again. Occasionally the image displays just once but there seems to be no correlation between this behaviour and image size - the smaller attachments usually suffer the same effect and the larger attachments sometimes display once without flickering. There appears to be some phase issue between form display and image display but I am only guessing.
In the reports, there is no flicker on initial display and as long as I don't use the scroll bar up/down arrows or drag the slider, any refresh is acceptable (certainly not slick but I can live with that). Clicking on the scroll bar region either side of the slider, the refresh is acceptable (not slick). Using the slider or the arrows, things get unacceptably strobic.
I have searched online for "MS ACCESS", Attachment, Image, Flicker several times but all I can find are articles on flickering forms, which is not a problem in this application running on Windows 10 64-bit, plenty of memory, 2Gb graphics. I can't find anything relevant to image attachment flicker.
My main concern is to improve the usability of the form; for the reports, careful advice not to drag the slider or use the scroll bar arrows will be enough, at a pinch.
In the context of my current application, this isn't a huge problem since image attachments are not a critical requirement. All the same, I would like to know of any way to stop the flicker for anything in the future where it is more important.
Edit, following further investigation prompted by suggestions below.
I played around with putting .visible = false and .visible = true into various event handlers but with not much change, except to make the flickering worse with at least one combination. I'm not sure what code I could provide to help you suggest anything but the following might be of use:
The form's record source is a single table which provides one field "imagelink" of data type attachment, plus two text fields: "description" (short text) and "comments" (long text). In addition, the form has three combo boxes. Two of these have a row source of simple SQL on one parent table each, tblSite and tblSpecies, with the foreign key hidden in both cases, just tbleSite.sitename and tblSpecies.commonname visible in the combo boxes. The third combo box has a simple SQL row source on one table (tblColour) which is a child of tblSpecies, constrained by a WHERE clause to offer only those colour types applicable to the current species. Apart from a Delete button, that is it.
I put some MsgBox traces into several event handlers to see what was happening. I used MsgBox rather than the debugger as I wanted to see the events and the behaviour on screen at the same time (Access noob). This is what happened, with no .Visible = False/True code active:
On firing up the form from scratch:
No image visible
Form_Open
Form_Load
attImage_AttachmentCurrent
Form_Current
The image appears, no flicker
Detail_Paint (twice)
On subsequently going to the next record via the navigation buttons:
Detail_Paint
First image blanks
Detail_Paint
First image reappears
Detail_Paint
First image blanks
Detail_Paint
Next (second) image appears
Detail_Paint (three times)
Second image blanks
Detail_Paint
Second image reappears
attImage_AttachmentCurrent
Form_Current
Detail_Paint
Second image blanks
Detail_Paint
Second image reappears
Detail_Paint
Second image blanks
Detail_Paint
Second image reappears
The sequence above occurs every time I move from one record to another. There will no doubt be other events; I have traced only what I thought to be the most relevant.
Further, while staying on any one record but changing focus between the various text and combo boxes via mouse clicks, this happens:
Detail_Paint
Image blanks
Detail_Paint
Image reappears
There is also a Delete button on the form, which occasionally (I haven't yet tried to trace this) causes the image to flicker as the mouse passes over it - I suspect lots of Detail_Paint events. That doesn't always happen - I could do some more tracing if required but I feel I am missing some very simple, obvious point (Access noob, as already said).
The only other event code I have is:
After Update on site and species, to ensure that only one or the other is not null and to enable/disable the other combo boxes accordingly (an image can be of an insect or a site, not both, a colour can only be assigned to an already selected species). The After Update on species also resets the Colour combo box row source to restrict the colours to those relevant for the new species.
It feels to me that setting .visible = False on leaving a record, then .visible = True once the new record has settled down, might work. However, I can't see any suitable Detail event that could do this. On_Next_Record_Click and On_Current_Record_Finshed_Doing_Stuff would help but there are no such things as far as I can see.
If there is any of the existing code you think might be of use, I can attach details. I still have some hair left. I apologise for anything silly I have done but event processing was never my strength - give me procedures, please.
I had the same problem in my database where when I went from one record to another the photo attachment would flicker. I used the application.echo property and turned it off on the current event code, but was still having the problem.
I realized that it was running through an after update event as well, but when I turned the application.echo off on that code it eliminated the flicker.
Make sure to turn the application.echo back on at the end of each Subroutine.
To prevent the screen from flickering in moving on a form from one attachment to the next, you could combine the Attachment control’s KeyDown event with the Timer event for the form.
I achieved the desired result by turning screen repainting off on the Attachment control’s KeyDown event and by turning it on again when the Form's Timer occurs. The form's TimerInterval property specifies the interval between Timer events. In my case, one millisecond was sufficient to prevent flickering.
Private Sub Attachment_Artwork_KeyDown(KeyCode As Integer, Shift As Integer)
If 33 = KeyCode Or _
34 = KeyCode Or _
37 = KeyCode Or _
38 = KeyCode Or _
39 = KeyCode Or _
40 = KeyCode Then Application.Echo False
End Sub
Private Sub Form_Timer()
Application.Echo True
End Sub
This is the first step to scroll two windows simulteneously, the second step is to find a way to apply that amount to the other program. But I really don't know where to start, all I see in from Google is about modifying the behavior of the scroll of the mouse, not the scrolling amount in one window. Advantages of using this instead of listening to the keys:
Scrolling will be seamless since the other program are scrolled in the background
Clicking on the scrollbar works
Different scrolling speeds don't affect the comparison
Moving lines in text editors won't scroll pages in PDF viewers
I can tell you what I would do, but it's not going to be fun...
Assuming the text is roughly evenly distributed (which it might not be between two languages like that, and two windows with different text sizes and widths, really consider this carefully before you do the work), then the goal is to force both scroll bars to be at the same percent relative to their whole. So what you need to do is write a function to determine what percent each scroll bar is at. I would screen capture both windows and crop out the important parts of the scroll bar like this:
Specifically the up and down buttons, the top of the scroll handle, the bottom of the scroll handle. Save them in their own files. Do it for both windows in case they draw their scroll bars a little differently.
Now the tricky part. Write a function that does the following: imagesearch for the top button within rightmost 25 pixels or of the specified window. Same for the bottom button. Same for the top of the handle. Same for the bottom of the handle. Use this to determine where your window is.
SetTitleMatchMode, 2 ; so it matches the end of the title
WinGetPos , X, Y, Width, Height, LibreOffice Writer ;exact substring of window name required
Use something like this to find the scroll bar parts.
CoordMode, Pixel , Screen ;so image search searches entire screen
barwidth = 25 ; make sure it's more than the bar is wide.
ImageSearch, TopButtonX, TopButtonY, X+Width-barwidth , Y, X+Width, Y+Height, TopButton.bmp ; no jpg, fuzzy edges make searches fail
Then do some math, something like:
TopButtonY := TopButtonY+TopButtonImageHeight ; because we only care about the position of the bottom of the button.
BottomHandleY := BottomHandleY+BottomHandleImageHeight ; because we only care about where the bottom of the handle is.
HandleHeight := TopHandleY - BottomHandleY ; how tall the scroll handle is
TotalHeight := TopButtonY - BottomButtonY - HandleHeight ;how tall the scroll field is
HandleOffset := TopHandleY - TopButtonY ;how far it is from the top
HandlePercent := HandleOffset / TotalHeight ; the part we care about. return this value
With a function like that, you can know how scrolled each window is. All that's left is to send the scroll commands. There's a few choices.
; ControlSend , Control, Keys, LibreOffice Writer
ControlSend , Control, {Pgdown}, LibreOffice Writer ; or {Pgup}
ControlSend , Control, {WheelUp}, LibreOffice Writer ; or {wheeldown}
ControlSend , Control, {Up}, LibreOffice Writer ; or {down}
If it lets you move the caret with up/down arrows while the window is inactive, that is probably the most precise option, even if it takes a bit longer. The fastest most precise way is to simulate a click drag also using control send. To use ControlSend you need to figure out which control you're working on. WindowSpy can help you with that.
So first: Find the scroll positions of both windows. Second determine which window is active. Third, nudge the inactive window in the correct direction. Repeat until they're within a certain tolerance range (otherwise it will bounce up and down endlessly).
I can't emphasize this enough, but please make sure that getting the scroll bars in approximately the same positions is sufficiently close before even attempting this. If it isn't, you will have wasted a lot of time fiddling with it. Keep in mind it will be less and less accurate the longer the text is.
If it is an option, I would definitely consider copying the contents of both windows into a program that gives you more access to the controls (or better one that is specialized for this purpose). If you had more access, you could use the paragraph breaks to line up the texts with far more precision.
If you really just want to see both texts side by side (and the paragraphs do line up), you could find a text editor that tells you information like this:
If autohotkey will let you read the text of that information, you can copy the PDF into autohotkey's memory (separated by line) and use autohotkey to show only the corresponding paragraph of the PDF as you move around in the editable document.
Hope something I said helps, good luck.
first off, I'm very new to using API's so please bear with me I'm on a steep learning curve !
I'm creating an application using Silverfrost Fortran FTN95.
I've been trying to initiate the opening of an initial Window within the program which uses the whole screen
useable area (the so-called WORKAREA in API parlance) but am having a problem.
Having used GET_WINDOW_LOCATION# API function within my Fortran code to obtain the dimensions and origin of the max possible area for
the window (without taskbar), I've then defined the 'origin' of the window to be at -n,-n where the border is n pixels thick and I've
increased the window dimensions by (2xn) in each direction so that the other 2 borders will be off-screen at top or under the taskbar at the bottom edge).
Anyway, I'm having difficulty obtaining exactly the same as produced via clicking the 'MAXimise button' on a window.
While the window produced itself seems to occupy the whole area available, when it appears the CAption appears right on the upper edge of the
CAption ba(i.e. not centre justified vertically).
Also, the MINimise, MAXimise and CLOSE buttons in top rh corner of window do not fill the whole depth of the CAption bar (they're about half the depth and indeed appear to be cut-off).
If I subsequently click the window 'MAXimise button' after initial window creation then the CAption and buttons re-align themselves correctly.
This is all illustrated in this image here:-
http://s1164.photobucket.com/user/john_pbucket/media/SilverfrostForumsImageFiles/MAXWIN-Summary_zpscajfx3vx.png.html
Note - I first created the full window with borders within the available screen area (this is the first example shown) where the window Border (8pix wide) is visible
The subsequent attempt to create the window as per the MAXimise button places the window at origin (-8,-8) and I increase the window dimensions by 16 (2xborder width) in each direction in order to get the borders off-screen, but thy're still there.
So, What series of Windows API commands should I be using exactly to get the window to open in a correctly maximised state, and are there any 'subtleties' of alignment and/or spacings I should be aware of which may be causing this problem?
I guess the question boils down to 'what sequence of API commands does the window MAXIMISE button execute ?' but I can't find an answer anywhere.
Maybe there are also some subtleties I need to know about with regard to any windows dimensions parameters which could be creating the anomaly ?
Any help/guidance would be appreciated. Thanks
I am using Visual Basic 6 (please don't laugh), and I would like to dynamically position a control, at runtime, in a position based upon the position of one of the "top level" menu items (such as "File", "Edit", "View").
Unfortunately, VB's Menu control (which is the type of control that these "File", "Edit", etc. things are) does not have any properties like "Top", "Left", "Height", or "Width".
I could just experiment, eyeballing it, and eventually arrive at numbers that I'll hardcode, but for various reasons I would prefer that the code actually figure out where the control should go.
I am thinking that perhaps there is some Windows API call that I can use to figure out the position of the Menu control?
Thanks in advance.
Edit: In case it matters, this is on an MDI form.
Edit #2:
OK, answering my own question:
You can get the position of each item on the main menu bar via the GetMenuBarInfo function, such as:
Dim mbi as MENUBARINFO
mbi.cbSize = LenB(mbi)
GetMenuBarInfo Me.hWnd, OBJID_MENU, lMenuNumber, mbi
Where "lMenuNumber" is 1 for the first (e.g. "File"), 2 for the second (e.g. "Edit"), etc.
That mbi struct has an rcBar member, which is the coordinates of the rectangle where that menu item is.
Unfortunately, it's in absolute coordinates relative to the screen, not relative to the MDI form. So, get the mbi the one you're interested in and of the first, and subtract.
Plus, the position as given in the mbi is in pixels, so convert as necessary to twips or whatever.
This is sort of doomed. Standard menus will vary in appearance and width with the version of Windows and the user's settings for UI fonts and such. For all we know Windows Next/8 may put them along the left side of the window!
If this information were meant to be used it would be available.
Perhaps if you could explain more about what you are trying to do with your UI we might have alternative suggestions.
OK, answering my own question:
You can get the position of each item on the main menu bar via the GetMenuBarInfo function, such as:
Dim mbi as MENUBARINFO
mbi.cbSize = LenB(mbi)
GetMenuBarInfo Me.hWnd, OBJID_MENU, lMenuNumber, mbi
Where "lMenuNumber" is 1 for the first (e.g. "File"), 2 for the second (e.g. "Edit"), etc.
That mbi struct has an rcBar member, which is the coordinates of the rectangle where that menu item is.
Unfortunately, it's in absolute coordinates relative to the screen, not relative to the MDI form. So, get the mbi the one you're interested in and of the first, and subtract.
Plus, the position as given in the mbi is in pixels, so convert as necessary to twips or whatever.
Is there any way to display ruler or scale in visual studio designer mode? Actually I want a scale that will measure the vertical and horizontal space between the components (eg label, text box) and distance of those components from the form body. The scale or ruler will help to show the components with equal horizontal and vertical space in each form.
If I correctly understood, you need to control the distance of the components in a Window(Winforms).
For this, you can create a control class, this class will check the distances between components in your window and establish rules of location for each component on run time.
For control each component you can use this:
For Each ctrl As Control In Me.Controls
If TypeName(ctrl) = "TextBox" Then
If Not ctrl.Width = 0 Then
MsgBox(ctrl.Name)
'Do Something
End If
End If
Next
This rules of location can be applied on your window "load" event
You can learn more about location of component through this links:
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.location(v=vs.110).aspx
Changing the location of a control on a windows form programatically using VB.net?
PS. You can see I use vb,net language examples but this can be easily converted to C# or other language.