cancel
Showing results for 
Search instead for 
Did you mean: 

Resizable response window with control menu

Former Member
0 Kudos

I'm using PB 11.5, in case that is relevant to the following.

So, response windows can't be resizable in PB, but I wanted them to be. I found a solution for that online somewhere (sorry, I seem to have lost the link). First you declare the following:

// Unicode declarations, - used to set Response windows to be resizable, which PB doesn't allow

function long GetWindowLongW (long hWindow, integer nIndex) Library "user32.dll" 

function long SetWindowLongW (long hWindow, integer nIndex, long dwNewLong) library "user32.dll"

The you call a function with the following code, in the window's Open event:

long        ll_Styles

n_cst_numerical lnv_num // PFC resize service

 

constant long WS_THICKFRAME = 262144

constant long WS_CAPTION = 12582912

constant long WS_SYSMENU = 524288 // N.B. This corresponds to ControlMenu in PB's General Window settings, it never seems to

               //        work for Response window, and in fact having ControlMenu checked on the actual

              //         Response window causes it to not be resizable!

/*

constant int SWP_NOSIZE = 1

constant int SWP_NOMOVE = 2

constant int SWP_NOZORDER = 4

constant int SWP_FRAMECHANGED = 32

*/

ll_styles = GetWindowLongW(handle(THIS), -16) 

if ll_styles <> 0 then 

    ll_styles = lnv_num.of_BitWiseOr(ll_styles, WS_THICKFRAME + WS_CAPTION)

   SetWindowLongW(handle(THIS), -16, ll_styles)

// According to the docs, calling the following is required after adding the WS_SYSMENU in SetWindowLong.

//     So I was hoping it would fix the fact that it makes the window non-rezisable. But it doesn't, unfortunately.

// SetWindowPos(handle(THIS), 0, 0, 0, 0, 0, SWP_FRAMECHANGED + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER)

end if

As you can perhaps see from my comments, there's a problem, which is that doing this doesn't work if Control Menu is checked in the window's painter (which is what puts the "X" at the top right, for closing the window). Adding in WS_SYSMENU to ll_styles should turn that back on, but it doesn't. (I can't immediately recall whether it does nothing, or also makes the window not be resizable.)

I don't suppose anybody has any bright ideas how to fix this? It's a bit weird in my app, which is mostly response windows, some of which are resizable and some of which aren't, that only the non-resizable ones have the control menu's "X".

Thanks.

Accepted Solutions (1)

Accepted Solutions (1)

former_member190719
Active Contributor
0 Kudos

Here's what we use.  This is the of_setresizable function that takes an argument that determines whether or not a sizegrip should show at the bottom right corner of the window:

ulong     ll_style, hMenu, hWnd, ll_newstyle

hWnd = Handle( this )

 

//Turn on the instance variable that is used elsewhere to see if the window is resizeable

ib_resizeable = TRUE

 

//Get the original size of the window, which we will prevent the user from sizing the window

//less than

il_orig_width = this.width

il_orig_height = this.height

 

//Get the current window style

ll_style = GetWindowLong(handle(this), GWL_STYLE)

 

IF this.ControlMenu THEN

    //You have to include the MINIMIZEBOX attribute to get the controls to show

    ll_newstyle = ll_style + WS_THICKFRAME + WS_MINIMIZEBOX

ELSE

    ll_newstyle = ll_style + WS_THICKFRAME

END IF

 

IF ll_style <> 0 THEN

    IF SetWindowLong ( hWnd, GWL_STYLE, ll_newstyle ) <> 0 THEN

        IF this.ControlMenu THEN

            //Get a handle to the system menu

            hMenu = GetSystemMenu( hWnd, FALSE )

            IF hMenu > 0 THEN

                InsertMenu( hMenu, 1, MF_BYPOSITION + MF_STRING, &

                    SC_MAXIMIZE, "Maximize" ) ;

                InsertMenu( hMenu, 1, MF_BYPOSITION + MF_STRING, &

                    SC_RESTORE, "Restore" ) ;

                //The Size menu option has to be added to allow the resize gripper to work

                //if there is a control menu

                InsertMenu( hMenu, 1, MF_BYPOSITION + MF_STRING, &

                    SC_SIZE, "Size" ) ;

                DrawMenuBar( hWnd )

            END IF

        END IF

        //Force a repaint

        SetWindowPos ( hWnd, 0, 0, 0, 0, 0, &

            SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_FRAMECHANGED )

        this.SetRedraw( TRUE )

        //Track whether or not the size grip should show

        ib_show_szgrip = ab_showgrip

    END IF

END IF

We use this in an event mapped to pbm_getminmaxinfo:

//If this isn't a resizeable response window, don't bother with this

IF NOT ib_resizeable THEN RETURN

//Since we're resizable, we want to make sure that the user doesn't try to resize the

//window smaller than it's original size

s_MinMaxInfo lstr_MinMaxInfo

//Populate the structure

GetMinMaxInfo(lstr_MinMaxInfo, MinMaxInfo, 40)

//Determine the minimum size of the window (the original size)

lstr_MinMaxInfo.ptMinTrackSize.x = UnitsToPixels(il_orig_width, XUnitsToPixels!)

lstr_MinMaxInfo.ptMinTrackSize.y = UnitsToPixels(il_orig_height, YUnitsToPixels!)

//Now set the Min/Max info

SetMinMaxInfo(MinMaxInfo,lstr_MinMaxInfo,40)

return 0

Former Member
0 Kudos

Bruce, I tried pretty much exactly your code, but first of all, although I have TitleBar checked in the painter for my window, unless I add in WS_CAPTION to the styles, the title bar disappears entirely. And even with it added in (and your code otherwise the same), I never get a control menu or "X" at the top right (which is what I really want).

The only other thing that might be different is I'm doing all this in an ancestor window of the actual window. (Maybe you were too and just didn't mention it.)

I will also study Chris' code that he pointed me at.

former_member190719
Active Contributor
0 Kudos

That code is from our w_reponse window, the ancestor of all our response windows.  Works great for us.  I can only think you might have a constant value or external declaration wrong.

Did you see the comment in the code about about "You have to include the MINIMIZEBOX attribute to get the controls to show" and include that code?

Here's the constants we're using

CONSTANT int GWL_STYLE = -16

CONSTANT uint SWP_NOSIZE = 1

CONSTANT uint SWP_NOMOVE = 2

CONSTANT uint SWP_NOZORDER  = 4

CONSTANT uint SWP_FRAMECHANGED = 32

CONSTANT ulong WS_SYSMENU = 524288

CONSTANT ulong WS_THICKFRAME = 262144

CONSTANT ulong WS_MINIMIZEBOX = 65536

CONSTANT ulong WS_MAXIMIZEBOX = 131072

CONSTANT uint SC_RESTORE = 61728

CONSTANT uint SC_MAXIMIZE = 61488

CONSTANT uint SC_MINIMIZE = 61472

CONSTANT uint SC_SIZE = 61440

CONSTANT uint MF_BYCOMMAND = 0

CONSTANT uint MF_STRING = 0

CONSTANT uint MF_BYPOSITION = 1024

Former Member
0 Kudos

Those are exactly the values I had, Bruce, with one exception: You seem to have swapped the values of WS_MINIMIZEBOX and WS_MAXIMIZEBOX, according to my copy of WinUser.h. So I tried using your value - no change in the behaviour. Without WS_CAPTION, there's no title bar, and with it, there's still no control menu or "X".

Here are my local external function declarations, in case you can see something wrong with them:

function long GetWindowLongW (long hWindow, integer nIndex) Library "user32.dll" 

function long SetWindowLongW (long hWindow, integer nIndex, long dwNewLong) library "user32.dll" 

function long SetWindowPos(long hWnd, long hWndInsertAfter, int newX, int newY, int newWidth, &

          int newHeight, uint uFlags) library "user32.dll"

function ulong GetSystemMenu(ulong hWnd, long bRevert) library "user32.dll"

function long InsertMenuW(uLong hMenu, uint uPosition, uint uFlags, uint uIDNewItem, String lpNewItem) library "user32.dll"

function long DrawMenuBar(ulong hWnd) library "user32.dll"

(Yes, I could use aliases to get rid of the "W" in the some of the function names, but obviously, that's not part of the problem.)

former_member190719
Active Contributor
0 Kudos

I have some differences, though I don't know that they're material:

function boolean SetWindowPos(long hWnd, long hWndInsertAfter, int newX, int newY, int newWidth, &

          int newHeight, long uFlags) library "user32.dll"
function ulong GetSystemMenu(ulong hWnd, boolean bRevert) library "user32.dll"

function boolean InsertMenuW(uLong hMenu, uint uPosition, uint uFlags, uint uIDNewItem, String lpNewItem) library "user32.dll"

function boolean DrawMenuBar(ulong hWnd) library "user32.dll"

What probably is material is this:

IF this.ControlMenu THEN

          //You have to include the MINIMIZEBOX attribute to get the controls to show

          ll_newstyle = ll_style + WS_THICKFRAME + WS_MINIMIZEBOX

ELSE

          ll_newstyle = ll_style + WS_THICKFRAME

END IF

Since I've got the values of WS_MINIMIZEBOX and WS_MAXIMIZEBOX reversed, you probably want to change that to:

IF this.ControlMenu THEN

     //You have to include the MAXIMIZEBOX attribute to get the controls to show

     ll_newstyle = ll_style + WS_THICKFRAME + WS_MAXIMIZEBOX

ELSE

     ll_newstyle = ll_style + WS_THICKFRAME

END IF

Former Member
0 Kudos

OK, Bruce, I finally figured out what was wrong. I was using a version of n_cst_numerical from the PFC to do the bitwise or-ing of the styles, whereas your code was just adding in the new styles.

When I tried just adding them in, your code worked perfectly.

When I dug into n_cst_numerical, the problem was that the of_BitwiseOr() function I was calling was expecting a long arg, and returning a long. When I looked at the current actual window styles value, pre-modification, it was not within the range of long values, but it was in the range of ulong values. When I hacked of_BitwiseOr to accept and return ulong values instead, it gave the same results as the simple addition in your code.

FWIW, Chris, your object that has similar functions for bitwise OR also uses longs, not ulongs, so presumably could run into the same problems if used on window styles. (Though perhaps there are other sensible uses where you would prefer to be working on signed longs.)

Just one remaining question - I really don't want the user to be able to maximize these windows, just size them a bit. (Largely because in almost all cases, I'm only sizing the main DW on the window vertically, not horizontally, so maximizing just puts in a lot of white space on the right.) So, any way to have this working, but remove the max box at the top right?

MANY THANKS!

Former Member
0 Kudos

P.S. The following two lines don't seem to be required:

SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER)

this.SetRedraw(TRUE)

Everything seems to work fine without them. Any reason to leave them in?

former_member190719
Active Contributor
0 Kudos

Glad you got it resolved.

You could probably tweak the code in the GetMinMaxInfo event. What I have just makes sure they don't make the window too small.  You should be able to set the ptMaxTrackSize (like the current code sets the ptMinTrackSize) to prevent the window from being sized over a set limit.

former_member190719
Active Contributor
0 Kudos

That forces a repaint of the window.  If it works fine for you without it, comment it out.  You can always uncomment it if you run into problems later.  It may only be required if you're using another control as a size grip (which we do).  Hard to tell at this point, I wrote that code back in 2004....

Former Member
0 Kudos

If you dont want the Maximize button to show, remove or comment out the portion in red below.

It doesn't max a lot of sense to me why removing the WS_MINIMIZEBOX also removes the Maximize box.  But it seems to work.  The X still appears in the corner.

IF this.ControlMenu THEN

    //You have to include the MINIMIZEBOX attribute to get the controls to show

    ll_newstyle = ll_style + WS_THICKFRAME //+ WS_MINIMIZEBOX

ELSE

    ll_newstyle = ll_style + WS_THICKFRAME

END IF

Former Member
0 Kudos

@ Bruce -

In your event mapped to pbm_getminmaxinfo - What are your external declarations for the GetMinMaxInfo and SetMinMaxInfo functions?

Maybe:

subroutine GetMinMaxInfo ( ref str_minmaxinfo d, long s, long l ) library 'kernel32.dll' alias for RtlMoveMemory

subroutine SetMinMaxInfo ( long d, str_minmaxinfo s, long l ) library 'kernel32.dll' alias for

RtlMoveMemory

Do they work on 64 bit machines?

former_member190719
Active Contributor
0 Kudos

Actually, the point of that was to use the setting of the control menu on the window to determine whether the min/max buttons show.  Otherwise the IF doesn't serve a purpose with that code commented out.

That is, you don't need to comment out the code.  Just don't check the ControlMenu option in the window painter and you won't get the buttons.

Also note that my sample has the values for WS_MINIMIZEBOX and WS_MAXIMIZEBOX reversed.  That's why the code passes WS_MINIMIXEBOX here.

former_member190719
Active Contributor
0 Kudos

Yes, that's exactly what they look like.

Works great on 64 bit.

Answers (2)

Answers (2)

Former Member
0 Kudos

Hi Dan;

  This is all done already in my Foundation Classes for PowerBuilder framework that is free and available for download from the SourceForge website. I have also adapted this code-line for Appeon as well so Web applications can have re-sizable response dialogs as well.

  You can download the framework from here: http://sourceforge.net/projects/stdfndclass

Feel free to use the framework or copy off what you need for this task. 

HTH

Regards ... Chris

Former Member
0 Kudos

Chris, all your code in the wn_response_master object seems to do is add in WS_THICKFRAME to the style, which in my testing does not actually allow the object to be resized.

Former Member
0 Kudos

Hi Dan;

  That's all you need to do. What you guys are doing is "way over kill" IMHO. Also, my framework allows a minimum and maximum size and will hold the window dialog to those dimensions during a resize operation. 

FWIW: The ancestor code is basically ...

IF ib_resizeable = TRUE THEN

  // Lets get ready to make it Resizable!

  IF ib_valid_environment = TRUE then

     long  ll_styles

     ll_styles = GetWindowLong (handle(THIS), -16)

     IF  ll_styles <> 0 THEN

          nc_numerical_master      lo_numerical

          lo_numerical          =  CREATE nc_numerical_master

          lo_numerical.of_register (THIS)

          ll_styles             =  lo_numerical.of_bitwiseor (ll_styles, ws_thickframe)

          Destroy lo_numerical

          SetWindowlong (Handle (THIS), -16, ll_styles)

     END IF

  End IF   

END IF

The key to the whole resizability is to be able to OR the current window's style onto the existing state of the dialog. My frameworks "numerical" object - written in pure PowerScript BTW - has implemented OR, AND, xOR and xAND plus SLL and SLR low level bit manipulations to make this easy to implement.

You might want to download the sample OrderEntry application from SourceForge as well and see the response dialog resizability feature in full operation within the initial Logon screen interaction.

HTH & good luck in your endevour!

Regards .. Chris

Former Member
0 Kudos

Chris, I have definitely tried code exactly equivalent to yours, where all I do is OR in WS_THICKFRAME and set it back to the window. I can't explain why it works for you and not for me, but when I do it, the window does definitely NOT become resizable. All that changes is that when the mouse hovers over the edges of the window, the cursor changes to a resizing cursor. But if you then click on that edge and try to drag to resize, nothing happens.

I really need to leave this problem now and move on to other things. Maybe you and Bruce could discuss why or whether the other parts of the code are required, and if you figure it out, post that back here.

Former Member
0 Kudos

That's Weird as this code has been working for me for about a decade now over many PB releases plus 4 years with Appeon. I will recheck & trace the flow to see if anything else in the framework is being done to assist this basic code. If I find anything - I will let you know.

former_member190719
Active Contributor
0 Kudos

If you look at the parts of the code where this test is done:

     IF this.ControlMenu THEN

You'll see that 90% of the code (or so) handles the TRUE case.  If you look at what happens for FALSE, it's basically the same thing Chris' code does.

That is, Chris' code is fine if you don't want to have a control menu on the window.  If you do, you need the additional code we use.

Former Member
0 Kudos

Well, that code still doesn't work for me, as I explained. Don't know why.

Former Member
0 Kudos

Hi Dan;

It does not work for me 100% either. I am working on refining my framework code - so far I have dynamic Min/Max controls working with resizing. A little better ... will let you know when I have it 100%.

BTW: Bruce ... thanks $!M though for your sharing your approach. I think that the optimal implementation is somewhere in between what we have all discussed. 

Regards ... Chris

Former Member
0 Kudos

I Googled and found the original article this was based on:

http://anvil-of-time.com/wordpress/powerbuilder/powerbuilder-resizing-a-response-window-or-user-obje...

There is a similar article and solution at http://eric.aling.tripod.com/PB/tips/pbtip39.htm, but he advises only adding in WS_THICKFRAME and optionally WS_SYSMENU to the styles. When I try that (with both), there is no title bar on the window at all, let alone a control menu!