Question

This one is driving me up a wall. Most of the conversion from Delphi 6 to XE5 is proceeding smoothly, but I have various routines to dynamically build various TForm descendents (NO DFM), pop it up and generally return a value. I have a number of them that work fine in D6. Generally, I choose a place I want to pop something up (like over a panel), and what I want to popup (editbox, memo, listbox...). I create the form, set initial values and call showmodal and return some result.

The same code, compiled in XE5 has execution (glitches). One is that the created form accepts left,top and such, but does NOT display itself there. The values are correctly in the properties, but the form is in the wrong place. A second, probably related (glitch) is that when I create a TMemo or TListbox and store some text in it, "ShowModal" displays the data properly, but "Show" does not.

It has taken me several hours to digest the problem down to its simplest form, removing virtual all of my personal code. AS SHOWN HERE, IT WORKS PERFECTLY

If I comment out this line, it does not work - the form is displayed in the wrong place

XX.ClientToScreen(Point(0,0));      // EXTREMELY WEIRD PATCH

This line is a function call which OUGHT NOT affect anything else, and I don't use the returned value.

The commented out "Show" line demonstrates the other problem (data not being displayed).

I have tried Application.ProcessMessages in all sorts of places, but it never makes things better, and at times make things worse.

Color me "puzzled".

//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------

type TMemoForm = class(TForm)
   private
   public
      XMemo         : TMemo;
   end;


Function  PopUpMemoStr(txt : AnsiString; x : integer = 200; y : integer = 200; w : integer = 400 ; h : integer = 400 ) : AnsiString;  // more or less a dummy for testing on XE5 2/28/14
var XX : TMemoForm;
     begin
     XX := TMemoForm.CreateNew(Application);

     XX.ClientToScreen(Point(0,0));      // *** EXTREMELY WEIRD FIX ***
     XX.Left := X; XX.Top := Y; XX.Width := w;  XX.height := h;
     XX.caption := 'Dummy PopUpMemo';

     XX.XMemo := TMemo.create(XX);
     XX.XMemo.parent := XX;
     XX.XMemo.align := alClient;
     XX.XMemo.text := txt;

     //logit('PopUpMemoStr R='+TRectToStr(MyGetScreenRect(XX)));
     XX.showmodal;
     //XX.show; delay(3.00);  // other "no data" problem
     XX.free;
     end;

//exercise code -- Panel2 is just a visible spot to see if positioning works correctly 
var s : AnsiString;
var R : TRect;
     begin
     //R := MyGetScreenRect(Panel2);                    
     R := Rect(414,514,678,642);        // just a useful screen location for testing
     s := 'One'+CRLF+'Two'+CRLF+'Three'+CRLF+'Four';    // "CRLF is #13#10
     PopUpMemoStr(s,R.Left,R.Top,R.Right-R.Left,R.Bottom-R.Top);


//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
Was it helpful?

Solution

To fix the form positioning problem, you need to set the form's Position to poDesigned.

For your second problem, you can't delay like that. You are not giving the Form a chance to process messages. Changing it to something like the code below displays the data correctly (although you really should not be doing this sort of thing either):

begin
  XX := TMemoForm.CreateNew(nil);
  try
    XX.Position := poDesigned; // This line needs to be added for the positioning
    XX.SetBounds(X, Y, w, h);
    XX.Caption := 'Dummy PopUpMemo';

    XX.XMemo := TMemo.Create(XX);
    XX.XMemo.Parent := XX;
    XX.XMemo.Align := alClient;
    XX.XMemo.Text := txt;

    //logit('PopUpMemoStr R='+TRectToStr(MyGetScreenRect(XX)));
    // XX.ShowModal;
    // This displays the data correctly but is not advisable
    XX.Show;
    for I := 1 to 6 do
    begin
      Sleep(500);
      Application.ProcessMessages;
    end;
  finally
    XX.Free;
  end;
end;

If you want to use Show() for a Form like that, you should use the Form's OnClose event and set its Action parameter to caFree and just do the Show() in your code. Put a timer on the Form for x seconds and Close() it when the timer finishes. A bit like this:

type
  TMemoForm = class(TForm)
  public
     XMemo : TMemo;
     XTimer: TTimer;
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure TimerElapsed(Sender: TObject);
  end;

procedure TMemoForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

procedure TMemoForm.TimerElapsed(Sender: TObject);
begin
  Close;
end;

begin
  XX := TMemoForm.CreateNew(nil);
  try
    XX.Position := poDesigned; // This line needs to be added for the positioning
    XX.SetBounds(X, Y, w, h);
    XX.Caption := 'Dummy PopUpMemo';
    XX.OnClose := XX.FormClose;

    XX.XMemo := TMemo.Create(XX);
    XX.XMemo.Parent := XX;
    XX.XMemo.Align := alClient;
    XX.XMemo.Text := txt;

    XX.XTimer := TTimer.Create(XX);
    XX.XTimer.Interval := 3000;
    XX.XTimer.OnTimer := XX.TimerElapsed;
    XX.Active := True;

    XX.Show; // Just show the form. The rest is in the Form itself.
  except
    XX.Free;
    raise;
  end;
end;

OTHER TIPS

Your extremely weird patch, calling ClientToScreen on the newly created form, should fix the issue as it does, even if you don't use the point that's returned.

In the case when you don't use it, when you set your form's bounds, since the window of the form has not yet been created, the VCL keeps this information to be later passed to the API when the window is about to be shown. But this information will be discarded since VCL also tells the API to use default window position because of the poDefaultPosOnly setting of Position property.

In the case when you use it, to be able to determine the position of the form in the screen the VCL first creates the window of the form. Hence when you later set the bounds of the form, they are actually implemented through SetWindowPos.

As such, if you've used

XX.HandleNeeded;

instead of

XX.ClientToScreen(Point(0,0));

it would be a more direct workaround.

Of course the correct solution is in Graymatter's answer.


I cannot comment on Show not displaying data, the code you posted in the question should not exhibit that kind of behavior.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top