?
Solved

BIZARE: ***MDI Maximize button disabled but not for long***

Posted on 2003-03-04
34
Medium Priority
?
704 Views
Last Modified: 2012-05-04
Hello,
I have an MDI with many MDI children, I can disable the maximize button and several other of the MDI's features using the following code:

Option Explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function DeleteMenu Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, _
ByVal wFlags As Long) As Long
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long

Const MF_BYPOSITION = &H400&
Const GWL_STYLE = (-16)
Const WS_THICKFRAME = &H40000
Const WS_MINIMIZEBOX = &H20000
Const WS_MAXIMIZEBOX = &H10000
Const WS_SYSMENU = &H80000

Private Sub RemoveMenus()
Dim hMenu As Long
' Get the form's system menu handle.
hMenu = GetSystemMenu(hWnd, False)
DeleteMenu hMenu, 6, MF_BYPOSITION 'Disables X Button
DeleteMenu hMenu, 5, MF_BYPOSITION 'Removes Form Seperator
DeleteMenu hMenu, 4, MF_BYPOSITION 'Disables Maximize Button
DeleteMenu hMenu, 2, MF_BYPOSITION 'Disables Resizing
' Grey out form's Maximize Button and use thin frame
Dim L As Long
L = GetWindowLong(Me.hWnd, GWL_STYLE)
L = L And Not (WS_MAXIMIZEBOX)
L = L And Not (WS_THICKFRAME)
L = SetWindowLong(Me.hWnd, GWL_STYLE, L)
End Sub

Private Sub MDIForm_Load()
RemoveMenus
formLogin.Show
End Sub

If I comment out the loading of the first form "formLogin" which is an MDIchild then the code works perfectly with maximize greyed out and a thin border on the MDI.

If formLogin.show is not commented out then the sysmenu changes remain i.e. they do not appear in the sysmenu, however the maximize button is not greyed and under windows XP is usable and the border of the MDI becomes thick. How do I fix this.

Thanks in Advance
r1gga
0
Comment
Question by:r1gga
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 17
  • 15
  • 2
34 Comments
 
LVL 5

Expert Comment

by:JohnMcCann
ID: 8067238
Just a though have you tried placing a refresh statment betwwen these two lines

RemoveMenus
formLogin.Show

Also does the MDI form draw properly after the formLogin has been displayed?

And finally does formLogin need to be an MDI child.
0
 
LVL 5

Expert Comment

by:JohnMcCann
ID: 8067247
Soory just noticed

but not for long

So it does work for a short period
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8071349
could try simply allowing the form to load and then using a timer to remove the frmmenus and then show frm login, that way the form gets drawn.

e.g.

Private Sub MDIForm_Load()
   Timer1.Interval = 100
   Timer1.enabled = true
End Sub

Private Sub Timer1_Timer()
   
   timer1.enabled = false

   RemoveMenus
   formLogin.Show
end sub
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:r1gga
ID: 8078582
Sorry I forgot to mention, I can achieve the desired effect through use of a timer, however I seek something more reliable as the timer can be beaten if the user clicks fast enough leaving the user with a maximized window that can then not be resized as the timer has occured. My timer code is:

Private Sub Timer1_Timer()
RemoveMenus
End Sub
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8078594
Can you load the MDIForm with its .Visible = false, then in the timer you can show it, that way the user can never beat your timer, as they cannot see the form.
0
 

Author Comment

by:r1gga
ID: 8078612
The thing is I am looking for a more gneric solution, as this method method may allow formLogin to be displayed but then after that when formMain is displayed the same problem occurs.
Thanks r1gga
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8078676
Hi okay, i think I figured it out, justt re-read your code

' Grey out form's Maximize Button and use thin frame
Dim L As Long
L = GetWindowLong(Me.hWnd, GWL_STYLE)
L = L And Not (WS_MAXIMIZEBOX)
L = L And Not (WS_THICKFRAME)
L = SetWindowLong(Me.hWnd, GWL_STYLE, L)

After calling SetWindowLong, you need to redraw the window, so add these lines:

swpFlags = SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER
SetWindowPos(Me.Hwnd,0,0,0,0,0,swpFlags)

Now you will notice that your remove menu function call will work properly.
0
 

Author Comment

by:r1gga
ID: 8078684
The thing is I am looking for a more gneric solution, as this method method may allow formLogin to be displayed but then after that when formMain is displayed the same problem occurs.
Thanks r1gga
0
 

Author Comment

by:r1gga
ID: 8078720
I get variable not defined SWP_Framechanged
Thanks
r1gga
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8078792
Private Const SWP_FRAMCHANGED = &H20
SWP_NOMOVE = &H2
SWP_NOSIZE = &H1
SWP_NOZORDER = &H4

And the SetWindowPos is defined in user32
0
 

Author Comment

by:r1gga
ID: 8079181
I get variable not defined SWP_Framechanged
Thanks
r1gga
0
 

Author Comment

by:r1gga
ID: 8079194
sorry I pressed refresh on an old window ignor ethe last post
0
 

Author Comment

by:r1gga
ID: 8079209
setwindowpos is not a valid function i.e. it appears in red.
Have u tried this out?
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8079366
err its an API  u have to declare it as :

Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
0
 

Author Comment

by:r1gga
ID: 8082788
The only effect your code has is to refresh the image of the maximize button, which is useful but only in conjunction with a timer that executes the function removemenus(). Here is the code as it stands with your modification:

Option Explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function DeleteMenu Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, _
ByVal wFlags As Long) As Long
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

Const MF_BYPOSITION = &H400&
Const GWL_STYLE = (-16)
Const WS_THICKFRAME = &H40000
Const WS_MINIMIZEBOX = &H20000
Const WS_MAXIMIZEBOX = &H10000
Const WS_SYSMENU = &H80000
Const SWP_FRAMECHANGED = &H20
Const SWP_NOMOVE = &H2
Const SWP_NOSIZE = &H1
Const SWP_NOZORDER = &H4

Private Sub RemoveMenus()
Dim hMenu As Long
' Get the form's system menu handle.
hMenu = GetSystemMenu(hWnd, False)
DeleteMenu hMenu, 6, MF_BYPOSITION 'Disables X Button
DeleteMenu hMenu, 5, MF_BYPOSITION 'Removes Form Seperator
DeleteMenu hMenu, 4, MF_BYPOSITION 'Disables Maximize Button
DeleteMenu hMenu, 2, MF_BYPOSITION 'Disables Resizing
' Grey out form's Maximize Button and use thin frame
Dim L As Long
L = GetWindowLong(Me.hWnd, GWL_STYLE)
L = L And Not (WS_MAXIMIZEBOX)
L = L And Not (WS_THICKFRAME)
L = SetWindowLong(Me.hWnd, GWL_STYLE, L)

L = SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER
L = SetWindowPos(Me.hWnd, 0, 0, 0, 0, 0, L)
End Sub

Private Sub MDIForm_Load()

formLogin.Show
RemoveMenus

End Sub

If you create a mockup mdi which has an intial mdi child that is replaced post-loading by on screen button clicks then you will better be able to visualise the problem. As the code stands the maximize button is ok initially but as soon as another form is loaded it becomes usable again.

Thanks for your help
r1gga
0
 

Author Comment

by:r1gga
ID: 8082841
So a simplification of my problem is that I need the changes to the MDI window to remain when MDI children are loaded
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8083324
Hi, what I suggest you do is rather than use the DeleteMenu api is use the following code to handle your management of the form buttons. For a demo, simply create a new project add a form1 and MDIForm. Now in the MDI form paste the following code:

Option Explicit

Private Const SWP_HIDEWINDOW = &H80
Private Const SWP_NOACTIVATE = &H10
Private Const SWP_NOCOPYBITS = &H100
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOOWNERZORDER = &H200      '  Don't do owner Z ordering
Private Const SWP_NOREDRAW = &H8
Private Const SWP_NOREPOSITION = SWP_NOOWNERZORDER
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOZORDER = &H4
Private Const SWP_SHOWWINDOW = &H40
Private Const SWP_NOSENDCHANGING = &H400
Private Const SWP_FRAMECHANGED = &H20
Private swpFlags As Long
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2
Private Const HWND_BOTTOM = 1
Private Const HWND_TOP = 0
Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

Private Const GWL_HWNDPARENT = -8
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long

' Used to get window style bits.
Private Const GWL_STYLE = (-16)
Private Const GWL_EXSTYLE = (-20)

' Style bits.
Public Enum WindowStyleBits
    WS_MAXIMIZEBOX = &H10000
    WS_MINIMIZEBOX = &H20000
    WS_THICKFRAME = &H40000
    WS_SYSMENU = &H80000
    WS_CAPTION = &HC00000
End Enum

Public Sub FormAPIs_ChangeWindowAppearance(hWnd As Long, WindowAppearanceProperty As WindowStyleBits, IsVisible As Boolean)

10      Dim lStyle As Long
   
            ' Retrieve current style bits.
20          lStyle = GetWindowLong(hWnd, GWL_STYLE)

            ' Set requested bit On or Off and Redraw.
60          If IsVisible = True Then
70              lStyle = lStyle Or WindowAppearanceProperty
80          Else
90              lStyle = lStyle And Not WindowAppearanceProperty
100         End If
            Call SetWindowLong(hWnd, GWL_STYLE, lStyle)
   
            ' Redraw window with new style.
140         swpFlags = SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOZORDER Or SWP_NOSIZE
            Call SetWindowPos(hWnd, 0, 0, 0, 0, 0, swpFlags)


End Sub

Private Sub MDIForm_Load()

    Call FormAPIs_ChangeWindowAppearance(Me.hWnd, WS_MAXIMIZEBOX, False)
   
End Sub

Private Sub mnuNewForm_Click()
    Form1.Show
End Sub

You can ignore the line numbers, the code is cut from another project that uses line number error handling. Okay after you paste the code, use the Menu Editor and create a menu option called mnuNewForm. So when you click it it will launch the Form1. You will notice that the MDIForm does not change the maximise enabled.

The only way that the Maximise button would get re-enabled, is if somewhere in your code, it is redrawing the menu from another API call you are using. I have been using this method to block form button user menus for ages and never had the problem you are describing.

You can also use the FormAPIs_ChangeWindowAppearance function to change the style of the other buttons. Give it a try and let me know.

0
 
LVL 5

Expert Comment

by:RainUK
ID: 8083357
Also just read in MSDN that you have to use DrawMenuBar API after calling DeleteMenu, to see the changes, so I am assuming that when you call SetWindowLong, it is redrawing the window, but not permanently setting the buttons. So when you load the other form, the MDI is being told to refresh, and it just goes back to the original menu setup as you never called DrawMenuBar?

Well thats just a guess.
0
 

Author Comment

by:r1gga
ID: 8083455
You're a great help rainUK, I have discovered why the problem I am experiencing is occuring. On every single one of my MDI Child forms I have the following code:

Private Sub Form_Load()
    ' Set Window Caption
    mdiMain.Caption = Me.Caption
End Sub

And this is what is refreshing the window. So my code should have worked from the outset if only I had omitted the reference to the MDI's caption. This said how do I go about changing the MDI's caption without experiencing the same problem?

Thanks r1gga
0
 

Author Comment

by:r1gga
ID: 8083539
Sorry just read your post on MSDN, so could you please give me the code to define the API Draw Menubar and also call it. This should enable me to alter the caption without re-enabling the maximize button right?
Thanks
r1gga
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8086559
Hi R1gga,

Just read MSDN again and I am sure that you not calling DrawMenuBar is not persisting the changes, so here is a proper example of using the APIs. So after you call DrawMenuBar you should not need to change any other code, as the change in caption should not affect the new window style.


Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long, ByVal bRevert As Long) As Long
Private Declare Function GetMenuItemCount Lib "user32" (ByVal hMenu As Long) As Long
Private Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function RemoveMenu Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Const MF_BYPOSITION = &H400&
Const MF_REMOVE = &H1000&
Private Sub Form_Load()
    Dim hSysMenu As Long, nCnt As Long
    ' Get handle to our form's system menu
    ' (Restore, Maximize, Move, close etc.)
    hSysMenu = GetSystemMenu(Me.hwnd, False)

    If hSysMenu Then
        ' Get System menu's menu count
        nCnt = GetMenuItemCount(hSysMenu)
        If nCnt Then
            ' Menu count is based on 0 (0, 1, 2, 3...)
            RemoveMenu hSysMenu, nCnt - 1, MF_BYPOSITION Or MF_REMOVE
            RemoveMenu hSysMenu, nCnt - 2, MF_BYPOSITION Or MF_REMOVE ' Remove the seperator
            DrawMenuBar Me.hwnd
            ' Force caption bar's refresh. Disabling X button
            Me.Caption = "Try to close me!"
        End If
    End If
End Sub
0
 

Author Comment

by:r1gga
ID: 8087230
Okay I have implemented draw menu bar as follows but the problem is still there, it only occurs when the mdiMain.Caption command is used, without this command it is okay:

Option Explicit
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long
Private Declare Function DrawMenuBar Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function RemoveMenu Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Const MF_BYPOSITION = &H400&
Const MF_REMOVE = &H1000&
Const GWL_STYLE = (-16)
Const WS_THICKFRAME = &H40000
Const WS_MAXIMIZEBOX = &H10000

Private Sub RemoveMenus()
    Dim hSysMenu As Long
    ' Get handle to our form's system menu
    ' (Restore, Maximize, Move, close etc.)
    hSysMenu = GetSystemMenu(Me.hWnd, False)
    ' Menu count is based on 0 (0, 1, 2, 3...)
    RemoveMenu hSysMenu, 6, MF_BYPOSITION Or MF_REMOVE ' Removes Close
    RemoveMenu hSysMenu, 5, MF_BYPOSITION Or MF_REMOVE ' Remove the seperator
    RemoveMenu hSysMenu, 4, MF_BYPOSITION Or MF_REMOVE ' Removes Maximize
    RemoveMenu hSysMenu, 2, MF_BYPOSITION Or MF_REMOVE ' Removes Resize
   
    ' Grey out form's Maximize Button and use thin frame
    Dim L As Long
    L = GetWindowLong(Me.hWnd, GWL_STYLE)
    L = L And Not (WS_MAXIMIZEBOX)
    L = L And Not (WS_THICKFRAME)
    L = SetWindowLong(Me.hWnd, GWL_STYLE, L)
   
    ' Force caption bar's refresh. Disabling X button
    DrawMenuBar Me.hWnd
    Me.Caption = "Try to close me!"
End Sub

Private Sub MDIForm_Load()
RemoveMenus
End Sub

If you have access to VB could u try this out?
0
 

Author Comment

by:r1gga
ID: 8087267
so in short calling:
Me.Caption = "Try to Close me!"
Undoes the greying out of the maximise button (Not the close button) and also undoes the thick border.

None of the SysMenu changes are lost so with or without drawmenubar follow the removemenu API, the changes that are lost are from the following code:
   Dim L As Long
   L = GetWindowLong(Me.hWnd, GWL_STYLE)
   L = L And Not (WS_MAXIMIZEBOX)
   L = L And Not (WS_THICKFRAME)
   L = SetWindowLong(Me.hWnd, GWL_STYLE, L)
Any clues?
Thanks r1gga
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8087284
give me 30 mins, I'll just try your code
0
 

Author Comment

by:r1gga
ID: 8087300
MSDN says:
"IF using SetWindowLong then SetWindPos must be used after this"
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8087311
yes thats right, but I just tried your code, the thing is, that you set the caption while the form is in its form loading routine, this is causing it to redraw for some reason, because if I take that out and change the caption from any other sub or form it is okay.
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8087313
actually thats a lie, sorry lol
I just tried it and you are right it redraws
0
 

Author Comment

by:r1gga
ID: 8087331
yeah I tried implementing the Set window Pos as follows but to no avail! Is it just me or does this not make sense?

    L = SetWindowLong(Me.hWnd, GWL_STYLE, L)
    ' Force caption bar's refresh. Disabling X button
    DrawMenuBar Me.hWnd
    L = SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER
    L = SetWindowPos(Me.hWnd, 0, 0, 0, 0, 0, L)
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8087347
actually thats a lie, sorry lol
I just tried it and you are right it redraws
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8087350
actually thats a lie, sorry lol
I just tried it and you are right it redraws
0
 
LVL 5

Accepted Solution

by:
RainUK earned 800 total points
ID: 8087498
okay well the only short answer without looking for more info is, everytime you change the caption of the MDI then you'll have to redraw the menu items. Its the simplest solution.

Alternatively try these

Here's some code that might help you:

Download...
http://www.vb-helper.com/HowTo/nosize.zip
Description: Let the user minimize, maximize, and restore a form, but not resize it (3K)

View code...
http://www.planetsourcecode.com/vb/scripts/ShowCodeAsText.asp?txtCodeId=26190&lngWId=1
Description: To disable(grey) the Maximize button.

Download...
http://www.planetsourcecode.com/vb/scripts/ShowZip.asp?lngWId=1&lngCodeId=4669&strZipAccessCode=ODE%5F4669PLOA
Description: Earlier on I submitted some code to Enable / Disable the Close button an a form. Back by popular demand, this code module allows you to do the same to the Minimise or Maximise (Queen's English!) buttons on the form. Here's the code module in a VERY Mickey-Mouse Demo App.
0
 

Author Comment

by:r1gga
ID: 8087578
I'm going to remove the reference to the MDI's caption, but hey thats life having to work around visual basics problems rather than visual basic working around my problems.
Anyway thanks for your help and your quick posts.
r1gga
0
 

Author Comment

by:r1gga
ID: 8087586
SOrry about the C but there was no real solution reached just a compromise
0
 
LVL 5

Expert Comment

by:RainUK
ID: 8087595
glad to help, its not the grade or points, but getting your code to work the way you need it to
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes some techniques which will make your VBA or Visual Basic Classic code easier to understand and maintain, whether by you, your replacement, or another Experts-Exchange expert.
This article describes how to use a set of graphical playing cards to create a Draw Poker game in Excel or VB6.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…
Suggested Courses
Course of the Month11 days, 13 hours left to enroll

752 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question