Владимир Коднянко дата публикации 16-09-2004 11:23 Улучшение вспомогательных окон среды DelphiВ практике программирования в среде часто приходится пользоваться вспомогательными окнами,
в которых необходимо вывести сообщение - однострочное или многострочное или задать вопрос (также однострочный или многострочный)
с тем, чтобы получить от пользователя программы ответ, который необходим для разрешения какой-либо ситуации.
Задача эта простая и даже для малоопытного программиста не представляет особых затруднений: можно использовать процедуру
ShowMessage, функцию MessageDlgPos стандартного модуля Dialogs.pas или подобные им подпрограммы. Однако есть несколько "но":
- для ускорения программирования или отладки программы обычно возникает потребность в том, чтобы с наименьшими затратами времени программировать вывод констант и значений переменных наиболее часто используемых типов (обычно строковых и числовых) с помощью одной или нескольких "подручных" подпрограмм, не тратя время на конвертацию из одного типа в другой (чаще строковый); для большинства случаев это можно сделать воспользовавшись, например, типом Variant;
- использование стандартных подпрограмм, например ShowMessage, иногда не удовлетворяет программиста по той причине, что это окно всегда выводится в центре экрана, и если окно приложения находится в этот момент не в центре, а в каком-нибудь углу экрана, то такое расположение окон нежелательно; можно, конечно, воспользоваться другой подпрограммой, позволяющей позиционировать окно где угодно, но "угадать", где в данный момент находится активное окно, обычными средствами непросто; наиболее приемлемой можно считать ситуацию, когда окно вопроса или сообщения имеет общий центр с активной формой, однако "не теряется" за пределами экрана если в большом окне активной формы ее центр находится вне экрана;
- площадь стандартных окон достаточно велика из-за неоправданно низкого расположения рисунка и кнопок в окне, а также довольно большого расстояния от кнопок до нижнего края окна; можно также улучшить вывод надписи на метке, позиционируя ее по отношению к рисунку в зависимости от числа строк на метке; такие изменения позволят, во-первых, уменьшить высоту окна и, во-вторых, улучшить расположение надписи на нем;
- если на компьютер устанавливается Delphi (англоязычная), то чтобы надписи в окнах сообщений и вопросов (в заголовках, на кнопках) были русскоязычными, надо затратить дополнительные усилия по русификации надписей, что требует отдельной работы: здесь желательно иметь подпрограммы, которые способны сразу "выдавать" надписи в окнах на русском языке вне зависимости от того, русифицирована Delphi или нет.
Разрешение этих "но" является целью настоящего сообщения.
Прежде нужно создать новый unit или добавить низлежащий код в уже имеющийся подходящий unit и объявить несколько переменных, которые потребуются для автоматической русификации надписей. Их лучше разместить в секции implementation выше текстов приведенных ниже подпрограмм.
var
ButtonEngCaptions: array[1..11] of string = ('Yes', 'No', 'OK', 'Cancel',
'Abort', 'Retry', 'Ignore',
'All', 'NoToAll','YesToAll',
'Help');
ButtonRusCaptions: array[1..11] of string = ('Да', 'Нет', 'OK', 'Отмена',
'Прервать','Повтор', 'Пропуск',
'Все', 'Нет Всем','Да Всем',
'Помощь');
MsgEngCaptions: array[1..4] of string =
('Confirm', 'Information', 'Warning', 'Error');
MsgRusCaptions: array[1..4] of string =
('Подтвердите', 'Сообщение','Предупреждение','Ошибка');
| |
Далее возьмем стандартную функцию MessageDlgPosHelp модуля Dialogs.pas и коррекцией ее кода создадим новую функцию KdnMessageDlg (текст функции снабжен необходимыми комментариями):
function KdnMessageDlg(MsgVariant: string;
DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons): Integer;
var w1,w2,h1,h2,t2,L2,cx,cy: Integer;
ScreenActFormVisBoo: boolean;
i,j: Integer;
F: TForm;
Msg,s: ^String;
begin
New(Msg); New(s);
Msg^:= MsgVariant;
F:= CreateMessageDialog(Msg^,DlgType,Buttons);
with F do
try
w1:=0; w2:=0; h1:= 0;
for i:= 1 to 4 do
if Caption = MsgEngCaptions[i] then Caption:= MsgRusCaptions[i];
for i:= 0 to F.ComponentCount-1 do
begin
if F.Components[i] is TImage then
With F.Components[i] as TImage do
Top:= Top-4;
if F.Components[i] is TLabel then
With F.Components[i] as TLabel do
begin
w1:=1;
if Length(Caption)>2 then
for j:= 1 to Length(Caption)-2 do
if Copy(Caption,j,2) = #13#10 then Inc(w1);
if w1=1 then Top:= Top+2 else
if w1=2 then Top:= Top-2 else Top:= Top-4;
w2:= Top+Height;
end;
if F.Components[i] is TButton then
With F.Components[i] as TButton do
begin
s^:= Caption;
Delete(s^,Pos('&',s^),1);
s^:= AnsiUpperCase(DelSymbAll(s^,' '));
for j:=1 to 11 do
if s^ = AnsiUpperCase(ButtonEngCaptions[j]) then
Caption:= ButtonRusCaptions[j];
if w1=1 then Top:= w2+20 else
if w1=2 then Top:= w2+12 else Top:= w2+10;
h1:= Top+Height;
end;
end;
Height:= h1+42;
cx:= -1; cy:= -1;
ScreenActFormVisBoo:= false;
if Screen.ActiveForm <> Nil then
if Screen.ActiveForm.Visible then
begin
w2:= Screen.ActiveForm.Width;
h2:= Screen.ActiveForm.Height;
t2:= Screen.ActiveForm.Top;
L2:= Screen.ActiveForm.Left;
cx:= L2 + w2 div 2;
cy:= t2 + h2 div 2;
ScreenActFormVisBoo:= true;
end;
w1:= Width; h1:= Height;
if ScreenActFormVisBoo then
begin
w2:= Screen.Width;
h2:= Screen.Height;
Top:= cy - h1 div 2;
Left:= cx - w1 div 2;
if Top<0 then Top:=0 else
if Top>h2-h1 then Top:= h2-h1;
Left:= cx - w1 div 2;
if Left<0 then Left:=0 else
if Left>w2-w1 then Left:= w2-w1;
end
else
Position:= poScreenCenter;
Result:= ShowModal;
finally
Dispose(Msg); Dispose(s);
F.Free;
Application.ProcessMessages;
end;
end;
| |
где функция DelSymbAll имеет код
function DelSymbAll(s: String; Ch: Char): String;
var i: Integer;
begin
i:= pos(Ch,s);
while i>0 do
begin
Delete(s,i,1);
i:= pos(Ch,s);
end;
Result:= s;
end;
| |
Теперь всякое окно, построенное на основе функции KdnMessageDlg, будет иметь с активной формой общий центр, за исключением тех случаев, когда центрирование увело бы любую часть F-окна за пределы экрана (F-окно будет всегда находиться полностью в экране), все надписи русифицированы, метка "правильно" позиционирована относительно рисунка.
- Используя KdnMessageDlg построим процедуру - усовершенствованый аналог стандартной процедуры ShowMessage:
procedure KdnMessage(Msg: Variant);
begin
KdnMessageDlg(Msg, mtInformation,[mbOK]);
end;
| |
Несколько примеров обращения к процедуре:
KdnMessage(24); // числовой целочисленный тип аргумента
KdnMessage(-224.89); // числовой вещественный тип аргумента
KdnMessage('Это строка'); // строковый тип
KdnMessage(Now); // тип TDateTime
KdnMessage(Tim); // тип TTime
KdnMessage(Dat); // тип TDate
В последнем случае активное окно и нависающее над ним окно сообщения будут выглядеть так (центры активной формы и окна сообщения совпадают):
- На основе предыдущей процедуры построим многострочное сообщение:
procedure KdnMessageV(Msg: array of Variant);
begin
KdnMessage(DinVarArrToStrs(Msg);
end;
| |
где функция DinVarArrToStrs имеет код:
function DinVarArrToStrs(a: array of Variant): Variant;
var s: array of String; i: byte;
begin
SetLength(s,2);
s[0]:='';
if Length(a)>0 then
begin
s[0]:= a[0];
if Length(a)>1 then
for i:= 1 to Length(a)-1 do
begin
s[1]:= a[i];
s[0]:= s[0]+''#13#10''+s[1];
end;
end;
Result:= s[0];
s:= Nil;
end;
| |
Пример обращения к процедуре:
KdnMessageV([1355,-15.87,Now,DateOf(Now),TimeOf(Now)]);
и окно, отображающее результат обращения:
- Аналогичным образом создадим однострочное окно для вывода вопроса с целью получения ответа от пользователя программы
function KdnYesNo(Question: Variant): boolean;
begin
Result:= KdnMessageDlg(Question,mtConfirmation,[mbYes,mbNo]) = mrYes;
end;
| |
и соответствующее многострочное окно
function KdnYesNoV(Question: array of Variant): boolean;
begin
Result:= KdnYesNo(DinVarArrToStrs(Question));
end;
| |
Примеры обращения к функциям:
if KdnYesNo('Удалить рисунок ?') then DeleteFile(ImFile);
if not KdnYesNoV(['Вы действительно желаете','удалить непустую папку',
ExeDir,'?'])
then exit;
Соответствующие окна показаны ниже.
Точно также можно создать окна с тремя кнопками:
function KdnYesNoCancel(Question: Variant): byte;
var r: Integer;
begin
r:= KdnMessageDlg(Question,mtConfirmation,[mbYes,mbNo,mbCancel]);
Result:= 3;
if r = mrYes then Result:= 1 else
if r = mrNo then Result:= 2;
end;
function KdnYesNoCancelV(Question: array of Variant): byte;
begin
Result:= KdnYesNoCancel(DinVarArrToStrs(Question));
end;
| |
Ограничимся примером обращения к последней функции
if KdnYesNoCancelV(['Вы действительно желаете','удалить непустую папку',
ExeDir,'?']) = 1
then if KdnYesNo('Подтвердите') then DeleteFolder(ExeDir);
Первое окно, которое появится в результате исполнения этого кода, имеет вид:
Аналогично на основе функции KdnMessageDlg могут быть без труда созданы другие подобные процедуры и функции.
Полный исходный текст KdnWins.pas (6 Кб), содержащий перечисленные процедуры и функции.
Коднянко Владимир,
Красноярск, 16.09.2004 г.
[Стандартные диалоги]
Обсуждение материала [ 29-09-2004 11:26 ] 12 сообщений |