這是個困惑我很長時間的問題,到今天終于得到解決了。
話說Delphi有個很強的窗體設計器,這一點讓VC粉絲垂涎三尺而不可得。但是,Delphi里設計的窗體并沒有自動適應屏幕分辨率的屬性,也就是說,軟件設計時調整完美的窗體控件布局,在不同屏幕分辨率的機器上運行時可能會變得面目全非??丶g會相互移位,有的甚至移出窗體再也找不到了。
這個問題在網上搜索過多次,但大都依據(jù)控件方法ScaleBy或者ChangeScale。采用這兩個方法進行自適應調整,我自己都試過,但效果并不理想。后來我自己也寫了一個繼承自窗體的基類,覆蓋構造函數(shù),調用自己的一個設備分辨率自適應方法,該方法遍歷窗體上所有控件,并按照設計時的屏幕分辨率和當前屏幕分辨率的比值,逐一計算控件的位置和尺寸。這個想法是不錯,效果也是有的,比單純的采用ScaleBy或者ChangeScale方法要好,但也不是非常理想,沒有達到自己設想的要求。原因在哪里,一直不知道。
我原來的代碼曾經發(fā)布在Delphi盒子和CSDN上。
這個問題今天終于得以徹底解決了??!
原因是,我原以為將所有控件的Align屬性設為alnone,Anchors屬性設為空[],控件位置和尺寸就不會受其容器尺寸改變的影響。今天我在設計期對此進行試驗時,發(fā)現(xiàn)不是這樣。當窗體大小改變的時候,即使某個控件的Align:=alNone,Anchors:=[],它依然會隨著窗體尺度的變化而變化。這意味著我需要一個數(shù)組事先保存所有控件的原始位置和尺寸。在窗體因為屏幕分辨率的改變而自動調整時,計算的依據(jù)依然是不變的原始窗體位置尺寸數(shù)據(jù),這樣問題就解決了。
閑話少說,上源碼。
unit uMyClassHelpers;
interface
Uses
SysUtils,Windows,Classes,Graphics, Controls,Forms,Dialogs,
uMySysUtils;
Const //記錄設計時的屏幕分辨率
OriWidth=1366; OriHeight=768;
Type
TfmForm=Class(TForm) //實現(xiàn)窗體屏幕分辨率的自動調整 Private
fScrResolutionRateW: Double; fScrResolutionRateH: Double;
fIsFitDeviceDone: Boolean; fPosition:Array of TRect; procedure
FitDeviceResolution; Protected Property IsFitDeviceDone:Boolean Read
fIsFitDeviceDone; Property ScrResolutionRateH:Double Read
fScrResolutionRateH; Property ScrResolutionRateW:Double Read
fScrResolutionRateW; Public Constructor Create(AOwner: TComponent);
Override; End;
TfdForm=Class(TfmForm) //增加對話框窗體的修改確認 Protected
fIsDlgChange:Boolean; Public Constructor Create(AOwner: TComponent);
Override; Property IsDlgChange:Boolean Read fIsDlgChange default
false; End;
implementation
Constructor TfmForm.Create(AOwner: TComponent); begin Inherited
Create(AOwner); fScrResolutionRateH:=1; fScrResolutionRateW:=1;
Try if Not fIsFitDeviceDone then Begin
FitDeviceResolution; fIsFitDeviceDone:=True; End;
Except fIsFitDeviceDone:=False; End; end;
procedure TfmForm.FitDeviceResolution; Var i:Integer;
LocList:TList; LocFontSize:Integer; LocFont:TFont;
LocCmp:TComponent; LocFontRate:Double; LocRect:TRect;
LocCtl:TControl; begin LocList:=TList.Create; Try
Try if
(Screen.width<>OriWidth)OR(Screen.Height<>OriHeight) then
begin Self.Scaled:=False;
fScrResolutionRateH:=screen.height/OriHeight;
fScrResolutionRateW:=screen.Width/OriWidth; Try if
fScrResolutionRateH<fScrResolutionRateW then
LocFontRate:=fScrResolutionRateH Else
LocFontRate:=fScrResolutionRateW; Finally ReleaseDC(0,
GetDc(0)); End;
For i:=Self.ComponentCount-1 Downto 0 Do
Begin LocCmp:=Self.Components[i]; If LocCmp Is
TControl Then LocList.Add(LocCmp); If
PropertyExists(LocCmp,'FONT') Then Begin
LocFont:=TFont(GetObjectProperty(LocCmp,'FONT')); LocFontSize :=
Round(LocFontRate*LocFont.Size);
LocFont.Size:=LocFontSize; End; End;
SetLength(fPosition,LocList.Count+1); For i:=0 to
LocList.Count-1 Do With TControl(LocList.Items[i])Do
fPosition[i+1]:=BoundsRect; fPosition[0]:=Self.BoundsRect;
With LocRect Do begin
Left:=Round(fPosition[0].Left*fScrResolutionRateW);
Right:=Round(fPosition[0].Right*fScrResolutionRateW);
Top:=Round(fPosition[0].Top*fScrResolutionRateH);
Bottom:=Round(fPosition[0].Bottom*fScrResolutionRateH);
Self.SetBounds(Left,Top,Right-Left,Bottom-Top); end;
i:= LocList.Count-1; While (i>=0) Do
Begin LocCtl:=TControl(LocList.Items[i]); If
LocCtl.Align=alClient Then begin
Dec(i); Continue; end; With LocRect
Do begin
Left:=Round(fPosition[i+1].Left*fScrResolutionRateW);
Right:=Round(fPosition[i+1].Right*fScrResolutionRateW);
Top:=Round(fPosition[i+1].Top*fScrResolutionRateH);
Bottom:=Round(fPosition[i+1].Bottom*fScrResolutionRateH);
LocCtl.SetBounds(Left,Top,Right-Left,Bottom-Top); end;
Dec(i); End; End;
Except on E:Exception Do Raise
Exception.Create('進行屏幕分辨率自適應調整時出現(xiàn)錯誤'+E.Message); End; Finally
LocList.Free; End; end;
{ TfdForm }
constructor TfdForm.Create(AOwner: TComponent); begin inherited;
fIsDlgChange:=False; end;
end.
上面包括兩個類,一個是普通窗體類,一個是其子類對話框型窗體類。在實際應用過程中只要自己創(chuàng)建的窗體類繼承自以上兩個類中的一個,例如 TForm1 =
class(TfdForm),則不需添加任何源碼,設計出窗體會自動調整其上控件的尺寸,以適應不同的屏幕分辨率。
以上源碼經過驗證,效果非常好,解決了一個多年未決的問題!
|