Delphi 3. Библиотека программиста

       

Масштабирование форм


Мститель открыл новый пакет чипсов и набил рот. Он решил не возвращаться в контору Брейкпойнта. Это было рискованно, но скорее всего, ничего страшного не произойдет. Шансы на то, что бывший сыщик сможет найти случайно оставленную улику, близки к нулю. Лучше как можно быстрее впитать побольше информации.

Дневник №16, 25 марта. Меня часто интересовало, как некоторые приложения ограничивают минимальный размер масштабируемого окна. Я решил узнать, как это делается. Мне даже в голову не приходило, как это просто. Тем не менее я заподозрил, что это должно иметь какое-то отношение к сообщениям. К тому времени я уже понял, что все, происходящее в Windows, связано с сообщениями.

Кроме того, меня заинтриговала способность некоторых сложных форм сохранять гармонию расположения своих компонентов даже при масштаби ровании формы. Этот вопрос также вошел в программу сегодняшнего расследования.

Мне нужна была форма с четырьмя компонентами как минимум . Я создал набросок формы, содержащей оперативную кнопку (TSpeedButton), поле Memo и две обычные кнопки (см. рис. 15.1). Прежде всего я решил ограничить пределы масштабирования минимальным размером формы, который должен задаваться программистом. Решение скрывалось в сообщении WM_GETMINMAXINFO.

Рис. 15.1. Форма для демонстрации масштабирования, изображенная в режиме конструирования

С помощью сообщения WM_GETMINMAXINFO приложение узнает о том, что система проверяет размер окна, и имеет возможность изменить параметры,
принятые по умолчанию. Среди этих параметров — значения, определяющие интервалы, в которых должен находиться размер окна. По умолчанию минимальный размер совпадает с размером значка (icon), а максимальный — с размером всего экрана.

Фактический параметр, передаваемый обработчику WM_GETMINMAXINFO, представляет собой точку, которая определяет смещения XY (в пикселях) от левого верхнего угла окна.

Следовательно, мне потребуется написать свой обработчик и определить в нем точки, описывающие минимальный и максимальный размер окна. Прежде всего я объявил переменные для минимальной высоты и ширины, чтобы упростить манипуляции с этими величинами.

Затем потребовалось определить точку, представляющую правый нижний угол формы. Параметр lParam стандартного обработчика WM_GETMINMAXINFO является указателем на массив из пяти структур-точек. К счастью, волшебники из Borland предусмотрительно создали тип сообщения TWMGetMinMaxInfo, избавляющий вас от многих трудностей.



В листинге 15.1 приведен полный исходный текст программы, в которой я экспериментировал с масштабированием. Листинг содержит обработчик, получившийся после нескольких неудачных попыток (удивительно, какие «интересные» эффекты могут возникнуть, если забыть о некоторых мелочах — например, о вызове унаследованного обработчика). Как видно из листинга, через структуру MinMaxInfo можно получить быстрый и удобный доступ к точкам, определяемым ptMinTrackSize и ptMaxTrackSize. Я вставил в обработчик OnCreate формы небольшой фрагмент для вычисления MinWidth и MinHeight на основании размеров компонентов в момент запуска.

Листинг 15.1. Исходный текст программы для демонстрации
масштабирования формы

{——————————} {Масштабирование формы (демонстрационная программа) } RS.PAS : Главная форма } {Автор: Эйс Брейкпойнт, N.T.P. } {При содействии Дона Тейлора } { } {Приложение показывает, как с помощью панелей } { с заданным типом выравнивания и обработки } сообщений } { Windows создаются гибкие формы, которые } ограничивают } { возможности масштабирования и учитывают } { их последствия. } { Написано для *High Performance Delphi 3 } Programming* } { Copyright (c) 1997 The Coriolis Group, Inc.} { Дата последней редакции 23/4/97 } {————————} unit Rs; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons; type TRSMainForm = class(TForm) ControlPanel: TPanel; RSMemoPanel: TPanel; RSMemo: TMemo; BtnPanel: TPanel; SBPanel: TPanel; QuitSB: TSpeedButton; QuitBtn: TButton; SBComboPanel: TPanel; ComboBox1: TComboBox; SpeedButton1: TSpeedButton; Button1: TButton; procedure QuitBtnClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormResize(Sender: TObject); private { Private declarations } procedure WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo); message WM_GETMINMAXINFO; public { Public declarations } end; var RSMainForm: TRSMainForm; MinWidth : Integer; MinHeight : Integer; implementation {$R *.DFM} procedure TRSMainForm.QuitBtnClick(Sender: TObject); begin Close; end; procedure TRSMainForm.FormCreate(Sender: TObject); begin MinWidth := RSMemoPanel.Width + BtnPanel.Width + 10; MinHeight := RSMainForm.Height - (RSMainForm.ClientHeight - (RSMemo.Top + RSMemo.Height)) + 10; end; procedure TRSMainForm.WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo); begin inherited; with Msg.MinMaxInfo^ do begin with ptMinTrackSize do begin X := MinWidth; Y := MinHeight; end; { with } with ptMaxTrackSize do begin X := Screen.Width; Y := Screen.Height; end; { with } end; { with } end; procedure TRSMainForm.FormResize(Sender: TObject); begin RSMemo.Height := RSMemoPanel.Height - (2 * RSMemo.Top); RSMemoPanel.Width := RSMainForm.ClientWidth - BtnPanel.Width; RSMemo.Width := RSMemoPanel.Width - (2 * RSMemo.Left); end; end.

Настало время поиграть с взаимным расположением компонентов, чтобы оно сохранялось даже при масштабировании формы, на которой они находятся.

Когда я только начинал работать с панелями, они приносили мне немало хлопот. Но, познакомившись с ними поближе, я просто влюбился в три замечательные возможности, которыми они обладают. Во-первых, с помощью свойства Alignment можно установить абсолютную связь панели с родитель ским объектом; например, если задать свойству Alignment значение alTop, панель будет занимать всю верхнюю часть формы, на которой она находится. Во-вторых, положение прочих компонентов (полей Memo, кнопок и т. д.), находящихся на панели, остается фиксированным по отношению к панели, пока ее размеры остаются прежними. Наконец, панель, расположенная на другой панели, ведет себя так же, как и панель, находящаяся на форме: например, если свойство Alignment имеет значение alBottom, внутренняя панель «приклеивается» к нижней части внешней панели и занимает всю ее ширину.

Такое поведение и позволяет сохранять общий вид формы при масштаби ровании. Создавая форму, изображенную на рис. 15.1, я преследовал несколько целей:

панель Panel3 должна иметь фиксированную высоту и занимать всю верхнюю часть формы, чтобы ее ширина всегда совпадала с шириной самой формы;
оперативная кнопка SB1 должна оставаться в фиксированном положении по отношению к левому краю Panel3 (и, как следствие, к левому верхнему углу окна);
панель Panel4 должна выравниваться по правому краю Panel3, чтобы при изменении размера Panel3 она сохраняла постоянный размер, но следовала за правым краем Panel3;
панель Panel5 (содержащая поле Memo1) должна выравниваться по левой стороне формы, а ее высота должна зависеть от высоты формы;
панель Panel1 (содержащая панель Panel2) должна выравниваться по правому краю формы, а ее высота должна зависеть от высоты формы;
панель Panel2 (содержащая кнопки Button1 и Button2) должна выравнивать ся по нижнему краю Panel1, чтобы при масштабировании она сохраняла постоянный размер и следовала за нижним краем Panel1 (а следовательно, и всей формы);
кнопки Button1 и Button2 должны находиться в фиксированных позициях панели Panel2Panel2, чтобы сохранялось их положение по отношению к нижнему и правому краю формы.

Уф! Мне пришлось потрудиться, задавая свойства разных панелей. Работа с панелями может вызвать некоторые трудности, пока вы не усвоите «правила хорошего тона». Значения alTop и alBottom свойства Alignment всегда имеют более высокий приоритет по сравнению с alLeft и alRight. В конце концов для Panel3 я задал значение alTop, для Panel1 и Panel4 — значение alRight, для Panel5 — alLeft, а для Panel2 — alBottom. Свойствам BevelOuter панелей Panel4 и Panel2 были присвоены значения bvNone, чтобы они «исчезли» и не выделялись на форме. Для панелей Panel3 и Panel4 был выбран цвет clGray, это позволило наглядно отделить их от других компонентов. Кроме того, я поместил на Panel4 комбини рованное поле и оперативную кнопку, чтобы убедиться в сохранении их положения. Наконец, я переименовал панели и убрал их заголовки.

Я решил, что ширина внешней панели с кнопками (ранее называвшейся Panel1)останется прежней, а панели с полем Memo нужно позволить заполнять оставшуюся часть формы. Кроме того, я автоматически изменяю размеры поля Memo1, чтобы оно занимало всю площадь внешней панели, оставляя лишь небольшие поля с каждого края. Мне удалось проделать это с помощью простых вычислений в обработчике OnResize формы.

После нескольких попыток все заработало, как надо. На рис. 15.2 изображена демонстрационная форма при запуске программы; на рис. 15.3 показано, как выглядит та же форма после масштабирования.

Конец записи (25 марта).

Рис. 15.2. Форма при запуске программы

Рис. 15.3. Та же форма после масштабирования



Содержание раздела