Avatar billede hugopedersen Nybegynder
14. juli 2010 - 12:52 Der er 18 kommentarer og
1 løsning

Flytte form på 2 skærme

Jeg plejer at bruge nedenstående kode til at håndtere at brugere ikke flytter en form uden form skærmen.
Men nu er jeg blevet gjort opmærksom på et problem hvis man har 2 skærme:  det forhindrer også at man flytter formen til en anden skærm.

Nogen forslag til hvad jeg kan rette for at få den kringlet?
Jeg har dog ikke lige selv mulighed for at teste for nuværende da jeg kun har 1 skærm tilsluttet.

procedure TfrmMain.WMMoving(var message : TWMMoving);
var
  rec: ^TRect;
  wrk: TRect;
begin
  SystemParametersInfo(spi_getworkarea, 0, @wrk, 0);
  rec := Pointer(message.DragRect);
  if rec^.Left < wrk.Left then
    begin
      rec^.Right := rec^.Right - (rec^.Left - wrk.Left);
      rec^.Left := wrk.Left;
    end
  else if rec^.Right > wrk.Right then
    begin
      rec^.Left := rec^.Left - (rec^.Right - wrk.Right);
      rec^.Right := wrk.Right;
    end;
  if rec^.Top < wrk.Top then
    begin
      rec^.Bottom := rec^.Bottom - (rec^.Top - wrk.Top);
      rec^.Top := wrk.Top;
    end
  else if rec^.Bottom > wrk.Bottom then
    begin
      rec^.Top := rec^.Top - (rec^.Bottom - wrk.Bottom);
      rec^.Bottom := wrk.Bottom;
    end;
end;
Avatar billede martinlind Nybegynder
14. juli 2010 - 13:18 #1
Hmm... det må alt andet lige være noget med

SystemParametersInfo(spi_getworkarea, 0, @wrk, 0);

der ikke får data på skræm to :-)
Avatar billede hugopedersen Nybegynder
15. juli 2010 - 06:52 #2
Det lyder meget plausibelt.
Avatar billede mbsnet Nybegynder
17. juli 2010 - 12:55 #3
prøv med...:

function getWorkSpaceRect:tRect;
begin
result.left:=0;result.right:=getSystemMetrics(78);
result.top:=0;result.bottom:=getSystemMetrics(79);
offsetRect(result,getSystemMetrics(76),getSystemMetrics(77))
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
memo1.lines.clear;
with getWorkSpaceRect do begin
  memo1.lines.add('x1: '+intToStr(left));
  memo1.lines.add('y1: '+intToStr(top));
  memo1.lines.add('x2: '+intToStr(right));
  memo1.lines.add('y2: '+intToStr(bottom));
end
end;
Avatar billede hugopedersen Nybegynder
17. juli 2010 - 20:30 #4
Desværre har jeg ikke mulighed for at afprøve dit forslag før jeg vender tilbage efter ferie da jeg ikke er udstyret med mere end 1 skærm her hjemme.
Avatar billede mbsnet Nybegynder
02. august 2010 - 12:27 #5
ok, er nu ret sikker på den skulle fungere...
Avatar billede hugopedersen Nybegynder
03. august 2010 - 09:46 #6
Jeg har prøvet koden på 1 skærm og det ser ganske rigtigt ud til at virke. Men der er dog den lille catch at formen kan flyttes under taskbaren - det kan den ikke med den 'orginale' opskrift.
Avatar billede mbsnet Nybegynder
03. august 2010 - 15:19 #7
Ok, kan godt se hvad du mener :-/ Der er også "screen.workAreaRect" men virker kun på den aktive skærm (virker dog med taskbar).

I forbindelse med Multiple Monitors, bør man nok loope "screen.monitors[]" via "screen.monitorCount", men prøv at hænge på et par dage mere....
Avatar billede mbsnet Nybegynder
04. august 2010 - 22:48 #8
Har lavet en unit som kan styre en enkelt form på en bestemt måde...
Den kan kun bruges hvis formen ikke må fylde mere end 1 skærm af gangen... Den snapper formen til den skærm, som formen mest overlapper, tager også højde for taskbar (lidt)...

Hvis du ikke kan bruge det, kan du måske bruge rutinen til at finde taskbar'ens dimensioner...

//mbs

UNIT
unit multiMonCtrl;

interface

//MULTIPLE MONITOR CONTROL (for a single form)
//Keep form within multiple monitors screen boundaries.
//Works with monitors of different sizes and positions.
//IMPORTANT: Use only if form should stay at one screen of the time.

//HOW TO USE
//Simply add this unit into the form's uses-clause,
//and set reference to it in "FormCreate" like: mMonCtrl.ctrl:=self;

//Last updated: Aug 2010, mortenbs.com/it/delphi/multiple_monitors/

uses
windows,mbs,classes,controls;

type
//-----------------------------|----------------|----------------------|----------------------------
tMultiMonCtrl=class(tThread)
private
protected
  procedure execute;override;
public
  ctrl                        :tWinControl;    //The assigned form to manage.
  constructor create;reintroduce;
end;
//-----------------------------|----------------|----------------------|----------------------------

function taskBarRect(var r:tRect):boolean;
function findMostMonitor(aCtrl:tControl;out aMonitor:integer):boolean;
procedure keepFormWithinMostMonitor(aCtrl:tControl);
function mMonCtrl:tMultiMonCtrl;

implementation

uses
forms;

function taskBarRect(var r:tRect):boolean;
var h:hWnd;
begin h:=findWindow('Shell_TrayWnd',nil);
result:=h>0;if result then windows.getWindowRect(h,r)
end;

function findMostMonitor(aCtrl:tControl;out aMonitor:integer):boolean;
var i,x1,y1,x2,y2,w,h,sq,lSq:integer;
begin aMonitor:=-1;lSq:=0;
for i:=0 to screen.monitorCount-1 do with screen.monitors[i] do begin
  x1:=aCtrl.left;y1:=aCtrl.top;x2:=x1+aCtrl.width;y2:=y1+aCtrl.height;w:=0;h:=0;
if (x2>=left) and (x1<=left+width) and (y2>=top) and (y1<=top+height) then begin
  w:=x2-x1;if x1<left then dec(w,left-x1) else if x2>left+width then dec(w,x2-(left+width));
  h:=y2-y1;if y1<top then dec(h,top-y1) else if y2>top+height then dec(h,y2-(top+height));
  sq:=w*h;if sq>lSq then begin aMonitor:=i;lSq:=sq end
  end
end;result:=aMonitor>-1
end;

procedure keepFormWithinMostMonitor(aCtrl:tControl);
var aMonitor:integer;r:tRect;
begin
if findMostMonitor(aCtrl,aMonitor) then with screen.monitors[aMonitor] do begin
  //MONITOR
  if aCtrl.left+aCtrl.width>left+width then aCtrl.left:=(left+width)-aCtrl.width;if aCtrl.left<left then aCtrl.left:=left;
  if aCtrl.top+aCtrl.height>top+height then aCtrl.top:=(top+height)-aCtrl.height;if aCtrl.top<top then aCtrl.top:=top;
  //TASKBAR
  if taskBarRect(r) and (r.left>=left) and (r.right<=left+width) and (r.top>=top) and (r.bottom<=top+height) then
  if (r.top>screen.height div 2) then begin//LOWER TASKBAR
    if aCtrl.top+aCtrl.height>r.top then aCtrl.top:=r.top-aCtrl.height;
    if aCtrl.top<top then aCtrl.top:=top;
  end else //UPPER TASKBAR
    if aCtrl.top<r.top then aCtrl.top:=r.top;
end
end;

//--------------------------------------------------------------------------------------------------
//tMultiMonCtrl:

constructor tMultiMonCtrl.create;//reintroduce;
begin inherited create(true);freeOnTerminate:=true;
ctrl:=application.mainForm;resume
end;

procedure tMultiMonCtrl.execute;//override;
begin
while not terminated do begin
  if ctrl<>nil then try keepFormWithinMostMonitor(ctrl) except end;
  sleep(250)
end
end;

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

var
fMultiMonCtrl:tMultiMonCtrl=nil;

function mMonCtrl:tMultiMonCtrl;
begin
if fMultiMonCtrl=nil then fMultiMonCtrl:=tMultiMonCtrl.create;
result:=fMultiMonCtrl
end;

initialization
mMonCtrl.resume;

finalization
if fMultiMonCtrl<>nil then begin
  if not fMultiMonCtrl.terminated then fMultiMonCtrl.terminate;
  fMultiMonCtrl:=nil
end;

end.


EKSEMPEL
unit Unit1;

interface

uses
  Windows, multiMonCtrl, Classes, Controls, Forms, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
mMonCtrl.ctrl:=self
end;

end.
Avatar billede mbsnet Nybegynder
04. august 2010 - 22:50 #9
p.s. fjern lige "mbs" fra uses... :-/
Avatar billede hugopedersen Nybegynder
10. august 2010 - 15:17 #10
Hov - den har jeg da ikke fået besked om at du har lagt op.
Den vil jeg prøve at se på i morgen tidlig.
Avatar billede hugopedersen Nybegynder
11. august 2010 - 11:52 #11
Kan ikke lige gennemskue om jeg kan bruge det da jeg på en maskine med bare 1 skærm kan flytte formen ud over kanten - og det må den ikke kunne.
Avatar billede mbsnet Nybegynder
18. august 2010 - 21:03 #12
Det skal nok bygges sammen med "din"
procedure TfrmMain.WMMoving(var message : TWMMoving);
for at køre mere som ønsket.

Som jeg ser det, vil det i visse situationer forekomme at formen rager ud over indre kanter (hvis skærmstørrelserne afveksler i højde osv. /og hvordan de er placeret i forhold til hinanden).

Eksemplet jeg gav, tager højde for dette, men tillader dog ganske kort tid at formen er udenfor, og snapper så lidt "senere"...

...Men det skulle være muligt at formen holdes inden for de ydre kanter.

Er selv ret booket op i øjeblikket, men vil gerne prøve at se på det igen, hvis det ikke haster helt vildt..
Avatar billede hugopedersen Nybegynder
19. august 2010 - 06:52 #13
No problem - det er ikke en funktion der er mission critical for mig :-)
Avatar billede mbsnet Nybegynder
07. september 2010 - 06:46 #14
Hej igen. Så fik jeg kigget lidt på sagen igen, og er kommet frem til følgende:

unit Unit1;

interface

uses
  windows, messages, classes, controls, graphics, forms;

type
  TForm1 = class(TForm)
  private
    procedure wmMoving(var aMsg:tWmMoving);message WM_MOVING;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
DI_LEFT  = $01;
DI_RIGHT  = $02;
DI_TOP    = $03;
DI_BOTTOM = $04;

function findMostMonitor(x1,y1,x2,y2:integer;out aMon:integer;out r:tRect):boolean;
var i,w,h,sq,lSq:integer;
begin aMon:=-1;lSq:=0;
for i:=0 to screen.monitorCount-1 do with screen.monitors[i] do
  if (x2>=left) and (x1<=left+width) and (y2>=top) and (y1<=top+height) then begin
  w:=x2-x1;if x1<left then dec(w,left-x1) else if x2>left+width then dec(w,x2-(left+width));
  h:=y2-y1;if y1<top then dec(h,top-y1) else if y2>top+height then dec(h,y2-(top+height));
  sq:=w*h;if sq>lSq then begin aMon:=i;lSq:=sq end
  end;result:=aMon>-1;if result then r:=screen.monitors[aMon].workareaRect
end;

function hasMonitorAt(aDir:byte;aMon:integer;p,mP:pRect):boolean;
var i:integer;
begin result:=true;with screen do
for i:=0 to monitorCount-1 do if i<>aMon then with monitors[i].workareaRect do case aDir of
  DI_LEFT:  if (right<=mP^.left) and (top<=p.top) and (bottom>=p.bottom) then exit;
  DI_RIGHT: if (left>=mP^.right) and (top<=p.top) and (bottom>=p.bottom) then exit;
  DI_TOP:  if (bottom<=mP^.top) and (left<=p.left) and (right>=p.right) then exit;
  DI_BOTTOM:if (top>=mP^.bottom) and (left<=p.left) and (right>=p.right) then exit;
end;result:=false
end;

procedure TForm1.wmMoving(var aMsg:tWmMoving);
var aMon:integer;p:pRect;r:tRect;
begin
if not findMostMonitor(left,top,left+width,top+height,aMon,r) then exit;p:=aMsg.dragRect;
with p^ do begin
  if (left<r.left) and not hasMonitorAt(DI_LEFT,aMon,p,@r) then begin dec(right,left-r.left);left:=r.left end else
  if (right>r.right) and not hasMonitorAt(DI_RIGHT,aMon,p,@r) then begin dec(left,right-r.right);right:=r.right end;
  if (top<r.top) and not hasMonitorAt(DI_TOP,aMon,p,@r) then begin dec(bottom,top-r.top);top:=r.top end else
  if (bottom>r.bottom) and not hasMonitorAt(DI_BOTTOM,aMon,p,@r) then begin dec(top,bottom-r.bottom);bottom:=r.bottom end
end
end;


end.
Avatar billede hugopedersen Nybegynder
13. september 2010 - 09:47 #15
Har desværre først haft tid til at se på det nu, men jeg kan ikke lige få det til at spille.

Har du det eksempel du har leget med som jeg evt. må få en kopi af?

oz8hp  snabelting  hotmail  punktum  com
Avatar billede mbsnet Nybegynder
13. september 2010 - 10:20 #16
Har ikke nogen udgående post server lige PT (profiber tilbyder ikke) men har forsøgt at tilføje dig til msn på den adresse du har oplyst på dit site (uden held). Men her er et midlertidigt link til projektet. http://www.mortenbs.com/it/delphi/move_example.zip

Denne gang har jeg ellers taget udgangspunkt i dit eget eksempel, hvor den nu undersøger, om der er "hul" til en parallel skærm ved de fire retninger... Har ikke testet med en enkelt skærm, men burde også fungere...
Avatar billede hugopedersen Nybegynder
13. september 2010 - 10:26 #17
På Messenger skal du også bruge

oz8hp  snabelting  hotmail  punktum  com
Avatar billede hugopedersen Nybegynder
13. september 2010 - 10:36 #18
Smid bare lige et svar her mbs
Avatar billede mbsnet Nybegynder
13. september 2010 - 10:51 #19
ok
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester