Link to home
Start Free TrialLog in
Avatar of GeneM
GeneM

asked on

Need a Customized InputBox

Hello all,
I have an application that does quite a bit of dialog with the user.  I have used the Input Box control to get input from him/her.  However, the InputBox seems kind of crude for what I am doing.  

It would seem fairly simple to substitute a form for the input box.  The thing I would really like is for the text box (where the user enters the data) to resize based on the length of the data I expect the user to enter.  For example, I might have input that is only 8 characters long.  I would like the input form to only allow 8 characters in that case.

Note what I would like here is a "general purpose" answer. I realize I could code a new form for each of my input requirements, but I do not want to do that.  I want to pass to the input form the length of the input and have the form resize the text box (and resize itself) to the correct length.

Does anyone have something like this, or know where I could download it.  Or, alternatively, is there an API to adjust some of these fields in the Inputbox control?

Avatar of Nitin Sontakke
Nitin Sontakke
Flag of India image

If you don't mind coding yourself, as i think, if you are ready for the new form, it is a very simple thing to do.

Define a new public property (or just a public variable, to be less than perfect!) and you are done.

'In declaration section of form.

Public intMaxLength as Integer.

'In form_Load()
If intMaxLength = 0 Then
    txtInput.MaxLength = 10 'A default value. Change as per your requirement!
Else
    txtInput.MaxLength = intMaxLength
End If

'While calling.

Dim objForm as New inputForm
objForm.intMaxLength = 8 'As the case may be.
objForm.Show

I haven't tested the code, but think it is okay.

You can set the MaxLength of input box or using Microsoft MaskedEdit control come with VB.  You can find it in Project->Components

Good Luck ^_^
If I understand your question, you want to resize the text box.  Try this:

Text5.Width = TextWidth("12345678") + 150

Where the string in quotes is really the length of the string you want the user to enter.

Good luck,

Frank
Hi

'========Bas module code=======
Option Explicit
Type INPUT_BOX_EX
    lWidth As Long
    sPasswordChar As String
    iMaxLength As Long
    sOKText As String
    sCancelText As String
    sHelpText As String
End Type

Type POINTAPI
     x As Long
     y As Long
End Type

Type RECT
     Left As Long
     Top As Long
     right As Long
     bottom As Long
End Type
Private Declare Function GetWindowRect& Lib "user32" (ByVal hWnd As Long, lpRect As RECT)
Private Declare Function ScreenToClient Lib "user32" (ByVal hWnd As Long, lpPoint As POINTAPI) As Long

Private Declare Function MoveWindow& Lib "user32" (ByVal hWnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long)
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function SetTimer& Lib "user32" (ByVal hWnd&, ByVal nIDEvent&, ByVal uElapse&, ByVal lpTimerFunc&)
Private Declare Function KillTimer& Lib "user32" (ByVal hWnd&, ByVal nIDEvent&)

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function GetDlgItem Lib "user32" (ByVal hDlg As Long, ByVal nIDDlgItem As Long) As Long
Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String) As Long
Private Const EM_SETPASSWORDCHAR = &HCC
Private Const EM_LIMITTEXT = &HC5

Const ID_OK = &H1        'OK button
Const ID_CANCEL = &H2    'Cancel Button
Const ID_TEXT = &H1324   'Input TextBox
Const ID_PROMPT = &H1325 'Prompt label
Const ID_HELP = &H1326   'Help Button

Const NV_MSG As Long = &H5000&
Dim hMsgBox As Long
Dim sTitle As String
Dim IB As INPUT_BOX_EX

Sub TimerProc(ByVal hWnd&, ByVal uMsg&, ByVal idEvent&, ByVal dwTime&)
    If sTitle = "" Then sTitle = App.Title
    hMsgBox = FindWindow("#32770", sTitle)
    If hMsgBox = 0 Then Exit Sub
    KillTimer hWnd, idEvent
    ModifyIB
End Sub

Public Function CustomInputBox(hOwner As Long, Prompt, _
                               Optional Title, Optional Default, _
                               Optional xPos, Optional yPos, _
                               Optional HelpFile, Optional Context, _
                               Optional lWidth As Long, _
                               Optional sPasswordChar As String, _
                               Optional iMaxLength As Integer, _
                               Optional sOKText As String, _
                               Optional sCancelText As String, _
                               Optional sHelpText As String) As String
   If IsMissing(Title) Then sTitle = App.Title Else sTitle = Title
   IB.iMaxLength = iMaxLength
   IB.lWidth = lWidth
   IB.sPasswordChar = sPasswordChar
   IB.sOKText = sOKText
   IB.sCancelText = sCancelText
   IB.sHelpText = sHelpText
   SetTimer hOwner, NV_MSG, 10, AddressOf TimerProc
   CustomInputBox = InputBox(Prompt, sTitle, Default, xPos, yPos, HelpFile, Context)
End Function

Private Sub ModifyIB()
   Dim hEdit As Long, hPrompt As Long
   Dim hOK As Long, hCancel As Long, hHelp As Long
   Dim dL As Long
   Dim rc As RECT, rcEdit As RECT
   hOK = GetDlgItem(hMsgBox, ID_OK)
   hCancel = GetDlgItem(hMsgBox, ID_CANCEL)
   hHelp = GetDlgItem(hMsgBox, ID_HELP)
   hEdit = GetDlgItem(hMsgBox, ID_TEXT)
   hPrompt = GetDlgItem(hMsgBox, ID_PROMPT)
   If IB.sPasswordChar <> "" Then Call SendMessage(hEdit, EM_SETPASSWORDCHAR, Asc(IB.sPasswordChar), 0)
   Call SendMessage(hEdit, EM_LIMITTEXT, IB.iMaxLength, ByVal 0&)
   Call SetItemText(hOK, IB.sOKText)
   Call SetItemText(hCancel, IB.sCancelText)
   Call SetItemText(hHelp, IB.sHelpText)
   If IB.lWidth Then
      GetWindowRect hMsgBox, rc
      dL = rc.right - rc.Left - IB.lWidth
      MoveWindow hMsgBox, rc.Left, rc.Top, IB.lWidth, rc.bottom - rc.Top, True
      ChangeWidth hEdit, dL
      ChangeWidth hPrompt, dL
      ShiftLeft hOK, dL
      ShiftLeft hCancel, dL
      ShiftLeft hHelp, dL
   End If
End Sub

Private Sub SetItemText(ByVal h As Long, ByVal s As String)
   If s = "" Then Exit Sub
   SetWindowText h, s
End Sub

Private Sub ShiftLeft(ByVal h As Long, ByVal dW As Long)
  Dim rc As RECT
  Dim pt As POINTAPI
  GetWindowRect h, rc
  pt.x = rc.Left
  pt.y = rc.Top
  ScreenToClient hMsgBox, pt
  MoveWindow h, pt.x - dW, pt.y, rc.right - rc.Left, rc.bottom - rc.Top, True
End Sub

Private Sub ChangeWidth(ByVal h As Long, ByVal dW As Long)
  Dim rc As RECT
  Dim pt As POINTAPI
  GetWindowRect h, rc
  pt.x = rc.Left
  pt.y = rc.Top
  ScreenToClient hMsgBox, pt
  MoveWindow h, pt.x, pt.y, rc.right - rc.Left - dW, rc.bottom - rc.Top, True
End Sub

'========Form code==========
Option Explicit

Private Sub Command1_Click()
  Dim ret As String
  Caption = CustomInputBox(Me.hWnd, "Enter Password", "Custom IB demo", , , , , , 300, "*", 8, "Get &It", "&Forget it!", "&Need help?")
End Sub

'Note - since you have control on InputBox, you also can change color,textsize/weight etc for all controls

Regards
Ark
Avatar of GeneM
GeneM

ASKER

Hello to all responders,

Thanks for all for the responses.  Sorry for the delay in getting back to you.

I have a couple of questions for Ark & Nitin.

First, for Ark.  That is some pretty heavy duty code for a country boy like me!  As you infer, I suspect I could do a lot more with it if I knew how.  One of my original goals was to be able to adjust the length of the text box to about the length of the string I expected the user to enter.  I could use a fixed length font.  Is it possible to do that in your solution?  Also, can you show me how to set the font in the text box?  Also, would it be possible to pass the inputbox an array of possible entries, and have the inputbox validate the entries?

Nitin, could you expand a little bit on your suggestion?  For example, how would you propose to return the entered string?  I assume you would have to have a public variable somewhere to accept the string.  Would the form then have to unload itself?  Sorry, I am not real sharp on communications between forms.  

Again, thanks to all who have participated.

GeneM
ASKER CERTIFIED SOLUTION
Avatar of Nitin Sontakke
Nitin Sontakke
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I already have a customized input box that I think you might like but I can't figure out how to include a zip file in this comment.
Avatar of GeneM

ASKER

Martin,
You can't include a zip file. Why don't you just copy the code and paste it in here.  If it is a form, you could load the form into a text editor and then do the copy and paste.  That way you would be posting the source for the controls also.
GeneM
Create a form that looks exactly like an InputBox with a 2-member command button named cmdButton (the caption for index 0 is &OK and &Cancel for index 1) and a textbox. Note that you could easily modify this form to add a MaxLen property that would control the maximum length of the input.

Form code:
Option Explicit

' You have a royalty-free right to use, modify, reproduce and distribute
' the Sample Application Files (and/or any modified version) in any way
' you find useful, provided that you agree that Martin Liss has no warranty,
' obligations or liability for any Sample Application Files.

' To use this form, you can access its objects directly or through the form's
' properties. The following is an example of ther latter.
 
'    frmInputBox.PasswordProtect = False ' If this is set to True, it doesn't make sense to set the Default property
'    frmInputBox.Prompt = "Please enter a value below"
'    frmInputBox.Title = "This is the caption"
'    frmInputBox.Default = "Enter Value"
'    frmInputBox.Display
'
' This will detect the user's click of the cancel button

'    If frmInputBox.ButtonClicked = vbCancel Then
'        MsgBox "You cancelled the InputBox"
'    End If



Private m_intButtonClicked As Integer
Private m_bProtect As Boolean
Private m_strPrompt As String
Private m_strTitle As String
Private m_strDefault As String

' Change these if you modify the dimensions of this form
Private Const FORM_HEIGHT = 2175
Private Const INPUT_TOP = 1360
Private Const PROMPT_WIDTH = 4095
Private Const PROMPT_HEIGHT = 1215

Private Enum btnButtons
    btnOK
    btnCancel
End Enum



Private Sub cmdButton_Click(Index As Integer)

    Select Case Index
        Case btnOK
            ButtonClicked = vbOK
        Case btnCancel
            ButtonClicked = vbCancel
    End Select
   
    Me.Hide

End Sub

Private Sub Form_Activate()
   
    ' Reset the form and it's controls to their original size and positions
    ' if they have been changed (below) by a long prompt.
    Me.Height = FORM_HEIGHT
    lblPrompt.Height = PROMPT_HEIGHT
    txtInput.Top = INPUT_TOP
   
    If PasswordProtect Then
        txtInput.PasswordChar = "*"
    End If
   
    lblPrompt = Prompt
    Me.Caption = Title
    txtInput = Default

    ' Modify the form if a long prompt needs to be displayed.
    lblPrompt.Width = PROMPT_WIDTH
    If lblPrompt.Height > PROMPT_HEIGHT Then
        Me.Height = Me.Height + (lblPrompt.Height - PROMPT_HEIGHT)
        txtInput.Top = txtInput.Top + (lblPrompt.Height - PROMPT_HEIGHT)
    End If
   
    ' Highlight the text in txtInput
    txtInput.SelStart = 0
    txtInput.SelLength = Len(txtInput)
    txtInput.SetFocus

End Sub

Public Sub Display()

    Me.Show 1
   
End Sub

Public Property Get ButtonClicked() As Integer

    ButtonClicked = m_intButtonClicked

End Property

Public Property Let ButtonClicked(ByVal intButton As Integer)

    m_intButtonClicked = intButton

End Property

Public Property Get PasswordProtect() As Boolean

    PasswordProtect = m_bProtect

End Property

Public Property Let PasswordProtect(ByVal bProtect As Boolean)

    m_bProtect = bProtect
   
End Property

Public Property Get Prompt() As String

    Prompt = m_strPrompt

End Property

Public Property Let Prompt(ByVal strPrompt As String)

    m_strPrompt = strPrompt
   
End Property
Public Property Get Title() As String

    Title = m_strTitle

End Property

Public Property Let Title(ByVal strTitle As String)

    m_strTitle = strTitle
   
End Property
Public Property Get Default() As String

    Default = m_strDefault

End Property

Public Property Let Default(ByVal strDefault As String)

    m_strDefault = strDefault
   
End Property
Avatar of GeneM

ASKER

Hi Martin,
Thank you for posting the code.  I have a couple of questions.

Can you briefly explain the enum statement.  It looks like it is assigning a vaule to btnOK and btnCancel (0 and 1).  Could you have defined them as constants as easily?  Why was the enum statement invented?

Also, I would like the user to be able to "overtype" the prompt.  If I was to prompt something like '.0123456' I would like the user to place the cursor on the 3 to change it to a 4 such that it would then be '.0124456'.  Is there any easy way to do this??  If it is a lot of code, don't worry about it.

Thank you for your help.

GeneM



An enum is an enumerated constant. The enum doesn't assign a value to the buttons. I use the enum to give names to the indexes of the command buttons just like you would use two separate constants. So the code in cmdButton_click which appears as

    Select Case Index
        Case btnOK
            ButtonClicked = vbOK
        Case btnCancel
            ButtonClicked = vbCancel
    End Select


could be re-written as

    Select Case Index
        Case 0
            ButtonClicked = vbOK
        Case 1
            ButtonClicked = vbCancel
    End Select

but using the enum I don't have to remember what the index of (for example) the OK button. I just refer to cmdButton(btnOK).


One advantage of an enum is that like in the enum that I use

Private Enum btnButtons
    btnOK
    btnCancel
End Enum

the btnOK constant automatically is given the value of 0 and all the enums that follow it are automatically incremented by 1, so btnCancel is = 1. I could also do somthing like the follwoing and btnStillAnother would automatically be assigned the value 8.

Private Enum btnButtons
    btnOK = 4
    btnCancel
    btnOther
    btnYetAnother
    btnStllAnother
    btnAdNaseum
End Enum

The enum keeps the related constants together and makes their reordering simple if you need to do that.

As for your other question, if you want the "3" to be aotomatically selected, one way would be to change the code in frmInputBox to look like this

    ' Highlight the text in txtInput
    txtInput.SelStart = 4 '0
    txtInput.SelLength = 1 'Len(txtInput)
    txtInput.SetFocus


A more flexible way would be to add a SelectionStart and SelectionLength properties to the form similar to the other properties.
Oh, I get it. If you want the user to be able to overtype any part of the prompt just *remove* this code

   ' Highlight the text in txtInput
   txtInput.SelStart = 0
   txtInput.SelLength = Len(txtInput)
   txtInput.SetFocus
Avatar of GeneM

ASKER

Hi Martin,

Thanks for the tutorial on Enum.  Microsoft should hire you to write their online help!

The problem with the Overtype is that a text box is normally in the Insert mode instead of the Overtype mode.

For example, create a text box with a MaxLength of 4.  Then load it with ABCD.  Then try to change the D to an E.  You have to delete the D first either by backspacing or with the delete button.  I would like the user to be able to type right over the top of the D to replace it.

Most word processors and text editors support either mode.  In fact, the VB editor supports both modes (by hitting the Insert button on the keyboard, you can switch modes).  

I just could not find a way to switch modes in a text box.

Gene M
Fool around with this code that I posted above. As is it will hightlight the "D" (the 4th character) in ABCD and replace it with whatever the user types in. If you want to automatically select more than one character, change the SelLength. The original code selects the whole text string with Len(txtInput).


   ' Highlight the text in txtInput
   txtInput.SelStart = 4 '0
   txtInput.SelLength = 1 'Len(txtInput)
   txtInput.SetFocus

Avatar of GeneM

ASKER

Thanks to all who contributed to this thread.  All the contributions were great!  I went with the form instead of the modified Input Box as proposed by Ark.  I thought the form gave me more flexibility (maybe because I didn't understand enough of Ark's API code).

I gave the points to Nitin because he was the first responder with a working form.