home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / QBAS / VBTIPS.ZIP / VB-TIPS.TXT
Text File  |  1991-08-04  |  33KB  |  1,078 lines

  1.                                    VB Tips
  2.  
  3.                       Compiled by Nelson Ford, 71355,470
  4.                   For distribution on the MSLANG Forum only.
  5.  
  6. This is a compilation of information about VB that has flowed through the
  7. Forum here (plus my own input). The hope is that new users can refer to this
  8. file rather than having to ask the same questions on the forum all the time.
  9. Experienced users whose memory is bad as mine might benefit from this as well.
  10.  
  11. Main contributors are:
  12. Jonathan Zuck
  13. Keith Funk
  14. Mark Novisoff
  15. Ted Young
  16. Dennis Harrington
  17. the Microsoft Section Leaders
  18.  
  19. Note: There are a number of very valuable free DLL's and VB routines on DL1
  20. ("New Uploads") and DL6 ("VB"). It is highly recommended that you Browse these
  21. two DL's. A few are mentioned in this file, but many more are available.
  22.  
  23. Uploading: If you would like to share your coding with others, upload to DL1,
  24. not DL6. All new uploads go to DL1 for 30 days and then are moved to the
  25. appropriate DL (6, for VB). Do NOT put VBRUN100.DLL in your archive, nor any
  26. of the other DLLs that are available on here. All that does is increase the
  27. download time for everyone who already has those files. Instead, in your
  28. upload description, just tell people that they need to download those files
  29. too, if they don't already have them.
  30.  
  31.  
  32. CONTENTS:  [Names in brackets are files in the MSLANG DL's 1 or 6.]
  33.  
  34. General:
  35.   Button Colors
  36.   .EXE Size
  37.   Far Pointers
  38.   Help Files
  39.   Icon - Get Rid Of
  40.   Networks & VB
  41.   Saving Your Work
  42.   Screen type
  43.   Sound.DLL (Need TPW)
  44.   Type...End Type
  45.  
  46. Arrays:
  47.   Dynamic Arrays
  48.   [VBSORT]
  49.  
  50. Combo Boxes:
  51.   Changing Text
  52.   Color
  53.   Pseudo Combo Dropdown List Box
  54.  
  55. DOS Functions:
  56.   File Copying
  57.   [VBDOS]
  58.  
  59. Form & Control Placement, Sizing:
  60.   Controlling Form Size
  61.   "Floating" Window
  62.   Form Size and Granularity
  63.   Grouped Controls
  64.   Mouse Pointer, Position
  65.   Placing Forms, Controls
  66.  
  67. File I/O:
  68.   Btrieve
  69.   Deleting Data in Sequential File
  70.   MKI$ & CVI in VB
  71.  
  72. Fonts:
  73.   Using Different Fonts, Colors in a Box
  74.   System Font
  75.  
  76. Forms: (Also see "Form & Control Placement, Sizing", above)
  77.   Focus & Order of Execution
  78.     Focus on StartUp
  79.   SetFocus
  80.  
  81. Keyboard & Mouse:
  82.   Trapping Double-Click before Click
  83.   Using "Enter" in Place of "Tab"
  84.  
  85. List Boxes:
  86.   Finding an Item Added to a Sorted List Box
  87.   Inhibiting Screen Updating
  88.   Linking Sorted and Unsorted List Boxes
  89.   Searching For An Item
  90.   [VBSORT]
  91.  
  92. Picture Boxes:
  93.   Copying a Drawing to Clipboard
  94.   Drawing - Scale
  95.   Saving Picture Files
  96.  
  97. Printer:
  98.   Printer Setup
  99.   Printer Control Codes
  100.   Printing Forms
  101.  
  102. System:
  103.   Calling the Windows 3 Calculator
  104.   hWnd for a Control
  105.   .INI Files
  106.   Multi-Instance App Prevention
  107.   SendMessage
  108.   Windows Termination
  109.  
  110. Text Boxes:
  111.   Cursor (text), Position
  112.   Data Entry Masking
  113.   Data Entry Routine
  114.   Data Entry Text Limit
  115.   Flicker
  116.   Flashing Text
  117.  
  118. Timer
  119.   Using the Timer
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126. Button Colors:
  127.  
  128. Button colors are controlled by WIN.INI.  See the 6/11/91 issue of PC Mag
  129. for details. Of course, changes you make there apply globally.
  130. ---------------
  131.  
  132.  
  133. .EXE Size:
  134.  
  135. 1. Long variable names increase EXE size.
  136. 2. Comments add a couple of bytes per line to EXE size.
  137. 3. A Global DIM of an array adds to EXE size.
  138.    For example:
  139.      Global sample as string*20000
  140.    will add about 20k to the size of the EXE file, but the same DIM
  141.    in a Form will not.
  142. ---------------
  143.  
  144.  
  145.  
  146. Far Pointers:
  147.  
  148. Here's a question regarding direct calls to the Windows API from a Visual Basic
  149. procedure. In trying to set up a call to the API entry "Polygon", I discovered
  150. that one of the arguments is a far pointer to an array of structures. I've
  151. searched the VB documentation, but can't find a method that will return the
  152. run-time address of a variable (or an array element) as a far pointer.
  153.  
  154. Is there a VB technique for passing a far pointer as an argument -- if so, how?
  155. Also, how would such an argument be specified in the corresponding DECLARE
  156. statement? Many thanks to anyone who can supply this information.
  157.  
  158. (Fm: Mark Novisoff (TA) 73047,3706)
  159.  
  160. If the structures themselves don't require pointers (which is the case with
  161. Polygon), then it's a piece of cake. Use TYPE...END TYPE to declare a model
  162. structure, and then DIM an array of the structures.
  163.  
  164. In your Global module Declare statement, use the "As" syntax:
  165.  
  166.    Type Points
  167.      ' Define the structure
  168.      X As Integer
  169.      Y As Integer
  170.    End Type
  171.    Declare Function Polygon Lib "Gdi" (hDC%, MyStruc As Points, nCount%)
  172.  
  173. In your code:
  174.  
  175.    ReDim MyStrucArray(0 To 10) As Points
  176.    ' Set the variables in the array here
  177.    Result = Polygon(hDC%, MyStrucArray(0), nCount%)
  178.  
  179. Note that this results in passing a far pointer to the zeroth element of the
  180. array. Because the length of the structure is known to the DLL routine, it
  181. will figure out where the rest of the elements are.
  182. ---------------
  183.  
  184.  
  185. Help Files:
  186.  
  187. 1. You can create Help files more easily and cheaply than with the SDK: use
  188.    the help compiler (HC.EXE) that comes with Turbo Pascal for Windows and
  189.    Borland C++.
  190.  
  191. 2. A shareware program named Xantippe is a good front-end for making help
  192.    files.
  193. ---------------
  194.  
  195.  
  196. Icon - Get Rid Of:
  197.  
  198. 1. Click on the form
  199. 2. Select the Icon property from the properties bar.
  200. 3. Click on the middle portion of the properties bar, where (icon) shows.
  201. 4. Press the Del key
  202. ---------------
  203.  
  204.  
  205. Networks & VB:
  206.  
  207. VBRUN100.DLL and VB apps (EXE's) should be placed on each machine's hard disk
  208. to avoid significant performance degradation.
  209. ---------------
  210.  
  211.  
  212. Saving Your Work:
  213.  
  214. Several users have reported losing their work in different ways. If you always
  215. save before test-running your code, you can prevent most losses. If you get
  216. any kind of warnings or other indications that something might be wrong with
  217. your system, try to get into DOS (or use File Manager) and copy your project
  218. files to a backup directory. Many people have had their project files
  219. "trashed" when the system went screwy. There is a utility on the DLs here for
  220. copying all the files in a Project - very handy to have, although a better
  221. idea, generally, is to keep each project in its own subdirectory.
  222. ---------------
  223.  
  224.  
  225. Screen type:
  226.  
  227. z = Screen.Height
  228. If z = 6000 Then
  229.   Type$="CGA"
  230. ElseIf z = 7000 Then
  231.   Type$ = "EGA"
  232. ElseIf z > 7000 Then
  233.   Type$ = "VGA or Better"
  234. End if
  235.  
  236. There's another way to do this, calling GetDeviceCaps to find out the vertical
  237. resolution; but this method is a lot easier... BTW, if you want to know if it
  238. is exactly VGA, not "or better" (i.e., better than 640x480), the number for
  239. that 7200 if memory serves...
  240. ---------------
  241.  
  242.  
  243. Sound.DLL (Need TPW):
  244.  
  245. Here's my DLL written in TPW. I had no documentation besides the Windows
  246. Programmer's Reference so maybe someone here can tell me if I'm on the right
  247. track. Some questions come to mind such as: how large shoule I make the voice
  248. queue? Is it unecessary to open and close the sound device every time I want to
  249. set a note?
  250.  
  251. library SoundDLL;
  252.  
  253. uses WinTypes, WinProcs;
  254.  
  255. procedure PlayNote(Note, nLength, Cdots: Integer);export; begin
  256.     OpenSound;
  257.     SetVoiceAccent(1,100,255,S_NORMAL,0);
  258.     SetVoiceQueueSize(1,1000);
  259.     SetVoiceNote(1,Note,nLength,Cdots);
  260.     StartSound;
  261.     WaitSoundState(S_QueueEmpty);
  262.     CloseSound;
  263.     StopSound; end;
  264.  
  265. exports PlayNote index 1;
  266.  
  267. begin end.
  268.  
  269. The declaration in VB (general):
  270.  
  271. Declare Sub PlayNote Lib "e:\tpw\output\soundll.dll" (ByVal note%, ByVal
  272. Length%, ByVal Cdots%)
  273.  
  274. (Mark N.):
  275. The size of the voice queue is one of those numbers that you simply "Pull
  276. out of thin air". It depends on what you're going to do. For example, in
  277. VBTools, we set the queue to 8K because we include several large pieces of
  278. music.
  279.  
  280. OTOH, if you're going to play single notes on an occasional basis, then 1K
  281. should be plenty.
  282.  
  283. It is not necessary to open and close the sound device every time. In fact,
  284. if you close it while there are notes in the queue, they'll be lost!
  285. I suggest that you do what we've done in VBTools:
  286.  
  287. 1. Open the sound device when the user first calls your proc.
  288. 2. If the device is open, then close it when your DLL unloads.
  289. 3. Give the user a method to close it so that sound can be generated by
  290.    other apps.
  291. ---------------
  292.  
  293.  
  294. Type...End Type:
  295.  
  296. Be sure when using arrays with Type variables to put the variable number
  297. AFTER the type name and BEFORE the element name.
  298. Example:
  299. Type Test
  300.   a1 as string*1
  301.   a2 as string*2
  302. End Type
  303.  
  304. Dim TypeName as Test
  305. TypeName(i).a1 = "xyz"  NOT  TypeName.a1(i) = "xyz"
  306. ---------------
  307.  
  308.  
  309. Dynamic Arrays:
  310.  
  311. In order to use a dynamic array, you must REDIM it in a module or form:
  312. (Mark N.)
  313.  
  314. Global:
  315.   Type SomeType
  316.        X As Integer
  317.        etc
  318.   End Type
  319.   Global ArrayName() As SomeType
  320.  
  321. Module or Form:
  322.   Redim ArrayName(x) As SomeType    ' x can be a number or a variable
  323. ---------------
  324.  
  325.  
  326. Combo Boxes: Changing Text
  327.  
  328. Text is read-only, but you can accomplish the same thing with:
  329. Combo1.ListIndex = 3. This would select and display the 4th item in the list.
  330. ---------------
  331.  
  332.  
  333. Combo Boxes: Color
  334.  
  335. BackColor does not apply to the list portion of the combo box, only the 'edit'
  336. part.
  337. ---------------
  338.  
  339.  
  340. Pseudo Combo Dropdown List Box:
  341.  
  342. To implement a pseudo Combo DropDown List Box like the one used in the VB
  343. Help/Index/Search Window: As you type text in a Text Box, VB hilights the
  344. first entry in the List Box that matches what you have typed. This can be
  345. implemented in VB by using a Text Box, a List Box and the API message
  346. LB_FINDSTRING.
  347. ---------------
  348.  
  349.  
  350. File Copying:
  351.  
  352. Open "FileIn" for Binary as #1
  353. whole = Lof(1) \ 32000        'numer of whole 32768 byte chunks
  354. part = Lof(1) MOD 32000     'remaining bytes at end of file
  355. buffer$ = string$(32000,0)
  356. start& = 1
  357. Open "FileOut" for Binary as #2
  358. for x=1 to whole                     'this for-next loop will copy 32,000
  359.        get #1, start&, buffer$       'byte chunks at a time. If there is
  360.        Put #2, start&, buffer$       'less than 32,000 bytes in the file,
  361.        start&= start& + 32000        'whole = 0  and the loop is bypassed.
  362. next x
  363. buffer$ = string$(part, 0)           'this part of the routine will copy
  364. get #1, start&, buffer$              'the remaining bytes at the end of the
  365. put #2, start&, buffer$              'file.
  366. close
  367. ---------------
  368.  
  369.  
  370. Controlling Form Size:
  371.  
  372. Set MaxButton to False so the user can't maximize the form.
  373.  
  374. General_Declarations:
  375. Dim OldWidth As Single                       '-- width before resizing.
  376. Dim OldHeight As Single                      '-- height before resizing.
  377. Const MinWidth = 6000!, MaxWidth = 9000!     '-- change these values to...
  378. Const MinHeight = 3000!, MaxHeight = 6000!   '-- the ones you want.
  379.  
  380. Sub Form_Load ()
  381.    OldWidth = Width
  382.    OldHeight = Height
  383. End Sub
  384.  
  385. Sub Form_Resize ()
  386.   If WindowState <> 1 then        '-- allows user to minimize window.
  387.     If Width < MinWidth Or Width > MaxWidth Then
  388.       Width = OldWidth
  389.     Else
  390.       OldWidth = Width
  391.     End If
  392.     If Height < MinHeight Or Height > MaxHeight Then
  393.       Height = OldHeight
  394.     Else
  395.       OldHeight = Height
  396.     End If
  397.   End If
  398. End Sub
  399. ---------------
  400.  
  401.  
  402. "Floating" Window:
  403.  
  404. Sometimes you may want a small window to show above the current window, but VB
  405. does not offer this feature. Here is how to force it (courtesy of Ted Young):
  406.  
  407. Global.Bas:
  408. Declare Sub SetWindowPos Lib "User" (ByVal hWnd As Integer,
  409.                                      ByVal hWndInsertAfter as Integer,
  410.                                      ByVal X as Integer,
  411.           (put this all on  -->      ByVal Y as Integer,
  412.            one line)                 ByVal cx as Integer,
  413.                                      ByVal cy as Integer,
  414.                                      ByVal wFlags as Integer)
  415. Declare Function GetWindow Lib "User" (ByVal hWnd as Integer,
  416.                                        ByVal wCmd as Integer) As Integer
  417. '  Set WindowPos Flags:
  418. Global Const SWP_Nosize = &H1
  419. Global Const SWP_NoMove = &H2
  420. Global Const SWP_NoActivate = &H10
  421. Global Const SWP_ShowWindow = &H40
  422.  
  423. Form1, Load:
  424.   Form2.Show  ' this is the "Floating" window
  425. End Sub
  426.  
  427. Form1, Timer1  (set Interval to 50 in Properties)
  428. Sub Timer1_Timer
  429.   If GetWindow(Form2.hwnd,0) <> Form2.hWnd Then
  430.     wFlags = SWP_Nomove or Swp_Nosize or Swp_ShowWindow or Swp_NoActivate
  431.     SetWindowPos Form2.hWnd, 0, 0, 0, 0, 0, wFlags
  432.   End If
  433. End Sub
  434. ---------------
  435.  
  436.  
  437. Form Size and Granularity
  438.  
  439. Check the "Granularity" under the Desktop settings in the Control Panel. If
  440. this number is anything but zero, you'll get the effect of all windows only
  441. being able to be sized and placed by increments of 16 pixels multiplied by the
  442. Granularity number. Set it to zero and this should fix things.
  443. ---------------
  444.  
  445.  
  446. Grouped Controls:
  447.  
  448. If you want to group a set of controls within another control, such as a
  449. Frame, you cannot do so by double-clicking the control and moving it into the
  450. Frame. You must single-click the control and then click-and-drag INSIDE the
  451. frame (or other control) to draw the added control.
  452. ---------------
  453.  
  454.  
  455. Mouse Pointer, Position:
  456.  
  457. Sometimes it nice to be able to place the user's mouse cursor for him. One
  458. example is when the user has to click on a button to continue, you can put the
  459. cursor on the box for him.
  460.  
  461. Declare Sub SetCursorPos Lib "User" (ByVal x%, ByVal y%)
  462. example:  Call SetCursorPos(100,200)
  463.  
  464. The above code will do a rudimentary job of positioning the cursor.
  465. Here is some more information about positioning:
  466.  
  467. Using the Windows API call SetCursorPos, you can move the mouse to any
  468. location on the screen. Note that the x & y coordinates are *screen*
  469. coordinates.  You'll need to translate the coordinates in your VB
  470. program to screen coordinates.  One way is to get the current coordinates of
  471. the mouse using GetCursorPos, and then moving the mouse in relation to those
  472. coordinates.  Note that GetCursorPos returns the coordinates in a Type
  473. structure (see below).  You can also use the API functions ClientToScreen
  474. and ScreenToClient to convert the coordinates from the Client (your Form) to
  475. the Screen, and back.  Experiment with the functions to see which suits your
  476. purpose, and holler if you have any questions.  Also, I'd recommend
  477. downloading WINAPI.ZIP which has all the Declares necessary for calling the
  478. API functions.
  479.  
  480.  Declare Sub SetCursorPos Lib "User" (byval x as integer, byval y as integer)
  481.  Declare Sub GetCursorPos Lib "User" (lpPoint as PointAPI)
  482.  Declare Sub ClientToScreen Lib "User" (ByVal hWnd As Integer,
  483.                                         lpPoint As PointAPI)
  484.  Declare Sub ScreenToClient Lib "User" (ByVal hWnd As Integer,
  485.                                         lpPoint As PointAPI)
  486.  Type PointAPI
  487.       x As Integer
  488.       y As Integer
  489.  End Type
  490.  
  491. Note: Type the declares on one line, not two as shown above.
  492. ---------------
  493.  
  494.  
  495. Placing Forms, Controls:
  496.  
  497. Sub InitializeWindow
  498.    Height = Screen.Height * .75
  499.    Width = Screen.Width * .75
  500.    Top = (Screen.Height - Height) / 2
  501.    Left = (Screen.Width - Width) / 2 '
  502. *** Then place and size your controls.
  503. End Sub
  504.  
  505. If you want to make the form a specific size, then Scale it in inches,
  506. centimeters, points, or twips (a twip is 1/20th of a point), and don't bother
  507. making references to Screen.Height or .Width.
  508. ---------------
  509.  
  510.  
  511. Btrieve:
  512.  
  513. There are the DLL-Declarations for Btrieve. The requestor must be loaded
  514. before Windows:
  515.  
  516. Declare Function wbtrvinit Lib "wbtrcall.dll" (ByVal init$) As Integer
  517. Declare Sub wbtrvstop Lib "wbtrcall.dll" ()
  518. Declare Function btrcall Lib "wbtrcall.dll" (ByVal a%, ByVal b$, ByVal c$,
  519.   d%, ByVal e$, ByVal f%, ByVal g%) As Integer
  520.  
  521. FIELD and VARPTR are definitely not necessary. You'll need to set up a
  522. user-defined type to work with Btrieve (instead of FIELD). The call syntax for
  523. Win Btrieve takes care of pointing to the record (instead of VARPTR). Just be
  524. sure to pass any variable-length strings (i.e, A$) BYVAL.
  525. ---------------
  526.  
  527.  
  528. Deleting Data in Sequential File:
  529.  
  530. Here's an idea for deleting text in the middle of a sequential file that might
  531. work.  Haven't tried it, but it seems like it would work and wouldn't be
  532. difficult to implement. First, you'll have to save file pointers to the
  533. beginning of each message (e.g., in a Long Int array during message load using
  534. the SEEK function) in MsgPtr&(). Now, if you want to delete message 50 and
  535. there are a total of 200, you'd do the following:
  536.  
  537.  MsgToDelete = 50
  538.  TotMsgs = 200
  539.  BlockSize& = 32768&'-- make this larger or smaller to find optimum size
  540.  Temp$ = Space$(BlockSize&)
  541.  Open "MESSAGE.MSG" For Binary As #1
  542.  
  543.  SizeOfFile& = LOF(1)
  544.  SizeToMove& = SizeOfFile& - MsgPtr&(MsgToDelete + 1)
  545.  NumBlocks = SizeToMove \ BlockSize&
  546.  LeftOver = SizeToMove& - Num32KBlocks * BlockSize&
  547.  FromLoc& = MsgPtr&(MsgToDelete + 1)
  548.  ToLoc& = MsgPtr&(MsgToDelete)
  549.  
  550.  For i = 1 To NumBlocks
  551.      Seek #1, FromLoc& + BlockSize& * (i - 1)
  552.      Get #1, Temp$
  553.      Seek #1, ToLoc& + BlockSize& * (i - 1)
  554.      Put #1, Temp$
  555.  Next
  556.  Temp$ = Space$(LeftOver)
  557.  Seek #1, FromLoc& + BlockSize& * NumBlocks
  558.  Get #1, Temp$
  559.  Seek #1, ToLoc& + BlockSize& * NumBlocks
  560.  Put #1, Temp$
  561.  '-- Now adjust the MsgPtr& array
  562.  TotMsgs = TotMsgs - 1
  563.  Adjust = MsgPtr&(MsgToDelete + 1) - MsgPtr&(MsgToDelete)
  564.  For i = MsgToDelete To TotMsgs
  565.      MsgPtr&(i) = MsgPtr&(i + 1) - Adjust
  566.  Next
  567. ---------------
  568.  
  569.  
  570. MKI$ & CVI in VB:
  571.  
  572. function MKI$ (Work%)
  573.   MKI$ = Chr$ (Work% MOD 256) + Chr$ (Work% \ 256)
  574. end function
  575.  
  576. function CVI% (Work$)
  577.   CVI% = (ASC (Left$ (Work$, 1)) * 256) + ASC (Right$ (Work$, 1))
  578. end function
  579. ---------------
  580.  
  581.  
  582. Using Different Fonts, Colors in a Box:
  583.  
  584. You can use different colors and fonts with the .Print method in a Picture Box
  585. (during run-time) to get different fonts and colors in a box. You can also use
  586. different fonts and colors in the form itself.
  587. ---------------
  588.  
  589.  
  590. System Font
  591.  
  592. In a List box using the System font, 9 pitch is non-proportional, 9.75 is
  593. proportional.
  594. ---------------
  595.  
  596.  
  597. Focus & Order of Execution:
  598.  
  599. Some times VB will delaying doing something while it executes more code. For
  600. example, if you write a lot of stuff to a List box in a tight loop, the List
  601. box display on the screen will not be shown updated until the code comes to a
  602. "resting point" (ie: waiting for user input).
  603.  
  604. Focus on StartUp
  605.  
  606. If you try to display a form on startup that has your logo, copyright notice,
  607. etc, you may find that the form displays, but the detail on it does not. You
  608. have to force VB to wait until the detail is displayed. One way to do that is
  609. do define a Global variable and when you are finished with the Title box, set
  610. the variable ("Continue", in the example below) to true (-1).
  611.  
  612. Form1_Load:
  613. TitleForm.Show
  614. Do
  615.   x = DoEvents() 'allow control to the system
  616. Loop Until Continue = -1
  617.  
  618. Another way is to "Loop Until Len(Label1.Caption) > 0" (You can omit the >0.)
  619. This assumes you have a Label1 box, of course.
  620.  
  621.  
  622. Another problem is trying to get some other form to load and to transfer focus
  623. to it during start-up, and then continuing the start-up. What happens is that
  624. you can do a Form2.Show, for example, but after the Form2_Load code (if any)
  625. is run, focus will immediately return to the original form. Users don't get a
  626. chance to interact with Form2. If Form2 is an installation form, for example,
  627. the installation will never get done.
  628.  
  629. The solution is to use Show Modal (eg: Form2.Show 1), with the possible
  630. drawback being that you have to Unload the form to return to the calling form.
  631. ---------------
  632.  
  633.  
  634. SetFocus:
  635.  
  636. You cannot do a SetFocus until a form is visible. Use Show instead.
  637. ---------------
  638.  
  639.  
  640. Trapping Double-Click before Click:
  641.  
  642. If you have code assigned to both the Click event AND the Double-Click,
  643. when you double-click, VB will execute the Click code on the first click of
  644. the double and the D-C code on the second click.
  645.  
  646. Here's how to avoid executing the Click when double clicking (J. Zuck):
  647.  
  648. On the first Click, setup a timer event to process the click in the "near"
  649. future. If a DblClick occurs within the timer.interval it disables the timer
  650. and processes the DblClick instead.
  651. ---------------
  652.  
  653. Using "Enter" in Place of "Tab":
  654.  
  655. Sub Text1_KeyPress(KeyAscii As Integer)
  656.     If KeyAscii = 13 Then
  657.        KeyAscii = 0            ' Avoid the beep
  658.        SendKeys "{Tab}"        ' Simulate pressing the Tab key
  659.     End If
  660. End Sub
  661. ---------------
  662.  
  663.  
  664. Finding An Item Added to a Sorted List Box:
  665.  
  666. Instead of using List.AddItem, use
  667. SendMessage(hWnd, LB_ADDSTRING, wParam, lParam).
  668.  
  669. This adds the item to the list and returns the ListIndex of the newly added
  670. item.
  671. ---------------
  672.  
  673.  
  674. Inhibiting Screen Updating:
  675.  
  676. If you are adding a whole bunch of items to a list box all at once like
  677. assigning items from an array from within a loop, you might want to use the API
  678. WM_SETREDRAW message to prevent the list box from being continually updated on
  679. the screen as each item is added. Not only does this stop the flickering, but
  680. it makes adding the items much faster.
  681. ---------------
  682.  
  683.  
  684. Linking Sorted and Unsorted List Boxes:
  685.  
  686. You can append a 4 byte value to an item in a List Box by using the
  687. LB_SETITEMDATA message and you can retrieve the value using the LB_GETITEMDATA
  688. message. You can use the ItemData to store the location of the item in the
  689. unsorted array.
  690. ---------------
  691.  
  692.  
  693. List Box: Searching For An Item:
  694.  
  695. Object: (general)  Proc: FindItem
  696. Sub FindItem (Lst as Control, a$)
  697. U = Lst.ListCount
  698. L = 0
  699. Do
  700.   IF U < L Then
  701.     Lst.Index = I
  702.     MsgBox "Not Found"
  703.     Exit Sub
  704.   I = (L + U) / 2
  705.   IF P = X(I) Then
  706.     Lst.Index = I  'Found. Set ".Index" accordingly
  707.     Exit Sub
  708.   ElseIf P > X(I) Then
  709.     L = I + 1
  710.   Else
  711.     U = I - 1
  712.   End If
  713. Loop
  714.  
  715. Note: If a match is not found, we still set ".Index" to "I" so that you can
  716.       do an alphabetical insert, if you wish.
  717. ---------------
  718.  
  719.  
  720. Copying a Drawing to Clipboard
  721.  
  722. The following will work, IF Picture1.AutoRedraw is True.  (J. Zuck)
  723.  
  724. Sub Picture1_Click ()
  725.    ClipBoard.Clear
  726.    Picture1.Circle (1000, 1000), 500
  727.    ClipBoard.SetData (Picture1.Image)      'Picture1.Picture won't work.
  728. End Sub
  729.  
  730. Sub Picture2_Click ()
  731.    Picture2.Picture = ClipBoard.GetData()  'Picture2.Image is illegal.
  732. End Sub
  733. ---------------
  734.  
  735.  
  736. Drawing - Scale
  737.  
  738. You can change the scale for pictures from twips to virtually anything you
  739. want (100 allows you to do easy percent drawing), but you still have to draw
  740. by using pixel measurements. I found this out by trying to set DrawWidth to
  741. draw a line covering the middle 20% of a small picture.
  742. ---------------
  743.  
  744.  
  745. Saving Picture Files:
  746.  
  747. It seems that SavePicture saves everything when the property is a Picture on a
  748. Form, but it saves only the original loaded file when the property is a
  749. Picture on a PictureBox, and it saves only the new drawn lines when the
  750. property is an Image in a PictureBox.
  751.  
  752. The following should fix it:
  753.  
  754.   'BMP or WMF loaded into Picture1
  755.   'Redlines added with Line, Pset, etc., then
  756.   Form1.Picture = Picture1.Image
  757.   PictureSave Form1.Image, "TEST1.BMP"
  758.  
  759. This works with WMFs but they cannot be changed and saved as WMFs, they get
  760. saved as BMPs, which may or may not be ok with you.
  761. ---------------
  762.  
  763.  
  764. Printer Setup:
  765.  
  766. Declare Function LoadLibrary Lib "kernel" (Byval LibName$) As Integer
  767. Declare Function FreeLibrary Lib "kernel" (hLib%) As Integer
  768. Declare Function DeviceMode Lib "HPPCL.DRV" (hWnd%, hLib%, Dev$, Port$)
  769.  
  770. SUB Command1_Click ()
  771.     hLib% = LoadLib% ("HPPCL.DRV")
  772.     Result% = DeviceMode (hWnd, hLib%,"HP / PCL LaserJet","LPT1:")
  773.     Result% = FreeLibrary (hLib%)
  774. END SUB
  775.  
  776. Declare Function LoadLibrary Lib "KERNEL" (ByVal lpLibFileName$) As Integer
  777. Declare Sub FreeLibrary Lib "KERNEL" (ByVal hLibModule%)
  778.  
  779. Then change PSetupMNU_Click to read:
  780.  
  781.  Sub PSetupMNU_Click ()
  782.    RetStr$ = String$(256, 0)
  783.    RetStrSize% = Len(RetStr$)
  784.    x% = GetProfileString("windows", "device", "", RetStr$, RetStrSize%)
  785.  
  786.    i% = InStr(RetStr$, ",")
  787.    If i% > 0 Then
  788.      a$ = Left$(RetStr$, i% - 1)
  789.      b$ = Right$(RetStr$, Len(RetStr$) - i%)
  790.      j% = InStr(b$, ",")
  791.        If j% > 0 Then
  792.            DriverName$ = Left$(b$, j% - 1)
  793.            PortName$ = Right$(b$, Len(b$) - j%)
  794.        End If
  795.    End If
  796.  
  797.    If Len(DriverName$) > 0 And Len(PortName$) > 0 Then
  798.      LibHandle% = LoadLibrary("PSETUP.DLL")
  799.         If LibHandle% >= 32 Then
  800.           r% = DoPrinterSetup(Form1.hwnd, DriverName$, PortName$)
  801.           FreeLibrary LibHandle%
  802.           If Not r% Then MsgBox "Can't run Printer Setup", 64, "Printer Setup"
  803.         End If
  804.    Else
  805.         MsgBox "No default printer selected", 64, "Printer Setup"
  806.    End If
  807.  End Sub
  808. ---------------
  809.  
  810.  
  811. Printer Control Codes:
  812.  
  813. The normal Print command in VB will not properly send printer control codes
  814. through to the printer. Here is how to get around VB:
  815.  
  816. Open "LPT1" For Binary As #1  'note: no colon on LPT1
  817. Put #1,,<put your control codes here>
  818. Close #1
  819. ---------------
  820.  
  821.  
  822. Printing Forms:
  823.  
  824. In VB's File menu, there is a Print option for printing forms and code. On an
  825. HPLJ, the top of forms will be cut off because VB/Windows tries to print in
  826. the top-most part of the page, which is, of course, unusable on an HPLJ.
  827. ---------------
  828.  
  829.  
  830. Calling the Windows 3 Calculator:
  831.  
  832. Declare Function isapploaded Lib "kernel"(name$) As Integer Alias
  833.         "GetModuleHandle"  'All one line
  834.  
  835. Sub MAIN
  836.   If isapploaded("calc") = 0 Then
  837.     Shell("calc.exe", 1)
  838.   Else
  839.     AppActivate "Calculator", 0
  840.     SendKeys "% r"
  841.   End If
  842. End Sub
  843. ---------------
  844.  
  845.  
  846. hWnd for a Control:
  847.  
  848. All you have to do is use the Windows API GetFocus call after setting the
  849. focus to the desired control:
  850.  
  851. General Section
  852.   Declare Function GetFocus% Lib "User" ()
  853.  
  854. In your code
  855.   Command1.SetFocus
  856.   ButtonHwnd = GetFocus()
  857. ---------------
  858.  
  859.  
  860. .INI Files:
  861.  
  862. The calls to read/write private ini file are:
  863.  
  864. GetPrivateProfileInt - returns int value
  865.   WORD GetPrivateProfileInt( LPSTR lpApplicationName
  866.                            , LPSTR lpKeyName
  867.                            , int nDefault
  868.                            , LPSTR lpFileName )
  869.  
  870. GetPrivateProfileString - returns string
  871. WritePriviteProfileString
  872.  
  873. The calls to read/write the WIN.ini file are almost the same -- just remove
  874. the Private.
  875. ---------------
  876.  
  877.  
  878. Multi-Instance App Prevention:
  879.  
  880. Here's an example of the Multi-Instance App prevention, as well as an example
  881. of calling DLL functions. You simply "Declare" them at the beginning (taking
  882. them from a provided .DEFs file) then use them!  (See the "Dummy" returning
  883. functions lower down.)
  884.  
  885. Dim PassString$         ' declare a local string to hold user's password
  886.  
  887. Const SW_SHOWNORMAL = 1
  888. Declare Function GetActiveWindow Lib "User" () As Integer
  889. Declare Function ShowWindow Lib "User" (ByVal hWnd As Integer, ByVal nCmdShow As
  890.  
  891. Form_Load()                ' this executes when the PassWord form loads
  892.  
  893.   On Error GoTo NoLink        ' setup our no-link error handler
  894.   SystemLink.LinkMode = 1     ' try to establish a hot link with a server
  895.   On Error GoTo 0             ' disable error trapping
  896.  
  897.   MsgBox "A DDE LINK HAS BEEN ESTABLISHED WITH A SERVER!!!"
  898.  
  899.   AppActivate "SalHist"                       ' activate the "other" SalHist
  900.   hActive = GetActiveWindow()                 ' pickup it's hWnd handle
  901.   Dummy = SetFocusAPI(hActive)                ' give it the system focus
  902.   Dummy = ShowWindow(hActive, SW_SHOWNORMAL)  ' and restore its size
  903.   End                                         ' finally, terminate this app!
  904.  
  905.   On Error GoTo 0             ' disable our temporary error trapping
  906.   SystemLink.LinkMode = 0     ' abandon our local DDE connection attempt
  907.   LinkMode = 1                ' and establish ourselves as a DDE server...
  908.  
  909.   CenterForm PassWord         ' (My standard.lib form-centering routine)
  910.   PassString$ = ""            ' init the
  911.   PassWord.Show 1             ' present the PassWord form for user data
  912.                               ' entry.  The "1" makes it "modal" (stickier)
  913. End Sub                       ' end of the Form_Load subroutine.
  914. ---------------
  915.  
  916.  
  917. SendMessage:
  918.  
  919. In the Global module:
  920. 'type the declaration on one line
  921. Declare Function Sendmessage Lib "user" (ByVal Hwnd%,
  922.                                          ByVal Msg%,
  923.                                          ByVal wParam%,
  924.                                          ByVal lparam As Any) As Long
  925.  
  926. Global Const wm_WinIniChange = &H1A
  927.  
  928. Then, for example:
  929. Sub Command1_Click
  930. x& = Sendmessage(&HFFFF, wm_WinIniChange, 0, ByVal "")
  931. End Sub
  932. ---------------
  933.  
  934.  
  935. Windows Termination:
  936.  
  937. If Windows is about to terminate, it will invoke your Form_Unload procedure. In
  938. that proc, you can do whatever you want, including telling Windows to *cancel*
  939. the shutdown!
  940.  
  941. To cancel the Form_Unload, use: Cancel=True.
  942. ---------------
  943.  
  944.  
  945. Cursor (text), Position:
  946.  
  947. You can get the cursor position in a text box as follows:
  948.  
  949. CursPos = Text1.SelStart
  950. ---------------
  951.  
  952.  
  953. Data Entry Masking:
  954.  
  955. General - Declarations:
  956.   Dim Txt as String
  957. Text1_GotFocus ():
  958.   Text1.Text = "01/23"
  959.   Txt$ = Text1.Text    'Txt$ = "01/23". This intitialization keeps KeyUp
  960.                        '  from kicking in until a key has been pressed.
  961. Text1_KeyPress:        'Assume cursor is on the "/" and "X" is pressed.
  962.   Txt$ = Text1.Text    'Note: Text1.Text is still "01/23" at this point.
  963. Text1_KeyUp
  964.   If Txt$ <> Text1.Text Then  'now Text1.Text is "01X/23"
  965.      Call CursPos(Text1)      'and Txt$ is still "01/23"
  966.   End If
  967. CursPos(Ctl as Control)
  968.   i = 0
  969.   Do While Len(Txt$) > i
  970.     i = i + 1
  971.     If Mid$(Txt$, i, 1) <> mid$(Ctl.Text, i, 1) Then
  972.       pos = i
  973.       Exit Do
  974.     End If
  975.   Loop
  976.   if pos = 3 then  'Example of preventing an unwanted change:
  977.     Ctl.Text = Txt$          'Reset Text1.Text and
  978.     Ctl.SelStart = pos - 1   'put the cursor back where it was.
  979.   end if
  980. ---------------
  981.  
  982.  
  983. Data Entry Routine:
  984.  
  985. .FRM Code:
  986. Sub Form_Load ()
  987.    SomeForm.Show
  988.    InitStdControl AcctNo1            'Initialize standard edit controls
  989.    InitStdControl AcctNo2
  990.    .. etc
  991. End Sub
  992.  
  993. Sub AcctNo1_KeyPress (KeyAscii As Integer)
  994.    StdEdit AcctNo1, KeyAscii         'Use standard editing procedure
  995. End Sub
  996.  
  997. Module (.BAS) Code:
  998. DefInt A-Z
  999. Declare Function GetFocus% Lib "USER" ()
  1000. Declare Function SendMessage& Lib "USER" (ByVal hWnd%, ByVal wMsg%
  1001.                                           ByVal wParm%, ByVal lParm&)
  1002. Const WM_USER = &H400                'Define the message number for
  1003. Const EM_LIMITTEXT = WM_USER + 21    '  limiting the length of an edit
  1004.                                      '  Control
  1005. Sub InitStdControl (Ctrl As Control)
  1006.    Ctrl.Width = 500                  'Make field some standard length
  1007.    Ctrl.SetFocus                     'Allow maximum of 14 characters
  1008.    Ok& = SendMessage(GetFocus(), EM_LIMITTEXT, 14, 0)
  1009.    .. more setup
  1010. End Sub
  1011.  
  1012. Sub StdEdit (Ctrl As Control, KeyAscii%)
  1013.    If KeyAscii >= 48 And KeyAscii <= 57 Then
  1014.       Ctrl.SelLength = 1             'This produces overstrike mode
  1015.    Else
  1016.       KeyAscii = 0                   'Ignore non-numeric keys
  1017.    End if
  1018. End Sub
  1019. ---------------
  1020.  
  1021.  
  1022.  
  1023. Data Entry Text Limit:
  1024.  
  1025. Declare Function GetFocus% Lib "USER" ()
  1026. Declare Function SendMessage& Lib "USER" (ByVal hWnd%, ByVal wMsg%,
  1027.                                           ByVal wParm%, Byval lParm&)
  1028. Const WM_USER = &H400
  1029. Const EM_LIMITTEXT = WM_USER + 21
  1030.  
  1031. In your Form_Load procedure:
  1032. MaxLen% = Whatever
  1033. Text1.SetFocus
  1034. Ok& = SendMessage(GetFocus(), EM_LIMITTEXT, MaxLen%, 0)
  1035. ---------------
  1036.  
  1037.  
  1038.  
  1039. Text Box - Flicker:
  1040.  
  1041. Concatenating text in a Text Box can cause screen flicker. Instead use
  1042. SelStart and SelLength - no flicker.
  1043. ---------------
  1044.  
  1045.  
  1046. Flashing Text:
  1047.  
  1048.   Sub FlashColor ()
  1049.      If FlashState = True Then
  1050.          Label1.BackColor = RGB(255, 0, 0)
  1051.          Label1.ForeColor = RGB(255, 255, 255)
  1052.      Else
  1053.          Label1.BackColor = RGB(255, 255, 255)
  1054.          Label1.ForeColor = RGB(0, 0, 0)
  1055.      End If
  1056.      If FlashState = True Then
  1057.          FlashState = False
  1058.      Else
  1059.          FlashState = True
  1060.      End If
  1061.  End Sub
  1062. --------
  1063.  
  1064.  
  1065. Flashing Text:
  1066.  
  1067. You can use the .SelLength and .SelStart to highlight and unhighlight a
  1068. section of the text.
  1069. ---------------
  1070.  
  1071.  
  1072. Timer:
  1073.  
  1074. If you use a Timer with a small interval (eg: 1) and another application is
  1075. active, Windows may not be able to respond at the specified duration and
  1076. execution of the related code will be postponed until Windows can get to it.
  1077. ---------------
  1078.