В разработке приложений на Delphi с использованием Object Pascal часто возникают сложности с масштабированием интерфейса, особенно при работе с различными разрешениями экрана. В этой статье мы разберем конкретную проблему, описанную на форуме, и предложим несколько решений.
Проблема: компонент не появляется там, где должен
Пользователь heebiejeebies столкнулся с необычной проблемой: при изменении разрешения интерфейса один конкретный компонент (TBCRoundedImage с именем MainHomeBtn1) отображался на 15 пикселей ниже, чем должен был. Особенность заключалась в том, что проблема проявлялась только при переключении разрешения, когда компонент не был виден (находился на неактивной вкладке).
Анализ исходного кода
Пользователь реализовал собственный механизм масштабирования с использованием TStringGrid, где хранились данные о размерах и позициях компонентов для разных разрешений. Вот ключевые части его кода:
procedure TMainForm.ApplyScaling(Resolution: String);
var
Grid: TStringGrid;
i, j: Integer;
CompName, PropName, PropValue: String;
Comp: TComponent;
PropInfo: PPropInfo;
ScreenWidth, ScreenHeight: Integer;
begin
// Установка размера формы в зависимости от разрешения
case Resolution of
'4K': begin ScreenWidth := 3200; ScreenHeight := 1750; end;
'HiRes': begin ScreenWidth := 2560; ScreenHeight := 1540; end;
'HD': begin ScreenWidth := 1920; ScreenHeight := 1050; end;
'LoRes': begin ScreenWidth := 1366; ScreenHeight := 738; end;
else
Exit; // Некорректное разрешение
end;
MainForm.Width := ScreenWidth;
MainForm.Height := ScreenHeight;
// Выбор правильной таблицы в зависимости от ОС и разрешения
case CurrentOS of
osWindows:
case Resolution of
'4K': Grid := ScalingData.WPanels4k;
'HiRes': Grid := ScalingData.WPanelsHiRes;
'HD': Grid := ScalingData.WPanelsHD;
'LoRes': Grid := ScalingData.WPanelsLoRes;
end;
// ... аналогично для других ОС
end;
// Применение масштабирования к компонентам
for i := 1 to Grid.RowCount - 1 do
begin
CompName := Grid.Cells[0, i];
Comp := MainForm.FindComponent(CompName);
if Assigned(Comp) then
begin
for j := 1 to Grid.ColCount - 1 do
begin
PropName := Grid.Cells[j, 0];
PropValue := Grid.Cells[j, i];
if PropName = 'Type' then Continue;
// Специальная обработка для некоторых компонентов
if (Comp is TBGRALabel) and (PropName = 'Font.Size') then
TBGRALabel(Comp).Font.Size := StrToIntDef(PropValue, 0);
// Общая обработка свойств
if (PropValue <> '') then
begin
PropInfo := GetPropInfo(Comp.ClassInfo, PropName);
if Assigned(PropInfo) then
begin
case PropInfo^.PropType^.Kind of
tkInteger, tkInt64:
SetPropValue(Comp, PropName, StrToIntDef(PropValue, 0));
tkFloat:
SetPropValue(Comp, PropName, StrToFloatDef(PropValue, 0.0));
tkString, tkLString, tkAString, tkWString, tkUString:
SetPropValue(Comp, PropName, PropValue);
end;
end;
end;
end;
end;
end;
end;
Возможные причины проблемы
Проблемы с родительским контейнером: Компонент может быть привязан не к той вкладке, где ожидается.
Конфликт с системным масштабированием: Включенное системное масштабирование (LCL Scaling) может конфликтовать с ручным управлением.
Особенности работы TBCRoundedImage: Этот компонент может иметь внутреннюю логику позиционирования.
Проблемы с обновлением визуализации: При скрытии/показе компонента может не обновляться его позиция.
Решения и рекомендации
1. Проверка родительского контейнера
Убедитесь, что компонент действительно находится на нужной вкладке:
if MainHomeBtn1.Parent <> ExpectedTabSheet then
MainHomeBtn1.Parent := ExpectedTabSheet;
2. Отключение системного масштабирования
Как советовал wp, отключите встроенное масштабирование: - В настройках проекта: "Use LCL Scaling (High DPI)" → Off - Свойство Scaled формы → False
3. Альтернативный подход к масштабированию
Вместо полного ручного управления можно использовать комбинацию Anchor и Constraints:
// Установка якорей (отключить, если используете ручное позиционирование)
MainHomeBtn1.Anchors := [akLeft, akTop];
// Установка ограничений
MainHomeBtn1.Constraints.MinWidth := 100;
MainHomeBtn1.Constraints.MinHeight := 50;
MainHomeBtn1.Constraints.MaxWidth := 200;
MainHomeBtn1.Constraints.MaxHeight := 100;
4. Принудительное обновление позиции
Добавьте принудительное обновление позиции после масштабирования:
procedure TMainForm.ApplyScaling(Resolution: String);
begin
// ... существующий код ...
// Принудительное обновление проблемного компонента
if Assigned(MainHomeBtn1) then
begin
MainHomeBtn1.Top := MainHomeBtn1.Top + 1;
MainHomeBtn1.Top := MainHomeBtn1.Top - 1;
MainHomeBtn1.Invalidate;
end;
end;
5. Решение с OnChange вкладки
Как временное решение, пользователь добавил вызов ApplyScaling в событие OnChange вкладки:
procedure TMainForm.TabSheetChange(Sender: TObject);
begin
ApplyScaling(CurrentResolution);
end;
Альтернативный подход: использование относительных координат
Вместо абсолютных значений в таблице можно хранить относительные координаты:
procedure TMainForm.ApplyRelativeScaling;
var
i: Integer;
Comp: TControl;
NewLeft, NewTop, NewWidth, NewHeight: Integer;
begin
for i := 0 to ComponentCount - 1 do
begin
if Components[i] is TControl then
begin
Comp := TControl(Components[i]);
// Расчет новых координат относительно размера формы
NewLeft := Round(Comp.Tag * Self.Width / 100); // Хранить процент в Tag
NewTop := Round((Comp.Tag shr 8) * Self.Height / 100);
NewWidth := Round((Comp.Tag shr 16) * Self.Width / 100);
NewHeight := Round((Comp.Tag shr 24) * Self.Height / 100);
Comp.SetBounds(NewLeft, NewTop, NewWidth, NewHeight);
end;
end;
end;
Заключение
Проблемы с позиционированием компонентов в Delphi при ручном масштабировании могут быть вызваны различными факторами. В данном случае наиболее вероятными причинами являются:
1. Конфликт с системным масштабированием
2. Особенности работы компонента TBCRoundedImage
3. Проблемы с обновлением визуализации при переключении вкладок
Рекомендуемые решения:
- Отключить системное масштабирование (LCL Scaling)
- Проверить и явно установить родительский контейнер
- Добавить принудительное обновление позиции проблемного компонента
Context описывает проблемы с позиционированием компонентов в Delphi и предлагает решения для масштабирования интерфейса на разных разрешениях экрана.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.