Link to home
Start Free TrialLog in
Avatar of haisai54
haisai54

asked on

How to remove the "X" button on Message Box in Visual Basic..?

Hi,

Can any one help me in how to remove the "X" that appears on the top right hand cornor of a message box.

I want to insisit user to click on "OK" or "Cancel" button which appears in the message box. But , I want to remove the "X" from the message box, there by removing the ability to click on "X" to close the message box.


Thanks in Advance.

Avatar of Baradin
Baradin

The one way I know of is as follows:

Msbox("Prompt", vbOKOnly, "Title")

And there you go!!
I tried Baradin's method, and it didn't work in VB6.
I don't believe that can be done.

However, you can fake it to near perfection.

Create your own message box, by adding a form to your project.
then set the ControlBox to false on the forms. This will remove the X and the control box on the top left of the form.

Nee More? Let me know.
HTH John
I use VB 6.0 sp 3 and it works just fine, IF (grin) you delete the extra spaces I put in there after and before the commas....
Baradin,
Under WinXP I still get the red X box in the title bar.
Reading the question, I believe that is what he wants to remove.

Do you have the red eXit box there?
John
Avatar of haisai54

ASKER

Hi

I tried BARADIN 's method but it didn't worked. I  need to remove the red "X" box on the top right hand corner of the message box.


John:
I have so many cases in the application where user has to accept or cancel the the given option. Not all the message are of same length. It differes from case to case. So is there any other way besides using the form.

Thanks,
Here is code to create a custom MsgBox, it uses the MessageBOX API but it sub classes it for a lot more options on the Message Box.




''ADD THIS CODE TO A MODULE
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright ©1996-2003 VBnet, Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce
'               or publish this code on any web site,
'               online service, or distribute as source
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'needed public for the Timer event
Public hwndMsgBox As Long

'custom user-defined type to pass
'info between procedures - easier than
'passing a long list of variables.
'Needed public for the Timer event
Public Type CUSTOM_MSG_PARAMS
   hOwnerThread         As Long
   hOwnerWindow         As Long
   dwStyle              As Long
   bUseTimer            As Boolean
   dwTimerDuration      As Long
   dwTimerInterval      As Long
   dwTimerExpireButton  As Long
   dwTimerCountDown     As Long
   dwTimerID            As Long
   sTitle               As String
   sPrompt              As String
End Type

Public cmp As CUSTOM_MSG_PARAMS

'Windows-defined uType parameters
Public Const MB_ICONINFORMATION As Long = &H40&
Private Const MB_ABORTRETRYIGNORE As Long = &H2&
Private Const MB_TASKMODAL As Long = &H2000&

'a const we define to identify our timer
Private Const MBTIMERID = 999

'Windows-defined MessageBox return values
Private Const IDOK As Long = 1
Private Const IDCANCEL As Long = 2
Private Const IDABORT As Long = 3
Private Const IDRETRY As Long = 4
Private Const IDIGNORE As Long = 5
Private Const IDYES As Long = 6
Private Const IDNO As Long = 7

'This section contains user-defined constants
'to represent the buttons/actions we are
'creating, based on the existing MessageBox
'constants. Doing this makes the code in
'the calling procedures more readable, since
'the messages match the buttons we're creating.
Public Const MB_SELECTBEGINSKIP As Long = MB_ABORTRETRYIGNORE
Public Const IDSELECT As Long = IDABORT
Public Const IDBEGIN As Long = IDRETRY
Public Const IDSKIP As Long = IDIGNORE
Public Const IDPROMPT As Long = &HFFFF&

'misc API constants
Private Const WH_CBT = 5
Private Const GWL_HINSTANCE As Long = (-6)
Private Const HCBT_ACTIVATE As Long = 5
Public Const WM_LBUTTONDOWN As Long = &H201
Public Const WM_LBUTTONUP As Long = &H202
Public Const WM_TIMER As Long = &H113

'UDT for passing data through the hook
Private Type MSGBOX_HOOK_PARAMS
   hwndOwner   As Long
   hHook       As Long
End Type

'need this declared at module level as
'it is used in the call and the hook proc
Private MHP As MSGBOX_HOOK_PARAMS

Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long

Public Declare Function GetDesktopWindow Lib "user32" () As Long

Private Declare Function GetWindowLong Lib "user32" _
   Alias "GetWindowLongA" _
  (ByVal hwnd As Long, _
   ByVal nIndex As Long) As Long

Public Declare Function GetDlgItem Lib "user32" _
  (ByVal hDlg As Long, _
   ByVal nIDDlgItem As Long) As Long
   
Private Declare Function MessageBox Lib "user32" _
   Alias "MessageBoxA" _
  (ByVal hwnd As Long, _
   ByVal lpText As String, _
   ByVal lpCaption As String, _
   ByVal wType As Long) As Long
   
Public Declare Function PostMessage Lib "user32" _
   Alias "PostMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, lParam As Long) As Long
     
Public Declare Function PutFocus Lib "user32" _
   Alias "SetFocus" _
  (ByVal hwnd As Long) As Long
 
Public Declare Function SetDlgItemText Lib "user32" _
   Alias "SetDlgItemTextA" _
  (ByVal hDlg As Long, _
   ByVal nIDDlgItem As Long, _
   ByVal lpString As String) As Long
     
Private Declare Function SetWindowsHookEx Lib "user32" _
   Alias "SetWindowsHookExA" _
  (ByVal idHook As Long, _
   ByVal lpfn As Long, _
   ByVal hmod As Long, _
   ByVal dwThreadId As Long) As Long
     
Private Declare Function SetWindowText Lib "user32" _
   Alias "SetWindowTextA" _
  (ByVal hwnd As Long, _
   ByVal lpString As String) As Long

Private Declare Function UnhookWindowsHookEx Lib "user32" _
   (ByVal hHook As Long) As Long
   
Private Declare Function SetTimer Lib "user32" _
  (ByVal hwnd As Long, _
   ByVal nIDEvent As Long, _
   ByVal uElapse As Long, _
   ByVal lpTimerFunc As Long) As Long
   
Private Declare Function KillTimer Lib "user32" _
  (ByVal hwnd As Long, _
   ByVal nIDEvent As Long) As Long
   

Public Function MsgBoxHookProc(ByVal uMsg As Long, _
                               ByVal wParam As Long, _
                               ByVal lParam As Long) As Long
     
  'When the message box is about to be shown
  'change the button captions
   If uMsg = HCBT_ACTIVATE Then
   
     'in a HCBT_ACTIVATE message, wParam holds
     'the handle to the messagebox - save that
     'for the timer event
      hwndMsgBox = wParam
             
     'the ID's of the buttons on the message box
     'correspond exactly to the values they return,
     'so the same values can be used to identify
     'specific buttons in a SetDlgItemText call.
      SetDlgItemText wParam, IDSELECT, "Select.."
      SetDlgItemText wParam, IDBEGIN, "Begin"
      SetDlgItemText wParam, IDSKIP, "Skip"
     
     'we're done with the dialog, so release the hook
      UnhookWindowsHookEx MHP.hHook
               
   End If
   
  'return False to let normal processing continue
   MsgBoxHookProc = False

End Function


Public Function TimedMessageBoxH(cmp As CUSTOM_MSG_PARAMS) As Long

   Dim hInstance As Long
   Dim hThreadId As Long
   
  'Set up the hook
   hInstance = GetWindowLong(cmp.hOwnerThread, GWL_HINSTANCE)
   hThreadId = GetCurrentThreadId()

  'set up the MSGBOX_HOOK_PARAMS values
  'By specifying a Windows hook as one
  'of the params, we can intercept messages
  'sent by Windows and thereby manipulate
  'the dialog
   With MHP
      .hwndOwner = cmp.hOwnerWindow
      .hHook = SetWindowsHookEx(WH_CBT, _
                                AddressOf MsgBoxHookProc, _
                                hInstance, hThreadId)
   End With
   
  '(re) set the countdown (or rather 'count-up') value to 0
   cmp.dwTimerCountDown = 0
   
  'if bUseTimer, enable the timer. Because the
  'MessageBox API acts just as the MsgBox function
  'does (that is, creates a modal dialog), control
  'won't return to the next line until the dialog
  'is closed. This necessitates our starting the
  'timer before making the call.
  '
  'However, timer events will execute once the
  'modal dialog is shown, allowing us to use the
  'timer to dynamically modify the on-screen message!
  '
  'The handle passed to SetTimer is the form hwnd.
  'The event ID is set to the const we defined.
  'The interval is 1000 milliseconds, and the
  'callback is TimerProc
   If cmp.bUseTimer Then
      cmp.dwTimerID = SetTimer(cmp.hOwnerThread, _
                               MBTIMERID, _
                               1000, _
                               AddressOf TimerProc)
   End If

  'call the MessageBox API and return the
  'value as the result of the function.
  '
  'Replace original '%T' variable in the
  'original prompt with starting duration.
   TimedMessageBoxH = MessageBox(cmp.hOwnerWindow, _
                                 Replace$(cmp.sPrompt, "%T", CStr(cmp.dwTimerDuration)), _
                                 cmp.sTitle, _
                                 cmp.dwStyle)

  'in case the timer event didn't
  'suspend the timer, do it now
   If cmp.bUseTimer Then
      Call KillTimer(cmp.hOwnerThread, MBTIMERID)
   End If
   
End Function


Public Function TimerProc(ByVal hwnd As Long, _
                          ByVal uMsg As Long, _
                          ByVal idEvent As Long, _
                          ByVal dwTime As Long) As Long


   Dim hWndTargetBtn As Long
   Dim sUpdatedPrompt As String

  'watch for the WM_TIMER message
   Select Case uMsg
      Case WM_TIMER
           
        'compare to our event ID of '999'
         If idEvent = MBTIMERID Then
           
           'assure that there is messagebox to update
            If hwndMsgBox <> 0 Then
           
              'increment the counter
              'and update the caption string
              'with the new time
              'Note: VB5 users see comments below
               cmp.dwTimerCountDown = cmp.dwTimerCountDown + 1
               sUpdatedPrompt = Replace$(cmp.sPrompt, "%T", CStr(cmp.dwTimerDuration - cmp.dwTimerCountDown))
         
              'update the prompt message with the countdown value
               SetDlgItemText hwndMsgBox, IDPROMPT, sUpdatedPrompt
         
              'if the timer has 'expired' (the
              'count=duration), we need to
              'programmatically 'press' the button
              'specified as the default on timeout
               If cmp.dwTimerCountDown = cmp.dwTimerDuration Then
               
                 'nothing more to do, so
                 'we can kill this timer
                  Call KillTimer(cmp.hOwnerThread, MBTIMERID)
                 
                 'now obtain the handle to the
                 'button designated as default
                 'if the timer expires
                  hWndTargetBtn = GetDlgItem(hwndMsgBox, cmp.dwTimerExpireButton)
                 
                  If hWndTargetBtn <> 0 Then
                 
                    'set the focus to the target button and
                    'simulate a click to close the dialog and
                    'return the correct value
                     Call PutFocus(hWndTargetBtn)
                   
                    'need a DoEvents to allow PutFocus
                    'to actually put focus
                     DoEvents
                     
                    'pretend a rodent pushed the button
                     Call PostMessage(hWndTargetBtn, WM_LBUTTONDOWN, 0, ByVal 0&)
                     Call PostMessage(hWndTargetBtn, WM_LBUTTONUP, 0, ByVal 0&)
                 
                  End If  'If hWndTargetBtn
               End If  'If cmp.dwTimerCountDown
            End If  'If hwndMsgBox
         End If  'If idEvent
      Case Else
   End Select
   
End Function
'--end block--'
 



'Put a command button (Command1) and a text box (Text1) on a form named (Form1). And add the following code to the form.


Option Explicit
Private Sub Command1_Click()
 
  'Display wrapper message box,
  'passing the CUSTOM_MSG_PARAMS
  'struct as the parameter.
   With cmp
      .sTitle = "VBnet API-Timed MessageBox Hook Demo"
      .dwStyle = MB_SELECTBEGINSKIP Or MB_ICONINFORMATION
      .bUseTimer = True               'True = update once per dwTimerInterval
      .dwTimerDuration = 10           'time to wait seconds
      .dwTimerInterval = 1000         'countdown interval in milliseconds
      .dwTimerExpireButton = IDBEGIN  'message to return if timeout occurs
      .dwTimerCountDown = 0           '(re)set to 0
      .hOwnerThread = Me.hwnd         'handle of form owning the thread on which
                                      'execution is proceeding.
                                      'The thread owner is always the calling form.
      .hOwnerWindow = Me.hwnd         'who owns the dialog (me.hwnd or desktop).
                                      'GetDesktopWindow allows user-interaction
                                      'with the form while the dialog is displayed.
                                      'This may not be desirable, so set accordingly.
      'to enable the countdown TimerProc routine
      'to update the message box, place a %T variable
      'inside the message string.
      'Note: VB5 users see comments below.
     
      .sPrompt = "To start searching C: immediately, select Begin." & vbCrLf & _
                 "To select a different drive, press Select." & vbCrLf & vbCrLf & _
                 "Automatic searching of C: will begin in %T seconds." & Space$(12)
   End With

   Select Case TimedMessageBoxH(cmp)
      Case IDSELECT: Text1.Text = "Select button pressed before timeout"
      Case IDBEGIN:  Text1.Text = "Begin button pressed or message timed out"
      Case IDSKIP:   Text1.Text = "Skip button pressed before timeout"
   End Select
     
End Sub


Make a customized Form and by runtime make the Borderstyle = 0 (None) and if you need code to move the form then tell me
there's an easier way !!!
just do that :

MsgBox "Prompt", vbYesNo, "Title"

some ButtonStyles for MsgBox automatically remove the X-close-ability
vbYesNo is one of them.
put this in a module

----------------------------------
Private Type mousepos
    intMouseX As Integer
    intMouseY As Integer
End Type
Dim blndrag As Boolean
Dim mousepos As mousepos
Public Sub V_MouseDown(f As Form, Button As Integer, Shift As Integer, X As Single, Y As Single)
    blndrag = True
    mousepos.intMouseX = X
    mousepos.intMouseY = Y
End Sub
Public Sub V_MouseMove(f As Form, Button As Integer, Shift As Integer, X As Single, Y As Single)
    If blndrag = True Then
        f.Move f.Left + (X - mousepos.intMouseX), f.Top + (Y - mousepos.intMouseY)
    End If
End Sub

Public Sub V_MouseUp(f As Form, Button As Integer, Shift As Integer, X As Single, Y As Single)
    blndrag = False
End Sub
----------------------------------------
and in your form put:
----------------

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Call MoveForm.V_MouseDown(Frmpass, Button, Shift, X, Y)
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Call MoveForm.V_MouseMove(Frmpass, Button, Shift, X, Y)

End Sub

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Call MoveForm.V_MouseUp(Frmpass, Button, Shift, X, Y)

End Sub
Or...

If u dont want to hard code it u can find it in the properties panel down the right i think
haisai54
I think that if you used a multiline text box to hold your message, with wordwrap the message would look ok.
You could put a function to display the box in a module as BrianGEFF719 suggests.
(Although at first glance, that seems to be a lot of code for a simple form.)
The return from the function could tell you OK or Cancel if you need that input.
Good Luck and if you Need More, Let me Know.
John
Thanks to all those who tried to help me.

Hello "TheQustion"
I tried the code which u posted. But its not clear. You are calling a procedure "MoveForm.V_MouseDown" . I don't understand what to do there. Should I create a new procedure other than what u wrote for a module..?
what is the object "MoveForm". Is this a new form specifially for message box..?
Please guide me step by step.

Hello ennixo
I tried ur way of MsgBox "Prompt", vbYesNo, "Title"
I think that is ok. because it is disabling the "X".
I need for msgbox for just "Ok" also.
If that works for just only "OK button, that would be great for me. It will solve my problem.


Hello MattAllen:
I don't understand what u are trying to help me here. Is that some thing which you are following some other's suggestion here?. or what ....? Please make me understand better.

Hello JohnChapin:
I tried, putting the separate form for t he message box. As we cannot control the size of the box depending on the text lenght, it is looking awkward. I am sure, I cannot impress my clients.
I think BrianGEFF719 suggestion is too big for implimenting.




Haisa,
You can control the size of the text box by setting the text1.height and text1.width.
You can also control the size of the form the same way.
This can be done in the form load event, so the changes are not apparent to the user.
Also, if you set the textbox borderstyle to none and the background color the same as the form,
the textbox "disappears" and the text shows alone on the form.

BY the way, you can also us a label as the control.

If you  use a fixed size font, you can compute the required size of the control,
depending on the length of the string to be displayed.  Or you can find the longest string,
expand the control to fit that, and thus it will hold all the message strings.

Need More? Let me know.
HTH John
HTMJohn:

Your idea is good and it works. But, the problem is, I am having a custom control. (OCX).
There are several controls which are clubbed into one control. Our  application does not have any forms.
If I made a form as VBmodal  for the message box, it is not acting as vbmodal form. We can click any other buttons on the control which are underneth it.

As you know the VBmodal form will make all the rest of the forms disable until the user chooses one of the option given in the modal form.

One point to your suggestion: Multiline property is not available in a label.


I want to make the message box pop up in the center of the screen. Is it possible?

H,
Would it help to make the modal form part of the user_control?
OR
Perhaps you could return a message from the user_control and let the application
use the modal form to display the message.


reference centering:

Public sub Centerform (thisForm as form)
    thisForm.Top = (Screen.Height - thisForm.Height) / 2
    thisForm.Left = (Screen.Width - thisForm.Width) / 2

exit sub

Which is called by:

    Centerform Me


Need More? Let me know.
HTH John
i don't remember where i saw it but i found your solution....

all you have to do is to find the message box window's handle and to remove the menu with getsystemmenu and removemenu apis...

but i don't remember how to find the message box window's handle...
haisai54,

the simplest solution would be the following:
create a new form and create the buttons you want to show on the new form (Yes/No/Cancel.. Whatever). set the following properties of the form to False.

ClipControls = False
ControlBox = False

then show the form as a modal form.
form2.show vbModal, me

cheers,
srimanth.
haisai54,

alternatively, to make sure that all the close, max and min buttons are removed from the form, use the following windows API which removes what you want. But you'd have to create a form and create your own buttons:

'*********************************************************************
Option Explicit

Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long, ByVal bRevert As Long) As Long
Private Declare Function DeleteMenu Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Integer) As Long

Private Const SC_SIZE = &HF000
Private Const SC_MOVE = &HF010
Private Const SC_MINIMIZE = &HF020
Private Const SC_MAXIMIZE = &HF030
Private Const SC_CLOSE = &HF060
Private Const SC_RESTORE = &HF120

Private Sub Form_Load()
    RemoveUserInterface
End Sub

Private Function RemoveUserInterface()
    Dim lMenuHandle As Long
   
    'Get the menu handle of the form
    lMenuHandle = GetSystemMenu(frmMain.hwnd, False)
   
    DeleteMenu lMenuHandle, CInt(SC_SIZE), CInt(MF_BYCOMMAND)       'Stop user from resizing the form
    DeleteMenu lMenuHandle, CInt(SC_MOVE), CInt(MF_BYCOMMAND)       'Stop user from moving the form
    DeleteMenu lMenuHandle, CInt(SC_MAXIMIZE), CInt(MF_BYCOMMAND)   'Stop user from maximizing the form
    DeleteMenu lMenuHandle, CInt(SC_MINIMIZE), CInt(MF_BYCOMMAND)   'Stop user from minimizing the form
    DeleteMenu lMenuHandle, CInt(SC_RESTORE), CInt(MF_BYCOMMAND)    'Stop user from restoring a minimized form
    DeleteMenu lMenuHandle, CInt(SC_CLOSE), CInt(MF_BYCOMMAND)      'Stop user from closing the form
End Function
Create two buttons on the new form which you want to show as message box and add the following code:

Private Sub cmdCancel_Click()
    nReturnValue = vbCancel
End Sub

Private Sub cmdOK_Click()
    nReturnValue = vbOK
End Sub

Store nReturnValue in a module as a public variable.
Public nReturnValue as Integer

From your calling form, just call
frmMessageBox.Show vbModal, Me

I've posted two solutions above. Hope atleast one of em helps you out. If you have any problems, just shout and i'd gladly help.

ps:
I forgot to include the following line in my second solution. Add the following line also:
Private Const MF_BYCOMMAND = &H0&

cheers,
srimanth.
haisai54,

have u tried any of the solutions i have provided?

cheers,
srimanth.
Hi Srimanth,

I am sorry, I couldn't check my mails and login to the experts-exchange yesterday. I have taken off. Sure I will try.  Unfortunately I am diverted to some other work area as this message box thing has become low priority now. Thank you for the prompt help and checking back with me.

I will post my comment once I tried it.

Thank you all.

Haisai54
haisai54,
award/split points if someone has helped you. if u feel your question is still unanswered, please close it.

http://oldlook.experts-exchange.com/help/closing.jsp

cheers,
srimanth.
Recommendation: Accept srimanth's comments as answer.
ASKER CERTIFIED SOLUTION
Avatar of PashaMod
PashaMod

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