Solved

DESPERATE: Assigning Panel.Parent in DLL to TabSheet in EXE

Posted on 1997-10-23
23
895 Views
Last Modified: 2010-08-05
First, sorry about the cross-posting, but the question seems
relevant to both newsgroups.  :)

Here's what I am doing:

1)  Creating a DLL that contains a Form.  On the form is a Panel
    that contains a few buttons and a PageControl with 2 or more
    tabsheets.

2)  Creating an EXE that contains a PageControl with 2 or more
    tabsheets.

You can view the EXE as the module level and the DLLs as the
sub-module levels.

What I am doing is calling a function in the DLL passing in
a pointer to a TTabSheet in the EXE.  The DLL then assigns the
Parent property of the Panel to the EXE's tabsheet.

This works wonderfully, except for the PageControl on the
DLLs panel.

If the PageControl on the DLLs panel has more than one tabsheet,
when I click on tabs once it is in the EXEs PageControl, the
tabbing functionality no longer works as expected.  When the
PageControl is created within the EXE, all looks normal until
I click on another tab.  The tab I click comes the the forefront,
but the tabsheet associated with the tab I clicked does not appear.
The tabsheet that originally appeared upon transfer (via the
parent property) is still visible.

This whole process works as you would expect if I am doing all the
transferring within a single EXE...between two forms.  But doing
it between an EXE and a DLL causes unexpected problems.

There is something going on that I'm not aware of and I desperately
need some information or a solution.  Hopefully someone from TeamB
can help.

Here are some code snippets:

--------------------from the DLL----------------------------------
void __fastcall TSatisTestModule::TransferPanelFromDLL()
{
  pnlContainer->Parent = winPage;
}

void __fastcall TSatisTestModule::TransferPanelToDLL()
{
  pnlContainer->Parent = this;
}
//---------------------------------------------------------------------------
//                 DLL Entry Points
//---------------------------------------------------------------------------
void LoadTestModule(TWinControl* i_winPage)
{
  SatisTestModule = new TSatisTestModule(NULL);
  SatisTestModule->Page = i_winPage;
  SatisTestModule->TransferPanelFromDLL();
}

void DestroyTestModule()
{
  SatisTestModule->TransferPanelToDLL();
  if(SatisTestModule->ShutDownCallback != NULL)
  {
    SatisTestModule->ShutDownCallback(SatisTestModule->Page);
  }
  delete SatisTestModule;
}


-------------------from the EXE------------------------------------

void __fastcall TfrmSatisMain::mnuClientLocateSSNClick(TObject *Sender)
{
  FLoadModuleCB LoadModule;
  HINSTANCE hDLL = LoadLibrary("Test");
  if(hDLL != NULL)
  {
    LoadModule = (FLoadModuleCB)GetProcAddress(hDLL, "_LoadTestModule");
  }
  if(LoadModule)
  {
    // tabTest is of type TTabSheet
    LoadModule(tabTest);
  }
}
0
Comment
Question by:mheacock
  • 14
  • 8
23 Comments
 
LVL 3

Expert Comment

by:mirek071497
ID: 1348712
Hi

3 months ago I was answer your question for 400pt. I work hard, but you don't respond to this. Do you grade this new question in any time or I loose my time the second once ?

Mirek.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348713
I don't recall what you are talking about...but I do plan on
grading this promptly...

A lot of things got lost in the shuffle...I moved 1.5 months
ago from BC Canada to California USA to a new job.

What was my last question that you are referring to?  Did it
have something to do with...oh right...bitmap lists or some such...I do recall that I never did use the suggestions I
received...I believe I took another totally different
view on the problem.

Anyhow, I do apologize for whatever problems I may have caused.
Like I said, I went through a big move and things got all
shuffled about.

Anyhow, I never had any intention of stiffing you out of 400
points.  Sorry for the problems.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348714
Hi

I once had a problem with assigning the Parent of a control in a DLL to one in an EXE.  I got around the problem by using ParentWindow (in Delphi 3).  
E.g. MyDLLControl.ParentWindow := MyExeControl.Handle;

My situation was a bit different from yours, but maybe you can use this...

JB
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348715
I'm using C++Builder...which is equivalalent to Delphi2.

I'm only interested in C++Builder code or Delphi2 code.

Thanks for the comment tho.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348716
Mmm...  I seem to recall that C++ Builder doesn't have a ParentWindow property anyway.

0
 
LVL 3

Author Comment

by:mheacock
ID: 1348717
Adjusted points to 1500
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348718
I was playing around with this, and managed to recreate your scenario, I think.  This is what I did, in Delphi (3):
A EXE, which consists of:
    Form, with TPageControl, with 2 TTabSheets.

A DLL, which consists of:
    Form, with Panel.  Panel has its own TPageControl with 2 TTabSheets.

I got the DLL's Panel onto the Form's PageControl's TabSheet, and (as you said) no matter which DLL TabSheet I click on, it only shows the first TabSheet.

The following Delphi 3 code fixes the problem.  (It is implemented in the DLL on TPageControl.OnChange.)

procedure TDLLForm.PageControl1Change(Sender: TObject);
var
    Tab: TTabSheet;
begin
    Tab := PageControl1.ActivePage;

    Tab.Parent := nil;
    Tab.ParentWindow := PageControl1.Handle;
end;

Because Delphi 2 & C++B don't have the ParentWindow property, here is Delphi 3's code for SetParentWindow.  You may be able to do something to "simulate" this.

procedure TWinControl.SetParentWindow(Value: HWnd);
begin
  if (FParent = nil) and (FParentWindow <> Value) then
  begin
    if (FHandle <> 0) and (FParentWindow <> 0) and (Value <> 0) then
    begin
      FParentWindow := Value;
      Windows.SetParent(FHandle, Value);
    end else
    begin
      DestroyHandle;
      FParentWindow := Value;
    end;
    UpdateControlState;
  end;
end;

You can see the SetParentWindow uses the SetParent(hWndChild, hWndParent) API call, but the following by itself does NOT fix your problem in Delphi 3:

procedure TDLLForm.PageControl1Change(Sender: TObject);
var
    Tab: TTabSheet;
begin
    Tab := PageControl1.ActivePage;

    Tab.Parent := nil;
    Windows.SetParent(Tab.Handle, PageControl1.Handle);
end;

Hope you come right.
JB
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348719
I'll try and get back to you quickly on this.  Perhaps a
day or two.  Expect something by Saturday evening at the
latest...thanks again.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348720
BTW, thanks for the effort...you DID recreate the exact problem.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348721
I have no idea what I was doing yesterday...perhaps it was late...but Delph i2 and C++Builder do have ParentWindow properties.  I have no idea where I was looking so that I figured it did not exist.

Anyhow, working on your answer now.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348722
It works great except for one thing...

When I initially click tab #2, tabsheet #1 still appears.  I have to click tab #1, then when I click tab #2 all is okay.

What I think is happening is that when the page control is loaded
I have to initialie the active page with the same code as in the OnChange event.

I've tried various things, but have been unable to do this.  I've tried in the Forms OnCreate, but (it was 2 hours ago) I think it simply cause a GPF type exception.

Any ideas on this one?  Where I should put the initialization code?

BTW, you can look forward to an A and 6000 points.  That'll push you up to the #7 spot.  ;)
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 3

Author Comment

by:mheacock
ID: 1348723
One other thing...I'd really like to understand why the code in
the OnChange event does the trick?

How did you figure out that that was the thing to do?  Why does
it work?  Why do we have to go the API?  Actually what is API SetParent doing that a simple assignment to TWinControl.Parent is
not doing?  Because an assignment to parent works for the other controls...just not PageControl.
0
 
LVL 5

Accepted Solution

by:
JimBob091197 earned 1500 total points
ID: 1348724
Hi

Regarding your 1st comment, I had the same problem in Delphi 3 and forgot to include the solution...  sorry.  I put the code that is in the DLL's OnPageChange into the TransferPanelFromDLL too.  In my example it looks as follows:

procedure TransferPanelFromDLL(TabSheet: TTabSheet);
var
    Tab: TTabSheet;
begin
    // Create the form and transfer the panel.
    frmDLLTest := TfrmDLLTest.Create(nil);
    frmDLLTest.pnlTest.Parent := TabSheet;

    // This is the same code as in pgcTest.OnChange.
    Tab := frmDLLTest.pgcTest.ActivePage;
    Tab.Parent := nil;
    Tab.ParentWindow := frmDLLTest.pgcTest.Handle;
end;

By the way, another thing I noticed was that the Panel on the DLL's form had to have "ParentFont = False" or I would get an Access Violation "Cannot assign a TFont to a TFont" when assigning the panel's parent.  I forgot to mention this, but you had this part working anyway.


Regarding your 2nd comment, I'd also like to know why the code in OnChange does the trick!!!  The only reason I had a clue as to what was going on is because I have played around before with creating controls in a DLL and assigning them to controls on a form in an EXE.  I'm not 100% sure why we have to resort to the API...

I trust you get up & running with this now.
JB

P.S.  I made this an answer.  If I'm jumping the gun, just reject it...
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348725
I'm going to grade this now...if I have any little troubles I'm
hoping you help out.

But I'm feeling positve that all will be okay...even if I
haven't yet added the additional code.

Thanks for the help.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348726
I hope you'll still help.

I've caught a couple of other components that are acting up...specifically StringGrids and ComboBoxes.

Here is how they sit in the PageControl.  On a tabsheet in the Page Control can be a number of group boxes, within the group boxes are TEdits, TStringGrids, and TComboBoxes.  The latter two
come up with error messages when I move to the tabsheet they are on:

   "StringGrid1 does not have a parent window"

At first this appeared to be similar problem to the one I was having with the TTabSheets.  I've spent half a day working on this and still haven't found a solution?

I'm hoping you can help a little further on this?  I'll keep trying things on my end and post if I find a solution.

Thanks.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348727
Hi

I followed your instructions, and indeed the controlls on the 2nd tabsheet gave the trouble you described.  (I had 2 tabsheets on my DLL PageControl; the 1st one works fine, but the 2nd with the grpbox with stringgrid gave your problem.)

The problem is related to the general problem so far, i.e. ParentWindow needs to be set for each child on the tabsheet, and each child's child, etc. etc...

The recursive routine which fixes this problem is below.  I implemented it as a nested procedure in the DLL on the PageControl's OnChange event:

procedure TfrmDLLTest.pgDLLTestChange(Sender: TObject);

    procedure SetControlParentWindows(Ctl: TWinControl);
    var
        i: Integer;
        ChildCtl: TWinControl;
    begin
        i := 0;
        while (i < Ctl.ControlCount) do
        begin
            if (Ctl.Controls[i] is TWinControl) then
            begin
                ChildCtl := TWinControl(Ctl.Controls[i]);
                ChildCtl.Parent := nil;
                ChildCtl.ParentWindow := Ctl.Handle;
                SetControlParentWindows(ChildCtl);
            end
            else Inc(i);
        end;
    end;

var
    Tab: TTabSheet;
begin
    Tab := pgDLLTest.ActivePage;

    Tab.Parent := nil;
    Tab.ParentWindow := pgDLLTest.Handle;

    SetControlParentWindows(Tab);
end;


A couple of comments:
1)  I found that the order of the Parent & ParentWindow statements in "SetControlParentWindows" is critical.
2)  The statement in the "SetControlParentWindows" procedure that sets the child control's parent to nil also decreases the owner control's ControlCount.  Thus I had to use a while loop, because the for loop kept getting index out of bounds...  Took me a while to figure that one out!!

I trust you come right with this solution.
Regards,
JB

P.S. I apologize if the indentation of the code isn't right.  Whenever I cut & paste code from my editor the tabs get lost...
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348728
I will try this code out tomorrow at work.  Thank again for all your efforts.  It is very much appreciated.  And I'm grateful for this valuable learning experience.

Now there's one more person in the world who knows how to implement this!  :)

0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348729
You're welcome.

JB
;-)
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348730
I'm having some trouble getting that code to work.  I'm still working on it, but I may need the code you successfully got working.

Could you mail it at your earliest convenience to (include the .EXE and .DLL in the zip file if you can):

      mheacock@sprynet.com
      mheacock@msjcorp.com

If you could send the code to both addresses, that would be much appreciated.

0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348731
I mailed the sample to you.

JB
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348732
Hey JimBob,

Well, you're solution never worked in C++Builder.  Though, through no fault of your own.  Whereas you're solution worked perfectly in Delphi 3, it never worked for certain VCL components in C++Builder.

I eventually had to call Borland Tech Support, and after a week of trying different things, they finally decided that it had to be a bug.

So we've decided that an ActiveX solution is the best bet.  It seems to work, except for a small problem.  I know the points are in the 1000+ range, but I never figured on coming back and having to ask another question.  I can always bump it to 350.

If you want to give my new question a shot, please check it out.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1348733
Hi again

I found something very interesting in Delphi.  I'm not sure that it's the same in C++ Builder.

If you create a component (TForm, TPanel, whatever...) in a DPL (Delphi Package Library) instead of a DLL, then you can call "MyPanel.Parent := MyForm;" instead of doing the whole "ParentWindow := MyForm.Handle" routine.  There are a few catches, but I can mail the sample to you if you want.  I found it quite interesting.  You can enhance it as need be...

JB
0
 
LVL 3

Author Comment

by:mheacock
ID: 1348734
Don't worry...

Never was able to do the task in C++Builder.  Even did the Borland tech support thing.  We tried various things or a week before they decided that it was an official bug in the C++Builder VCL.  Bug #8551.  It will be fixed in the next release.

BTW, according to Borland, the normal method ot do what I was asking was to just set the DLL form's parent to the control I wanted it embedded into.  So the form was contained in the TTabSheet.  Not something I would have thought of.

That's what I'm doing now...it all works from an EXE, just not a DLL...like I said bug 8551.

We have decided to contain our sub-modules in the EXE until the next release of C++Builder.  I continued with my current method so upon the next release, breaking the sub-modules into DLLs will be a day or two worth of work.

Cheers,

PS if you want to try the form within a form thing...do this:

(convert to Delphi yourself)

  Form2 = new TForm(Form1);
  Form2->Parent = Form1;
  Form2->Show();

0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

760 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

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now