Page 2 of 3 FirstFirst 1 2 3 LastLast
Results 16 to 30 of 32

Thread: Centreer een dialog op mainform

  1. #16
    TWinControl stamt ook af van TPersistent. Dialogs stammen vooral af van TCommonDialog die weer afstamt van TComponent. Maar dat doet niet terzake, wanneer je het dialog toont, dan heb je wel degelijk een geldig window, mét een geldige handle, waarmee je dat soort dingen zou kunnen doen. SetWindowPos heeft alleen een handle nodig en werkt normaliter ook op vensters die niets met TWinControl te maken hebben (omdat bijvoorbeeld de hele applicatie niet in Delphi is geschreven).
    Dat het kan werken wordt bewezen door het feit dat TFindDialog wel werkt. Ik denk dat TOpenDialog en TSaveDialog die opdracht om de één of andere reden tegenhouden.
    1+1=b

  2. #17
    Ik heb geprobeerd om een afgeleide van de savedialog te maken en dan de dialog te centreren in de dialoginit message (zie code hieronder). Zelfs dit lukt niet, je komt wel in de handler maar de SetWindowPos lijkt de tweede keer openen van het dialog helemaal niets te doen. Ik heb ook nog wat gespeeld met de vlaggen die je meegeeft aan de SetWindowPos maar dat verbeterde het niet.

    Code:
    unit RioDialogs;
    
    interface
    
    uses Dialogs, Forms, Messages, Windows, CommDlg;
    
    type
      TCenterSaveDialog = class(TSaveDialog)
      private
        FCenterForm: TCustomForm;
      protected
        procedure WndProc(var Message: TMessage); override;
      public
        // When this property is assigned, the dialog will center
        // itself over the given form each time the dialog appears.
        property CenterForm: TCustomForm read FCenterForm write FCenterForm;
      end;
    
    implementation
      
    procedure TCenterSaveDialog.WndProc(var Message: TMessage);
    var
      lpOfNotify: POFNotify;
      FormRect, DialogRect: TRect;
      NewLeft, NewTop: Integer;
    begin
      inherited;
      if (Message.Msg = wm_Notify) and Assigned(CenterForm) then
      begin
        lpOfNotify := POFNotify(Message.LParam);
        if lpOfNotify.hdr.code = cdn_InitDone then
        begin
          GetWindowRect(CenterForm.Handle, FormRect);
          GetWindowRect(lpOfNotify.hdr.hwndFrom, DialogRect);
          NewLeft := FormRect.Left
            + (FormRect.Right - FormRect.Left) div 2
            - (DialogRect.Right - DialogRect.Left) div 2;
          NewTop := FormRect.Top
            + (FormRect.Bottom - FormRect.Top) div 2
            - (DialogRect.Bottom - DialogRect.Top) div 2;
          SetWindowPos(
            lpOfNotify.hdr.hwndFrom,
            HWND_TOP,
            NewLeft, NewTop,
            0, 0,
            swp_NoSize);
        end;
      end;
    end;
    
    end.

  3. #18
    Silly member NGLN's Avatar
    Join Date
    Aug 2004
    Location
    Werkendam
    Posts
    5,133
    Je hebt een WndProc-hook nodig zoals NLDMessageBox dat doet. Zie http://svn.nldelphi.com/nldelphi/ope...nldmessagebox/
    (Sender as TNLDUser).Signature := 'Groeten van Albert';

  4. #19
    De WndProc-hook is dat niet dezelfde als dat ik hierboven heb staan?

    Als ik met de functie GetWindowPlacement de positie van het dialog teruglees na de setWindowPos dan staat deze op het center van de form. Echter het dialog komt toch echt op een andere positie te staan. Zou het kunnen dat windows na de init message nog een andere message stuurt waardoor de dialog van positie veranderd? Zo ja hoe zou je z'n message kunnen bekijken / afvangen?

  5. #20
    Silly member NGLN's Avatar
    Join Date
    Aug 2004
    Location
    Werkendam
    Posts
    5,133
    Nee, ik bedoel een 'echte' hook. Zie verderop; want dit is toch wel een interessant probleem...

    Ter info, de messages die jouw overriden WndProc passeren zijn, in deze volgorde:

    WM_INITDIALOG
    WM_SHOWWINDOW
    WM_WINDOWPOSCHANGING
    WM_NCCALCSIZE
    WM_CHILDACTIVATE
    WM_SIZE
    WM_WINDOWPOSCHANGED
    WM_NOTIFY
    WM_SETREDRAW
    WM_SETREDRAW
    WM_NOTIFY
    WM_UPDATEUISTATE
    WM_NOTIFY
    WM_DESTROY
    WM_NCDESTROY


    Ik had even over het hoofd gezien dat je bij een TCommonDialog al een Handle beschikbaar hebt, dus mijn vorige post was iets te snel geroepen. Maar hoe ik het ook probeer, ik loop tegen dezelfde problemen aan als jullie.

    Afijn, ik heb het op de volgende manier toch klaargekregen:
    Delphi Code:
    1. type
    2.   TSaveDialog = class(Dialogs.TSaveDialog)
    3.   private
    4.     FHooking: Boolean;
    5.     FHookProc: Pointer;
    6.     FParent: HWND;
    7.     FWndHook: HHOOK;
    8.     procedure HookProc(var Message: THookMessage);
    9.     procedure SetParent(const Value: TWinControl);
    10.   protected
    11.     function TaskModalDialog(DialogFunc: Pointer;
    12.       var DialogData): LongBool; override;
    13.   public
    14.     function Execute: Boolean; override;
    15.     property Parent: TWinControl write SetParent;
    16.   end;
    17.  
    18. procedure TForm1.Button1Click(Sender: TObject);
    19. begin
    20.   SaveDialog1.Parent := Self;
    21.   SaveDialog1.Execute;
    22. end;
    23.  
    24. { TSaveDialog }
    25.  
    26. function TSaveDialog.Execute: Boolean;
    27. begin
    28.   try
    29.     try
    30.       FHookProc := MakeHookInstance(HookProc);
    31.       FWndHook := SetWindowsHookEx(WH_CALLWNDPROCRET, FHookProc, 0,
    32.         GetCurrentThreadID);
    33.       Result := inherited Execute;
    34.     finally
    35.       if FWndHook <> 0 then
    36.         UnhookWindowsHookEx(FWndHook);
    37.       if FHookProc <> nil then
    38.         FreeHookInstance(FHookProc);
    39.     end;
    40.   except
    41.     Result := False;
    42.   end;
    43. end;
    44.  
    45. procedure TSaveDialog.HookProc(var Message: THookMessage);
    46. var
    47.   Data: PCWPRetStruct;
    48.   Title: array[0..MAX_PATH] of Char;
    49.   ParentRect: TRect;
    50.   Rect: TRect;
    51.   X: Integer;
    52.   Y: Integer;
    53. begin
    54.   if (not FHooking) and (Message.nCode = HC_ACTION) then
    55.   begin
    56.     Data := PCWPRetStruct(Message.lParam);
    57.     if Data.message = WM_WINDOWPOSCHANGED then
    58.     begin
    59.       GetWindowText(Data.hwnd, @Title, SizeOf(Title));
    60.       if String(Title) = 'Opslaan als' then
    61.       begin
    62.         FHooking := True;
    63.         GetWindowRect(FParent, ParentRect);
    64.         GetWindowRect(Data.hwnd, Rect);
    65.         with ParentRect do
    66.         begin
    67.           X := ((Right - Left - Rect.Right + Rect.Left) div 2) + Left;
    68.           Y := ((Bottom - Top - Rect.Bottom + Rect.Top) div 2) + Top;
    69.         end;
    70.         SetWindowPos(Data.hwnd, 0, X, Y, 0, 0, SWP_NOACTIVATE or
    71.           SWP_NOOWNERZORDER or SWP_NOSIZE or SWP_NOZORDER);
    72.       end;
    73.     end;
    74.   end;
    75.   with Message do
    76.     if nCode < 0 then
    77.       Result := CallNextHookEx(FWndHook, nCode, wParam, lParam)
    78.     else
    79.       Result := 0;
    80.   if FHooking then
    81.   begin
    82.     UnhookWindowsHookEx(FWndHook);
    83.     FWndHook := 0;
    84.     FreeHookInstance(FHookProc);
    85.     FHookProc := nil;
    86.     FHooking := False;
    87.   end;
    88. end;
    89.  
    90. procedure TSaveDialog.SetParent(const Value: TWinControl);
    91. begin
    92.   if Value = nil then
    93.     FParent := GetDesktopWindow
    94.   else
    95.     FParent := Value.Handle;
    96. end;
    Zie NLDMessageBox voor de MakeHookInstance functie.
    Wat hierin natuurlijk bijzonder lelijk en onpraktisch is, is het identificeren van het Dialog a.d.h.v. zijn caption: 'Opslaan als'. Ik heb geprobeerd om dat op een andere manier te doen, door 'customer data' aan het Dialog toe te voegen:
    Delphi Code:
    1. function TSaveDialog.TaskModalDialog(DialogFunc: Pointer;
    2.   var DialogData): LongBool;
    3. begin
    4.   TOpenFilename(DialogData).lCustData := 34567;
    5.   Result := inherited TaskModalDialog(DialogFunc, DialogData);
    6. end;
    (moet natuurlijk een constante worden, is even om te testen)
    En daar kun je in HookProc dan op de volgende manier op testen:
    Delphi Code:
    1. if Data.message = WM_INITDIALOG then
    2.       if POpenFilename(Data.lParam).lCustData = 34567 then
    Maar de volgorde van de messages die van belang zijn gooit roet in het eten, want op deze manier heb je de WM_INITDIALOG te laat, of de WM_WINDOWPOSCHANGED te vroeg.

    Succes verder. Zal er 't weekend nog eens naar kijken.

    Mocht dit (ooit) gaan werken, dan kun je hier een mooie functie omheen wrappen:

    Delphi Code:
    1. function ExecuteCentered(Dialog: TCommonDialog): Boolean;
    (Sender as TNLDUser).Signature := 'Groeten van Albert';

  6. #21
    Silly member NGLN's Avatar
    Join Date
    Aug 2004
    Location
    Werkendam
    Posts
    5,133
    (kan niet in slaap komen)

    Hmm, het blijkt dat de identificatie van het Dialog niet al te moeilijk is: Data.hwnd is namelijk de Parent van TCommonDialog.Handle

    En dan wordt het uiteindelijk toch nog 'redelijk' eenvoudig:
    Delphi Code:
    1. unit AwDialogs;
    2.  
    3. interface
    4.  
    5. uses
    6.   Dialogs, Windows, Controls, Messages, CommDlg, AwHookInstance;
    7.  
    8. function ExecuteCentered(Dialog: TCommonDialog;
    9.   Center: TWinControl = nil): Boolean;
    10.  
    11. implementation
    12.  
    13. type
    14.   TAwCommonDialog = class(TObject)
    15.   private
    16.     FCenter: HWND;
    17.     FDialog: TCommonDialog;
    18.     FHooking: Boolean;
    19.     FHookProc: Pointer;
    20.     FWndHook: HHOOK;
    21.     procedure HookProc(var Message: THookMessage);
    22.     function Execute: Boolean;
    23.   end;
    24.  
    25. function ExecuteCentered(Dialog: TCommonDialog;
    26.   Center: TWinControl = nil): Boolean;
    27. begin
    28.   with TAwCommonDialog.Create do
    29.   try
    30.     if Center = nil then
    31.       FCenter := GetDesktopWindow
    32.     else
    33.       FCenter := Center.Handle;
    34.     FDialog := Dialog;
    35.     Result := Execute;
    36.   finally
    37.     Free;
    38.   end;
    39. end;
    40.  
    41. { TAwCommonDialog }
    42.  
    43. function TAwCommonDialog.Execute: Boolean;
    44. begin
    45.   try
    46.     FHookProc := MakeHookInstance(HookProc);
    47.     FWndHook := SetWindowsHookEx(WH_CALLWNDPROCRET, FHookProc, 0,
    48.       GetCurrentThreadID);
    49.     Result := FDialog.Execute;
    50.   finally
    51.     if FWndHook <> 0 then
    52.       UnhookWindowsHookEx(FWndHook);
    53.     if FHookProc <> nil then
    54.       FreeHookInstance(FHookProc);
    55.   end;
    56. end;
    57.  
    58. procedure TAwCommonDialog.HookProc(var Message: THookMessage);
    59. var
    60.   Data: PCWPRetStruct;
    61.   CenterRect: TRect;
    62.   Rect: TRect;
    63.   X: Integer;
    64.   Y: Integer;
    65. begin
    66.   if (not FHooking) and (Message.nCode = HC_ACTION) then
    67.   begin
    68.     Data := PCWPRetStruct(Message.lParam);
    69.     if (Data.hwnd = Cardinal(GetWindowLong(FDialog.Handle, GWL_HWNDPARENT))) and
    70.       (Data.message = WM_SHOWWINDOW) then
    71.     begin
    72.       FHooking := True;
    73.       GetWindowRect(FCenter, CenterRect);
    74.       GetWindowRect(Data.hwnd, Rect);
    75.       with CenterRect do
    76.       begin
    77.         X := (Right - Left - Rect.Right + Rect.Left) div 2 + Left;
    78.         Y := (Bottom - Top - Rect.Bottom + Rect.Top) div 2 + Top;
    79.       end;
    80.       SetWindowPos(Data.hwnd, 0, X, Y, 0, 0, SWP_NOACTIVATE or
    81.         SWP_NOOWNERZORDER or SWP_NOSIZE or SWP_NOZORDER);
    82.     end;
    83.   end;
    84.   with Message do
    85.     if nCode < 0 then
    86.       Result := CallNextHookEx(FWndHook, nCode, wParam, lParam)
    87.     else
    88.       Result := 0;
    89.   if FHooking then
    90.   begin
    91.     UnhookWindowsHookEx(FWndHook);
    92.     FWndHook := 0;
    93.     FreeHookInstance(FHookProc);
    94.     FHookProc := nil;
    95.   end;
    96. end;
    97.  
    98. end.
    (Sender as TNLDUser).Signature := 'Groeten van Albert';

  7. #22
    Senior Member
    Join Date
    Apr 2003
    Location
    Netherlands
    Posts
    376
    Eenvoudig zegt ie... :S Maar ik bewonder je vasthoudendheid... ik had gewoon "fuck it, dan niet" gezegd lol
    Iedereen heeft recht op mijn mening!
    "You're not thinking, you're merely being logical!"

  8. #23
    Alvast heel erg bedankt voor je pogingen om dit probleem neer te slaan!

    Ik probeer om jouw voorbeeld werkend te krijgen maar krijg wat onverwachte meldingen. Zo kent Delphi bijvoorbeeld geen THookMessage. Ik vermoed dat deze in de unit AwHookInstance zit maar die kan ik nergens vinden. Klopt dit?

    Heb je misschien een klein demo programmaatje hoe je dit nu moet aanroepen?

    Groet Anton

  9. #24
    Silly member NGLN's Avatar
    Join Date
    Aug 2004
    Location
    Werkendam
    Posts
    5,133
    O sorry. Zie http://svn.nldelphi.com/nldelphi/ope...nldmessagebox/, maar daar heet hij NLDMBHookInstance.pas. (Aw is mijn eigen prefix)

    En de demo code:
    Delphi Code:
    1. procedure TForm1.Button1Click(Sender: TObject);
    2. begin
    3.   if ExecuteCentered(OpenDialog1, Self) then
    4.     Caption := OpenDialog1.FileName;
    5. end;
    Hij werkt nu goed voor TOpenDialog, TSaveDialog, TOpenPictureDialog, TSavePictureDialog. Helaas de rest nog niet. T.b.c....
    Attached Files Attached Files
    Last edited by NGLN; 22-Jan-10 at 15:31.
    (Sender as TNLDUser).Signature := 'Groeten van Albert';

  10. #25
    Geen probleem ik ben al lang blij dat jij erin gedoken bent. Want het werkt nu perfect!!!

    Heel erg bedankt!!
    Last edited by antonbeerens; 22-Jan-10 at 21:39.

  11. #26
    Ik ben nu thuis en probeer het op mijn eigen pc uit en merk dat het hier niet werkt.

    Mijn pc heeft windows 7 - 64 bit dus blijkbaar moet daar nog wat meer gebeuren. Een probleem heb ik al gevonden en dat is dat je voor 64 bit GetWindowLongPtr moet gebruiken. Echter is dit niet alles. De handle die ik terugkrijg (FDialog.Handle) is altijd 0. Als ik een breakpoint zet na saveDialog := TSaveDialog.Create(self); zie ik inderdaad ook dat de saveDialog.handle 0 is. Hoe kan dit ik dacht dat deze na de create altijd een handle kreeg toegewezen.

    Iemand enig idee?

  12. #27
    Misschien dat de handle pas wordt toegekend bij het tonen van het dialog (omdat op dat moment misschien pas het venster wordt gemaakt). Zo ja, dan zou in de hookproc Data.Handle wel toegekend moeten zijn. Kun je daar een breakpoint zetten?
    1+1=b

  13. #28
    Dat klopt de Data.hwnd is niet nul, maar FDialog.Handle is wel steeds 0. Dit is omdat in de functie ExecuteCentered Dialog nog steeds 0 is. Ik moet eerlijk bekennen dat ik niet goed thuis ben in het hele windows message systeem dus dit slecht kan debuggen. Maar je moet iets hebben om te weten of het om om jouw dialog gaat dus lijkt de handle me een perfecte methode.

    Wat ik wel raar vind is dat het op de ene windows versie (XP 32 bits) wel perfect werkt en op een andere versie (win 7 64 bits) niet. Ik zou verwachten dat dit soort dingen generiek zouden zijn anders kun je bij elke versie gaan controleren of het wel werkt.

  14. #29
    Silly member NGLN's Avatar
    Join Date
    Aug 2004
    Location
    Werkendam
    Posts
    5,133
    Die handle is inderdaad alleen geldig tijdens Execute.

    Hierbij een demo met aangepaste versie van de functie die voor álle TCommonDialogs werkt: TFindDialog, TOpenDialog, TColorDialog, etc...

    Maar blijkbaar dus alleen voor 32-bit. Ik heb hier geen 64-bit, dus kan helaas niet testen.
    Attached Files Attached Files
    (Sender as TNLDUser).Signature := 'Groeten van Albert';

  15. #30
    Beter!! Ik heb je nieuwe versie getest op een windows 7 - 64 bit pc en kom tot het volgende:

    Dialog - designtime - runtime
    ColorDialog - werkt - werkt
    OpenDialog - faalt - faalt
    SaveDialog - faalt - faalt
    FontDialog - werkt - werkt
    PageSetup - werkt - werkt
    Find - werkt - werkt

    De open en save dialog komen niet in het midden van het form maar gebruiken wel elkaars laatste positie. Tevens zijn dit de enige twee dialogs die afwijken van de anderen in de zin dat het eigenlijk windows explorers zijn.

    In mijn test komt de opendialog niet voorbij de regel:
    "if (FDialog.Handle <> 0) and (Data.message = WM_SHOWWINDOW) then" omdat de handle 0 is. Ik snap echter niet wat dat met windows 7 of 64 bit te maken kan hebben en waarom alleen de open en save dialog dit probleem hebben.

Page 2 of 3 FirstFirst 1 2 3 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •