Friday, September 30, 2005

ModalPopupMode

Remember this post?  It was where I described the newly added PopupMode and PopupParent properties to TCustomForm.  Lets fast-forward to now, in the midst of the DeXter release of Delphi.  It seems that this change highlighted some shortcomings in the implementation of, not only, Borland supplied components but also of many third-party components... Well.. maybe shortcomings is too strong of a word... let's just say “issues.”  One of the original goals of VCL visual components was to delay the construction of the underlying window handle to the last possible moment.  It was also a goal for all visual components that use a window handle to also properly survive a handle recreation.  The visual component should save off any state that is internally associated with the window handle and then properly restore it once the new handle was created.  The reason for this is that there are some window styles and/or states that cannot be change on the fly without destroying and creating a new window handle.  This was especially true back in Win16.  Thankfully, this is less of an issue for Win32, but there still are a few cases.  So when the PopupMode properties were introduced, the only way to change the “owner” of a window handle was to destroy it and then recreate.  There is no SetOwner API similar to SetParent. (Ok, if there is, I haven't found it..)  So why is it important that the correct owner be set for modal UI?  Raymond Chen explains one reason quite well here.  In fact, I encourage you to peruse a lot of what Raymond has to say regarding the inner workings of Windows.  In today's modern world of frameworks and abstraction layers, rarely do folks ever need to really understand the raw Windows API level functionality, however sometimes it is good to go back and revisit why and how things are done the way they are.  Raymond rarely disappoints in that regard.

So back to the subject at hand... In order to properly guarantee that modal forms were “owned” properly, the ShowModal method on TCustomForm was changed to force a recreation of the handle if the PopupMode was set to pmNone.  What this did was ensure in CreateWnd, the proper owner was selected for the new window handle.  This seemed to wreak no end of havoc on existing VCL applications.  It also served to highlight how “recreate brittle” many of our own VCL components were.  The good thing about this is that for DeXter, we've corrected all those issues and our VCL components are now “recreate tolerant.”  In fact, if you're a third-party component developer, I encourage you to review your own components to determine their “recreate brittleness.” 

Now the standard technique for “fixing” this issue was to simply set the PopupMode property on all your forms that are intended for modal operation to pmAuto.  This would ensure that when ShowModal was called, no recreates were nessesary because the correct owner would have, presumably, been selected.  There are cases where this may still not be the case, especially when you don't create and destroy your modal forms near the point they're needed.  In response to the feedback regarding this issue and the fact that when you are bitten by it, it is extremely difficult to determine the cause, we'll be introducing a new TApplication property that controls this modal behaviour at the global level in the DeXter release.  The new TApplication.ModalPopupMode property does this.  For consistency sake the type of this property is TPopupMode, which is an enumeration with three elements.  The default value for this is pmNone, which makes ShowModal behave the same as the Delphi 7 version of VCL and earlier.  Setting this property programatically to anything other than pmNone (pmAuto or pmExplicit), will enable the current Delphi 8 and Delphi 2005 behaviour.  So now you can choose when and if you need to support this new behaviour.

For component developers, the IDE code explicitly sets this value to pmAuto, so if you have any modal dialogs as part of the component/property editors associated with your components, then you'll still need to ensure that those dialog's PopupModes are either set to pmAuto, or they are not “recreate brittle.” This is simply because your VCL components are running on top of the same core VCL that  the IDE is using.