![]() |
Positionierung einer Form in Multimonitorumgebung
Hallo Leute,
hier ist schon wieder der ohne Lama :mrgreen:. Vorgestern ist mir eine Sache in meinen Total Commander Plugins aufgefallen: Die Positionierung der Formulare ist falsch, wenn sie in einer Multimonitorumgebung benutzt werden. Es werden alle davon auf dem linken Monitor plaziert, und zwar unabhängig davon, welcher der primäre Monitor ist. Interessanterweise trifft das nur auf das mit XE2 erstellte Kompilat zu. Die mit Delphi 5 erstellte DLL positioniert alle Forms immer auf dem aktiven Monitor, d.h. dort, wo sich Total Commander befindet. Nun hab ich mir natürlich die Eigenschaft TForm.DefaultMonitor angeschaut und damit ein bisschen rumgespielt, aber der Satz Zitat:
Frage: An welcher Schraube muss ich drehen, um eine Änderung der Formpositionierung zu erreichen? Geht das ohne manuelle Plazierung der Formulare oder muss ich wirklich alles von Hand machen? MfG Dalai |
AW: Positionierung einer Form in Multimonitorumgebung
Hallo...
Ich rate mal: :zwinker: Stelle mal die PlugInForm auf poMainFormCenter z.B. Ich tippe sie steht auf poDesigned. Da dein Plugin vom TotalCommander geladen wird sollte "MainForm" TotalCommander heißen. |
AW: Positionierung einer Form in Multimonitorumgebung
TForm.Position steht auf poOwnerFormCenter. poScreenCenter hab ich ebenfalls probiert, aber das ändert nichts. Auch poMainFormCenter bringt nichts, weil es keine MainForm gibt (Application.MainForm ist nicht zugewiesen, also vermutlich nil).
MfG Dalai |
AW: Positionierung einer Form in Multimonitorumgebung
Also ich merke mir immer die linke obere Ecke einer Form, deren Position ich speichern will, damit hatte ich noch nie Probleme. Wenn dein Haupt-Monitor eine horizontale Bildschirmauflösung von 1920 hat und der zweite rechts davon positioniert ist, dann ist das erste Pixel des zweiten Monitors in der Spalte 1920.
|
AW: Positionierung einer Form in Multimonitorumgebung
Tut mir leid, dass ich so lange nichts von mir hören ließ. Es war zuviel Arbeit, und da musste sich dieses Privatprojekt hinten anstellen.
Heute kam ich wieder dazu, mich damit zu beschäftigen. Zuerst eine Korrektur: Es ist wohl anders als ich bisher annahm, dass die Forms nicht immer auf dem linken Monitor dargestellt werden sondern immer auf dem primären; ich hoffe, das ist diesmal richtig, denn momentan ist nicht hundertprozentig sicher, wie die Anordnung auf dem einen Rechner war, an dem mir die Sache überhaupt erst aufgefallen war. Weiterhin ist es so, dass die Sache nicht nur dieses spezifische Projekt (Total Commander Plugin) betrifft sondern offenbar alle Delphi-Programme - nach einigen Tests mit meinen normalen Programmen erlaube ich mir, diese Behauptung aufzustellen. Das erste Form eines Delphi-Programms wird offenbar immer auf dem primären Monitor angezeigt. Soweit ist das ja auch in Ordnung. Aber bei Forms aus DLLs wird die Sache knifflig, vor allem dann, wenn man TApplication.MainForm und/oder TApplication.Handle nicht gesetzt hat - was in diesem Fall seine Gründe hat (für Hintergründe siehe ![]() Kommen wir zum Wesentlichen. Ich habe eine Möglichkeit gefunden, mein Ziel mit ein wenig Code zu erreichen. Hier ein Auszug (ohne die ganzen Ergänzungen für alte Delphis):
Delphi-Quellcode:
Hinweis: Der Wert von FhTotalCmd wird im Konstruktor gesetzt, der in obigem Code fehlt.
type
TTotalCmdWfxForm = class(TForm) private { Handle to TC's main window } FhTotalCmd : HWND; function GetMonitorOfParent(const AParent: HWND = 0): TMonitor; procedure CenterOnMonitor(const AMonitor: TMonitor); procedure FormActivate(Sender: TObject); public procedure Init; virtual; end; procedure TTotalCmdWfxForm.Init; begin Self.OnActivate:= Self.FormActivate; end; //------------------------------------------------------------------------------ function TTotalCmdWfxForm.GetMonitorOfParent(const AParent: HWND = 0): TMonitor; begin Result:= Screen.MonitorFromWindow(AParent, mdNearest); end; //------------------------------------------------------------------------------ procedure TTotalCmdWfxForm.CenterOnMonitor(const AMonitor: TMonitor); var Lrect: TRect; Lwidth, Lheight: integer; begin if AMonitor <> nil then begin Lrect:= AMonitor.WorkAreaRect; Lwidth:= Lrect.Width; Lheight:= Lrect.Height; Self.Left:= AMonitor.Left + ((Lwidth div 2) - (Self.Width div 2)); Self.Top:= AMonitor.Top + ((Lheight div 2) - (Self.Height div 2)); end; end; //------------------------------------------------------------------------------ procedure TTotalCmdWfxForm.FormActivate(Sender: TObject); var Lmon: TMonitor; begin if (Screen.MonitorCount > 1) then begin Lmon:= GetMonitorOfParent(FhTotalCmd); CenterOnMonitor(Lmon); end; end; Was mit daran überhaupt nicht gefällt, ist die Benutzung des OnActivate-Ereignisses. Dummerweise setzt die VCL irgendwann im OnShow (SetVisible) die Position der Form um, so dass irgendwelche vorherigen Änderungen überschrieben werden. Ich muss also nach OnShow ansetzen; da kommt aber nur noch OnActivate, oder liege ich da falsch? Hat dazu jemand Ideen? MfG Dalai |
AW: Positionierung einer Form in Multimonitorumgebung
Der richtige Zeitpunkt wäre wohl der erste Idle-Zustand nach dem OnShow
|
AW: Positionierung einer Form in Multimonitorumgebung
Wenn ich ein 'OnAfterShow' benötige, dann sende ich im Show eine Message per PostMessage an die Form selber und kann dann in
procedure WndProc(var Message: TMessage); override; darauf reagieren. Bei OnIdle ist das Problem, das ich mir merken muss, das es das erste OnIdle nach dem Show ist, da OnIdle immer wieder aufgerufen wird, wenn nichts zu tun ist. Durch das Postmessage am Ende von OnShow werden zunächst noch alle Windows-Messages verarbeitet und dann kommt erst deine eigene 'OnAfterShow', so dass Du sicher sein kannst, dass alle Messages beim Formularanzeigen, wie z.B. die korrekte Anzeige aller Controlls abgeschlossen ist. Ach.. Und bei OnActivate hatte ich die Probleme, das es manchmal gar nicht aufgerufen wurde oder wenn die Form mit Show statt ShowModal angezeigt wurde bei jedem Formularwechsel wieder aufgerufen wurde.. ;) |
AW: Positionierung einer Form in Multimonitorumgebung
Ich wollte eigentlich auf etwas anderes hinaus: Wenn ich mich an ein Event (OnActivate, OnShow) hänge, das von außen gesetzt werden kann - entweder im Objektinspektor oder im Code - wird der Code zur Plazierung des Forms nicht ausgeführt, und zwar ohne, dass es eine Warnung gibt oder sonstwas.
Daher habe ich etwas tiefer gegraben und bin fündig geworden:
Delphi-Quellcode:
Anscheinend ist es so (ausgehend von gezielt gesetzten Breakpoints auf das
TTotalCmdWfxForm = class(TForm)
private { Handle to TC's main window } FhTotalCmd : HWND; { Has this form been centered on the active screen? } FCentered : Boolean; function GetMonitorOfParent(const AParent: HWND = 0): TMonitor; procedure CenterOnMonitor(const AMonitor: TMonitor; procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED; end; procedure TTotalCmdWfxForm.CMShowingChanged(var Message: TMessage); var Lmon: TMonitor; begin inherited; if Self.Showing then begin if NOT FCentered then begin if (Screen.MonitorCount > 1) then begin Lmon:= GetMonitorOfParent(FhTotalCmd); CenterOnMonitor(Lmon); end; FCentered:= True; end; end; end;
Delphi-Quellcode:
und die nachfolgende Zeile), dass die Nachricht einerseits zum richtigen Zeitpunkt und andererseits selten genug gesendet wird, so dass es nicht stört.
inherited;
Offenbar ist es auf diese Weise möglich, in der Basisklasse privat deklarierte Messages zu überschreiben. Sofern es keine (begründeten) Einwände gegen diese Variante gibt, werde ich es dabei belassen. Danke auf jeden Fall an alle Beteiligten! :thumb: MfG Dalai |
AW: Positionierung einer Form in Multimonitorumgebung
Weil es bei mir dasselbe Problem gibt, habe ich das aus dem letzten Beitrag mal ausprobiert, bekomme aber Fehlermeldungen.
procedure CenterOnMonitor(const AMonitor: TMonitor; Da fehlt die Klammer vor dem Semikolon. function GetMonitorOfParent(const AParent: HWND = 0): TMonitor; procedure CenterOnMonitor(const AMonitor: TMonitor); -> Ungenügende Forward- oder External-Deklaration. Habe ich irgendwas übersehen? Wenn ich das mit dem weiter vorne geposteten Quelltext kombiniere, gibt es andere Fehlermeldungen über fehlende "Bezeichner" : in "GetMonitorOfParent" : MonitorFromWindow, mdNearest ( Result:= Screen.MonitorFromWindow(AParent, mdNearest); ) in "CenterOnMonitor" : WorkAreaRect, width und height ( Lrect:= AMonitor.WorkAreaRect; Lwidth:= Lrect.Width; Lheight:= Lrect.Height; ) Könntest Du bitte mal einen kompletten Quellcode eines (ansonsten leeren) Basisformulars veröffentlichen, bei dem der TC das Formular wirklich im gerade aktiven Fenster startet ?:roll: Danke im Voraus.:thumb: Ach, ich sehe gerade bei dem älteren Beitrag "Hier ein Auszug (ohne die ganzen Ergänzungen für alte Delphis):" Wie sehen diese "ganzen Ergänzungen" denn für Delphi 5 aus ?:cyclops: |
AW: Positionierung einer Form in Multimonitorumgebung
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Zitat:
Benutzung der Klasse dann z.B. so:
Delphi-Quellcode:
Wenn du die Methode Init nicht brauchst, kannst du auch direkt von TFormEx ableiten (TTotalCmdWfxForm ist eh nur eine leere Hülle).
uses ..., TotalCmdGUI;
type TfmSvcProperties = class(TTotalCmdWfxForm) //[...] end; implementation procedure PluginShowForm(const AhParent: HWND); var fmSvcProperties: TfmSvcProperties; Lmon: TFormMonitor; begin fmSvcProperties:= TfmSvcProperties.Create(nil, AhParent); try case IniFile.Monitor of 0..4: Lmon:= TFormMonitor(IniFile.Monitor); else Lmon:= fmActive; end; fmSvcProperties.ShowModal(Lmon); finally FreeAndNil(fmSvcProperties); end; end; Zitat:
MfG Dalai |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:06 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz