Sunday 21 July 2013

TFireMonkeyContainer update: bugs fixed, features added

The example application showing a 3D FireMonkey form.
On the other tab is a standard 2D form.
On Wednesday I announced TFireMonkeyContainer, a VCL control that can host a FireMonkey form, allowing you to mix FireMonkey elements into your VCL app. It was (and is) a new project, and the announcement page listed several known bugs and possible future design changes.

Bugs: gone! Design changes: made! The current state is not quite perfect but is bug-free enough I feel happy about you using it in real applications, or at least trying it out. (With the caveat that I'm the only one who has tested it so far, and my entire QA department is me :) If you try it, please let me know how it goes.

 Bugs fixed

  • Focus now correctly follows the host VCL and hosted FMX forms
  • Window activation now correctly follows the host VCL and hosted FMX forms, when switching by clicking on the host VCL form, the hosted FMX form, via alt-tab, via the taskbar, and when there are several VCL forms and several hosted FMX forms in the one application.
  • The title bar of host VCL forms correctly changes when the form is active and inactive.
  • An exception is now thrown when two containers both try to host the same form.

Design changes

    This FireMonkey form embedded in the VCL form can't be
    edited in the VCL designer - it's a static preview. Switch
    to the FMX form tab to edit it.
  • Design-time (in the IDE): When a FireMonkey form is hosted in a VCL form in designtime, it shows an uneditable view of the hosted form. (This used to be an editable FireMonkey form, ie you could invoke the FireMonkey designer. This only partially worked and caused a lot of problems.) Switch tabs to the FireMonkey form's IDE design tab to edit the FireMonkey form. Changes will be reflected immediately when you switch back to the VCL form's design tab.
  • There are two new events you can use to control hosting the FireMonkey form. This means there are three ways to embed a FireMonkey form:
    1. Set the FireMonkeyForm property at design-time. (Not recommended, see 'Known problems' below.)
    2. Set the FireMonkeyForm property at runtime.
    3. Use the OnCreateFMXForm and OnDestroyFMXForm events.
      • OnCreateFMXForm fires when the component is first created. It has a var Form parameter which you can use to set the hosted FireMonkey form. If a form was specified at designtime, Form refers to it. If you change it, OnDestroyFMXForm will be called for the old value.
      • OnDestroyFMXForm fires when the component is destroyed. Much like a TForm's OnClose event, you can choose an action to apply to the hosted FMX form, eg to free it. The default is to do nothing, since it was probably created with an owner. The Destroy event also occurs when you change the Form parameter in the Create event (eg, when there was a FMX form set at design-time, but in the Create event you change it to another form.)

Known problems

  • When you set the FireMonkeyForm property at designtime, you can only set it to a FMX form that is open in a tab in the IDE - otherwise the IDE doesn't see it and won't show it in the dropdown in the Object Inspector. When the IDE tab is closed, the reference is invalid - and this means the FireMonkeyForm property is cleared. This might be due to problems mixing VCL and FMX forms in the one app, or (more likely) it might be due to a mistake I've made in how to correctly reference another form in a component. Suggested workaround: set the property at runtime, or use the OnCreateFMXForm and OnDestroyFMXForm events instead.

Notes, and getting the code

The component subclasses both the VCL form and the FMX form in order to catch the WM_NCACTIVATE (VCL form) and WM_ACTIVATE and WM_MOUSEACTIVATE (FMX form) messages. I'm not an expert in window activation and there may be bugs (or I might have just plain written bad code) in the  handlers. I'm also not certain I have hit on completely the right design for the events. I'd quite appreciate feedback if you use the component and feel that the events that occur are a bad design or force you to write ugly code. The same goes for any aspects of the component, really.

You can download the latest version from the Google code homepage and Subversion source checkout.

7 comments:

  1. Trying to get it running on XE2. Looks like FireMonkeyContainer_Design.* files are replaced by FireMonkeyContainer_Designtime.*, don't they?
    Ouch! I'm out of luck
    [DCC Error] FMXContainer.pas(401): E2003 Undeclared identifier: 'IFMXWindowService'
    [DCC Error] FMXContainer.pas(406): E2003 Undeclared identifier: 'TPlatformServices'
    [DCC Error] FMXContainer.pas(483): E2003 Undeclared identifier: 'TWinWindowHandle'
    [DCC Error] FMXContainer.pas(496): E2003 Undeclared identifier: 'WindowHandleToPlatform'
    [DCC Error] FMXContainer.pas(497): E2029 'END' expected but ')' found
    Last one expects Exit statement without parameters.

    ReplyDelete
    Replies
    1. Hi Ilya,

      XE2 uses an older version of FireMonkey (I think XE3 introduced FireMonkey 2.) I expect you will have trouble with this method (HandleResize) and also GetFMXFormWindowHandle.

      For the first, try using FMX.Platform and FMX.Platform.Win and using Platform.SetWindowRect. For the second, try calling FmxHandleToHWND(Form.Handle) instead of WindowHandleToPlatform. See https://code.google.com/p/delphisorcery/source/browse/trunk/Source/Windows/DSharp.Windows.FMXAdapter.pas for a unit demonstrating this (I saw that code and was inspired to create this component.)

      If this works, please feel free to email me a patch and if I can figure out the right FireMonkey version ifdefs, I'll add it to the component.

      Delete
    2. After some quick investigation, there may be more issues with window parenting and message handling. I believe FireMonkey 2 changed quite a few things.

      I'm probably only going to support FM2, not FM1, myself, simply because of my limited time. But, if you make any progress with an early version of FM, feel free to let me know and I'll do what I can to add support to the component.

      Delete
    3. So much time passed :)
      I've made it running in XE2! Your suggestion have worked out quite well. I've also get rid of many FMX settings (Designer*, FormFactor.*, StyledSettings, KillFocusByReturn, IsSelected) in FMXForm.fmx and TLightMaterialSource in FMX3DForm.
      May I report this as issue on the google code and attach modifications needed?

      Delete
    4. It has been some time! That sounds great, so yes, please do :)

      Delete
    5. I've created issues and attached the patch on the google code page. Have you seen it?

      Delete
    6. Ilya - thanks! I will look at it as soon as possible (hopefully tomorrow.)

      Delete