Как заставить мой GUI вести себя хорошо, когда масштабирование шрифтов Windows превышает 100% - PullRequest
104 голосов
/ 28 ноября 2011

При выборе шрифтов большого размера на панели управления Windows (например, 125% или 150%) возникают проблемы в приложении VCL, каждый раз, когда что-то устанавливается по пикселям.

Возьмите TStatusBar.Panel,Я установил его ширину так, чтобы он содержал ровно одну метку, теперь с большими шрифтами метка «переполняется».Та же проблема с другими компонентами.

Некоторые новые ноутбуки от Dell уже поставляются с настройкой по умолчанию 125%, поэтому в прошлом эта проблема была довольно редкой, а сейчас она действительно важна.

Что можетсделать, чтобы преодолеть эту проблему?

Ответы [ 4 ]

60 голосов
/ 28 ноября 2011

Ваши настройки в файле .dfm будут корректно увеличены, если Scaled равно True.

Если вы задаете размеры в коде, тогда вам нужномасштабируйте их на Screen.PixelsPerInch, деленное на Form.PixelsPerInch.Для этого используйте MulDiv.

function TMyForm.ScaleDimension(const X: Integer): Integer;
begin
  Result := MulDiv(X, Screen.PixelsPerInch, PixelsPerInch);
end;

Это то, что делает каркас персистентности формы, когда Scaled равен True.

Фактически, вы можете сделать убедительный аргумент длязаменив эту функцию версией, которая жестко кодирует значение 96 для знаменателя.Это позволяет вам использовать абсолютные значения размеров и не беспокоиться об изменении значения, если вам случится изменить масштабирование шрифта на компьютере разработчика и повторно сохранить файл .dfm.Причина, по которой это важно, заключается в том, что свойство PixelsPerInch, хранящееся в файле .dfm, является значением компьютера, на котором последний раз был сохранен файл .dfm.

const
  SmallFontsPixelsPerInch = 96;

function ScaleFromSmallFontsDimension(const X: Integer): Integer;
begin
  Result := MulDiv(X, Screen.PixelsPerInch, SmallFontsPixelsPerInch);
end;

Итак, продолжение темы - еще одна вещьследует опасаться, что если ваш проект разработан на нескольких машинах с разными значениями DPI, вы обнаружите, что масштабирование, которое Delphi использует при сохранении файлов .dfm, приводит к элементам управления, блуждающим по серии изменений.На моем рабочем месте, чтобы избежать этого, мы придерживаемся строгой политики, согласно которой формы редактируются только с разрешением 96 точек на дюйм (100% масштабирование).

На самом деле моя версия ScaleFromSmallFontsDimension также допускает возможностьшрифт формы во время выполнения отличается от установленного во время разработки.На машинах XP формы моего приложения используют 8pt Tahoma.На Vista и выше используется 9pt Segoe UI.Это обеспечивает еще одну степень свободы.Масштабирование должно учитывать это, поскольку предполагается, что значения абсолютных размеров, используемые в исходном коде, относятся к базовой линии 8pt Tahoma при 96 dpi.

Если вы используете какие-либо изображения или глифы в вашем пользовательском интерфейсе, тогда они должнымасштаб тоже.Типичным примером могут служить глифы, которые используются на панелях инструментов и в меню.Вы захотите предоставить эти символы в виде ресурсов значков, связанных с вашим исполняемым файлом.Каждый значок должен содержать диапазон размеров, а затем во время выполнения вы выбираете наиболее подходящий размер и загружаете его в список изображений.Некоторые подробности по этой теме можно найти здесь: Как загрузить значки с ресурса без наложения псевдонимов?

Еще один полезный прием - определить размеры в относительных единицах относительно * 1030.* или TextHeight.Итак, если вы хотите, чтобы размер был около 10 вертикальных линий, вы можете использовать 10*Canvas.TextHeight('Ag').Это очень грубая и готовая метрика, потому что она не учитывает межстрочный интервал и так далее.Тем не менее, часто все, что вам нужно сделать, - это обеспечить правильное масштабирование GUI с помощью PixelsPerInch.

. Вы также должны пометить ваше приложение как с высоким разрешением .Лучший способ сделать это - через манифест приложения.Поскольку инструменты сборки Delphi не позволяют вам настраивать манифест, который вы используете, это заставляет вас связывать свой собственный ресурс манифеста.

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

Сценарий ресурса выглядит следующим образом:

1 24 "Manifest.txt"

где Manifest.txt содержит фактический манифест.Вам также необходимо включить раздел comctl32 v6 и установить для requestedExecutionLevel значение asInvoker.Затем вы связываете этот скомпилированный ресурс с вашим приложением и убедитесь, что Delphi не пытается сделать то же самое с его манифестом.В современном Delphi вы достигаете этого, устанавливая для параметра проекта Runtime Themes значение None.

Манифест - это правильный способ объявить ваше приложение поддерживающим высокий уровень DPI.Если вы просто хотите попробовать это быстро, не связываясь с манифестом, позвоните по номеру SetProcessDPIAware.Сделайте это как самое первое, что вы делаете при запуске приложения.Желательно в одном из разделов начальной инициализации модуля или в качестве первого шага в вашем файле .dpr.

Если вы не заявите, что ваше приложение поддерживает высокий DPI, то Vista и более поздние версии отобразят его в устаревшем виде.режим для любого шрифта с масштабированием выше 125%.Это выглядит довольно ужасно.Постарайтесь не попасть в эту ловушку.

Windows 8.1 для каждого обновления DPI монитора

Начиная с Windows 8.1, теперь ОС поддерживает настройки DPI для каждого монитора (http://msdn.microsoft.com/en-ca/magazine/dn574798.aspx).) Это большая проблема для современных устройств, которые могут иметь разные дисплеи с очень разными возможностями. У вас может быть очень Экран ноутбука с высоким разрешением и внешний проектор с низким разрешением. Поддержка такого сценария требует еще больше работы, чем описано выше.

52 голосов
/ 28 ноября 2011

Примечание: смотрите другие ответы, так как они содержат очень ценные приемы. Мой ответ здесь содержит только предостережения и предостережения от предположения, что осведомленность о DPI проста.

Я обычно избегаю масштабирования с поддержкой DPI с помощью TForm.Scaled = True. Осведомленность о DPI важна для меня только тогда, когда она становится важной для клиентов, которые звонят мне и готовы платить за это. Техническая причина, лежащая в основе этой точки зрения, заключается в том, что осведомленность о DPI или нет, вы открываете окно в мир боли. Многие стандартные и сторонние элементы управления VCL не работают в высоком разрешении. Заметным исключением является то, что части VCL, которые обертывают общие элементы управления Windows, работают замечательно хорошо при высоком разрешении. Огромное количество сторонних и встроенных пользовательских элементов управления Delphi VCL не работают или работают вообще с высоким DPI. Если вы планируете включить TForm.Scaled, обязательно протестируйте на 96, 125 и 150 DPI для каждой формы в вашем проекте, а также для каждой третьей стороны и встроенного элемента управления, который вы используете.

Сам Delphi написан на Delphi. Для большинства форм включен флаг осведомленности с высоким разрешением, хотя даже недавно, как и в Delphi XE2, сами авторы IDE решили НЕ включать этот флаг манифеста с высоким разрешением. Обратите внимание, что в Delphi XE4 и более поздних версиях флаг осведомленности HIGH DPI включен, и среда IDE выглядит хорошо.

Я полагаю, что вы не используете TForm.Scaled = true (это значение по умолчанию в Delphi, поэтому, если вы не изменили его, большинство ваших форм имеют Scaled = true) с флагами High DPI Aware (как показано в David's ответы) с приложениями VCL, созданными с использованием встроенного конструктора форм Delphi.

В прошлом я пытался составить минимальную выборку типа поломки, которую вы можете ожидать, когда TForm.Scaled имеет значение true, и когда при масштабировании Delphi происходит сбой. Эти глюки не всегда и только вызваны значением DPI, отличным от 96. Я не смог определить полный список других вещей, включая изменения размера шрифта Windows XP. Но поскольку большинство этих глюков появляются только в моих собственных приложениях, в довольно сложных ситуациях я решил показать вам некоторые доказательства, которые вы можете проверить сами.

Delphi XE выглядит так, когда вы устанавливаете масштабирование DPI на «Fonts @ 200%» в Windows 7, а Delphi XE2 аналогично разбивается на Windows 7 и 8, но эти глюки, по-видимому, исправлены в Delphi XE4:

enter image description here

enter image description here

В основном это стандартные элементы управления VCL, которые плохо работают при высоком DPI. Обратите внимание, что большинство вещей вообще не масштабировалось, поэтому разработчики Delphi IDE решили игнорировать осведомленность о DPI, а также отключить виртуализацию DPI. Такой интересный выбор.

Отключайте виртуализацию DPI только в том случае, если вам нужен новый дополнительный источник боли и трудный выбор. Я предлагаю вам оставить это в покое. Обратите внимание, что общие элементы управления Windows в основном работают нормально. Обратите внимание, что элемент управления проводника данных Delphi представляет собой оболочку C # WinForms вокруг стандартного общего элемента управления Windows Tree. Это просто глюк Microsoft, и для его исправления может потребоваться, чтобы Embarcadero переписал чистый собственный элемент управления .Net для своего проводника данных, или написал некоторый код DPI-check-and-modify-properties для изменения высоты элементов в элементе управления. Даже Microsoft WinForms не может работать с высоким разрешением чисто, автоматически и без специального кода.

Обновление: интересный фактоид: хотя Delphi IDE, по-видимому, не "виртуализирован", он не использует содержимое манифеста, показанное Дэвидом, для достижения "не-DPI-виртуализации". Возможно, он использует какую-то функцию API во время выполнения.

Обновление 2: в ответ на то, как я буду поддерживать 100% / 125% DPI, я разработал двухэтапный план.Этап 1 - инвентаризация моего кода для пользовательских элементов управления, которые необходимо исправить для высокого DPI, а затем составить план их исправления или поэтапного отказа от них.Фаза 2 будет состоять в том, чтобы взять некоторые области моего кода, которые спроектированы как формы без управления макетом, и заменить их на формы, которые используют какое-то управление макетом, чтобы изменения DPI или высоты шрифта могли работать без отсечения.Я подозреваю, что эта компоновка «между контролями» будет гораздо более сложной в большинстве приложений, чем работа «внутриконтроля».

Обновление: В 2016 году последняя версия Delphi 10.1 Berlinхорошо работает на моей рабочей станции с разрешением 150 точек на дюйм.

41 голосов
/ 26 марта 2012

Также важно отметить, что соблюдение DPI пользователя - это лишь часть вашей реальной работы:

с учетом размера шрифта пользователя

В течение десятилетий Windows решала эту проблему, предлагая использовать макет, используя Диалоговые единицы , а не пиксели. «диалоговая единица» определена так, что средний символ шрифта равен

  • 4 диалоговых блока (dlus) в ширину и
  • 8 диалоговых единиц (кластер), высокий

enter image description here

Delphi поставляется с (ошибочным) понятием Scaled, где форма пытается автоматически корректироваться на основе

  • Настройки Windows DPI пользователя, стихи
  • Настройка DPI на компьютере разработчика, который в последний раз сохранил форму

Это не решает проблему, когда пользователь использует шрифт, отличный от того, с которым вы создали форму, например:

  • Разработчик разработал форму с MS Sans Serif 8pt (где средний символ 6.21px x 13.00px, при 96dpi)
  • пользователь работает с Tahoma 8pt (где средний символ 5.94px x 13.00px, при 96dpi)

    Как и у любого, кто разрабатывает приложение для Windows 2000 или Windows XP.

или

  • Разработчик разработал форму с ** Tahoma 8pt * (где средний символ 5.94px x 13.00px, при 96dpi)
  • пользователь, работающий с Segoe UI 9pt (где средний символ 6.67px x 15px, при 96dpi)

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

  • развернуть все по горизонтали на 12,29% (6,67 / 5,94)
  • растянуть все по вертикали на 15,38% (15/13)

Scaled не справится с этим.

Становится хуже, когда:

  • разработал вашу форму в Segoe UI 9pt (Windows Vista, Windows 7, Windows 8 по умолчанию)
  • пользователь работает Segoe UI 14pt , (например, мои предпочтения), что составляет 10.52px x 25px

Теперь вам нужно все масштабировать

  • по горизонтали на 57,72%
  • по вертикали на 66,66%

Scaled не справится с этим.


Если вы сообразительны, вы можете увидеть, насколько почтительным является DPI:

  • форма, разработанная с использованием Segoe UI 9pt @ 96dpi (6,67px x 15px)
  • пользователь, работающий с Segoe UI 9pt @ 150dpi (10.52px x 25px)

Вы не должны смотреть на настройку DPI пользователя, вы должны смотреть на размер шрифта . Два пользователя под управлением

  • Пользовательский интерфейс Segoe 14pt @ 96dpi (10,52px x 25px)
  • Пользовательский интерфейс Segoe 9pt при 150 dpi (10,52px x 25px)

используют тот же шрифт . DPI это просто одна вещь, которая влияет на размер шрифта; предпочтения пользователя - другое.

StandardizeFormFont

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

function StandardizeFormFont(AForm: TForm): Real;
var
    preferredFontName: string;
    preferredFontHeight: Integer;
begin
    GetUserFontPreference({out}preferredFontName, {out}preferredFontHeight);

    //e.g. "Segoe UI",     
    Result := Toolkit.StandardizeFormFont(AForm, PreferredFontName, PreferredFontHeight);
end;

Windows имеет 6 разных шрифтов; в Windows нет единой «настройки шрифта».
Но по опыту мы знаем, что наши формы должны соответствовать значку шрифта заголовка

procedure GetUserFontPreference(out FaceName: string; out PixelHeight: Integer);
var
   font: TFont;
begin
   font := Toolkit.GetIconTitleFont;
   try
      FaceName := font.Name; //e.g. "Segoe UI"

      //Dogfood testing: use a larger font than we're used to; to force us to actually test it    
      if IsDebuggerPresent then
         font.Size := font.Size+1;

      PixelHeight := font.Height; //e.g. -16
   finally
      font.Free;
   end;
end;

Как только мы узнаем размер шрифта, мы масштабируем форму от до , мы получаем текущую высоту шрифта формы ( в пикселях ) и увеличиваем ее на этот коэффициент.

Например, если я устанавливаю форму на -16, а форма в настоящее время находится на -11, то нам нужно масштабировать всю форму следующим образом:

-16 / -11 = 1.45454%

Стандартизация происходит в два этапа. Сначала масштабируйте форму по соотношению новый: старый размер шрифта. Затем фактически измените элементы управления (рекурсивно), чтобы использовать новый шрифт.

function StandardizeFormFont(AForm: TForm; FontName: string; FontHeight: Integer): Real;
var
    oldHeight: Integer;
begin
    Assert(Assigned(AForm));

    if (AForm.Scaled) then
    begin
        OutputDebugString(PChar('WARNING: StandardizeFormFont: Form "'+GetControlName(AForm)+'" is set to Scaled. Proper form scaling requires VCL scaling to be disabled, unless you implement scaling by overriding the protected ChangeScale() method of the form.'));
    end;

    if (AForm.AutoScroll) then
    begin
        if AForm.WindowState = wsNormal then
        begin
            OutputDebugString(PChar('WARNING: StandardizeFormFont: Form "'+GetControlName(AForm)+'" is set to AutoScroll. Form designed size will be suseptable to changes in Windows form caption height (e.g. 2000 vs XP).'));
                    if IsDebuggerPresent then
                        Windows.DebugBreak; //Some forms would like it (to fix maximizing problem)
        end;
    end;

    if (not AForm.ShowHint) then
    begin
        AForm.ShowHint := True;
        OutputDebugString(PChar('INFORMATION: StandardizeFormFont: Turning on form "'+GetControlName(AForm)+'" hints. (ShowHint := True)'));
                    if IsDebuggerPresent then
                        Windows.DebugBreak; //Some forms would like it (to fix maximizing problem)
    end;

    oldHeight := AForm.Font.Height;

    //Scale the form to the new font size
//  if (FontHeight <> oldHeight) then    For compatibility, it's safer to trigger a call to ChangeScale, since a lot of people will be assuming it always is called
    begin
        ScaleForm(AForm, FontHeight, oldHeight);
    end;

    //Now change all controls to actually use the new font
    Toolkit.StandardizeFont_ControlCore(AForm, g_ForceClearType, FontName, FontHeight,
            AForm.Font.Name, AForm.Font.Size);

    //Return the scaling ratio, so any hard-coded values can be multiplied
    Result := FontHeight / oldHeight;
end;

Вот работа по масштабированию формы.Он работает над ошибками в собственном методе Borland Form.ScaleBy.Сначала необходимо отключить все якоря в форме, затем выполнить масштабирование, а затем снова включить якоря:

TAnchorsArray = array of TAnchors;

procedure ScaleForm(const AForm: TForm; const M, D: Integer);
var
    aAnchorStorage: TAnchorsArray;
    RectBefore, RectAfter: TRect;
    x, y: Integer;
    monitorInfo: TMonitorInfo;
    workArea: TRect;
begin
    if (M = 0) and (D = 0) then
        Exit;

    RectBefore := AForm.BoundsRect;

    SetLength(aAnchorStorage, 0);
    aAnchorStorage := DisableAnchors(AForm);
    try
        AForm.ScaleBy(M, D);
    finally
        EnableAnchors(AForm, aAnchorStorage);
    end;

    RectAfter := AForm.BoundsRect;

    case AForm.Position of
    poScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter,
    poDesigned: //i think i really want everything else to also follow the nudging rules...why did i exclude poDesigned
        begin
            //This was only nudging by one quarter the difference, rather than one half the difference
//          x := RectAfter.Left - ((RectAfter.Right-RectBefore.Right) div 2);
//          y := RectAfter.Top - ((RectAfter.Bottom-RectBefore.Bottom) div 2);
            x := RectAfter.Left - ((RectAfter.Right-RectAfter.Left) - (RectBefore.Right-RectBefore.Left)) div 2;
            y := RectAfter.Top - ((RectAfter.Bottom-RectAfter.Top)-(RectBefore.Bottom-RectBefore.Top)) div 2;
        end;
    else
        //poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly:
        x := RectAfter.Left;
        y := RectAfter.Top;
    end;

    if AForm.Monitor <> nil then
    begin
        monitorInfo.cbSize := SizeOf(monitorInfo);
        if GetMonitorInfo(AForm.Monitor.Handle, @monitorInfo) then
            workArea := monitorInfo.rcWork
        else
        begin
            OutputDebugString(PChar(SysErrorMessage(GetLastError)));
            workArea := Rect(AForm.Monitor.Left, AForm.Monitor.Top, AForm.Monitor.Left+AForm.Monitor.Width, AForm.Monitor.Top+AForm.Monitor.Height);
        end;

//      If the form is off the right or bottom of the screen then we need to pull it back
        if RectAfter.Right > workArea.Right then
            x := workArea.Right - (RectAfter.Right-RectAfter.Left); //rightEdge - widthOfForm

        if RectAfter.Bottom > workArea.Bottom then
            y := workArea.Bottom - (RectAfter.Bottom-RectAfter.Top); //bottomEdge - heightOfForm

        x := Max(x, workArea.Left); //don't go beyond left edge
        y := Max(y, workArea.Top); //don't go above top edge
    end
    else
    begin
        x := Max(x, 0); //don't go beyond left edge
        y := Max(y, 0); //don't go above top edge
    end;

    AForm.SetBounds(x, y,
            RectAfter.Right-RectAfter.Left, //Width
            RectAfter.Bottom-RectAfter.Top); //Height
end;

, а затем мы должны рекурсивно фактически использовать новый шрифт:

procedure StandardizeFont_ControlCore(AControl: TControl; ForceClearType: Boolean;
        FontName: string; FontSize: Integer;
        ForceFontIfName: string; ForceFontIfSize: Integer);
const
    CLEARTYPE_QUALITY = 5;
var
    i: Integer;
    RunComponent: TComponent;
    AControlFont: TFont;
begin
    if not Assigned(AControl) then
        Exit;

    if (AControl is TStatusBar) then
    begin
        TStatusBar(AControl).UseSystemFont := False; //force...
        TStatusBar(AControl).UseSystemFont := True;  //...it
    end
    else
    begin
        AControlFont := Toolkit.GetControlFont(AControl);

        if not Assigned(AControlFont) then
            Exit;

        StandardizeFont_ControlFontCore(AControlFont, ForceClearType,
                FontName, FontSize,
                ForceFontIfName, ForceFontIfSize);
    end;

{   If a panel has a toolbar on it, the toolbar won't paint properly. So this idea won't work.
    if (not Toolkit.IsRemoteSession) and (AControl is TWinControl) and (not (AControl is TToolBar)) then
        TWinControl(AControl).DoubleBuffered := True;
}

    //Iterate children
    for i := 0 to AControl.ComponentCount-1 do
    begin
        RunComponent := AControl.Components[i];
        if RunComponent is TControl then
            StandardizeFont_ControlCore(
                    TControl(RunComponent), ForceClearType,
                    FontName, FontSize,
                    ForceFontIfName, ForceFontIfSize);
    end;
end;

С рекурсивным отключением якорей:

function DisableAnchors(ParentControl: TWinControl): TAnchorsArray;
var
    StartingIndex: Integer;
begin
    StartingIndex := 0;
    DisableAnchors_Core(ParentControl, Result, StartingIndex);
end;


procedure DisableAnchors_Core(ParentControl: TWinControl; var aAnchorStorage: TAnchorsArray; var StartingIndex: Integer);
var
    iCounter: integer;
    ChildControl: TControl;
begin
    if (StartingIndex+ParentControl.ControlCount+1) > (Length(aAnchorStorage)) then
        SetLength(aAnchorStorage, StartingIndex+ParentControl.ControlCount+1);

    for iCounter := 0 to ParentControl.ControlCount - 1 do
    begin
        ChildControl := ParentControl.Controls[iCounter];
        aAnchorStorage[StartingIndex] := ChildControl.Anchors;

        //doesn't work for set of stacked top-aligned panels
//      if ([akRight, akBottom ] * ChildControl.Anchors) <> [] then
//          ChildControl.Anchors := [akLeft, akTop];

        if (ChildControl.Anchors) <> [akTop, akLeft] then
            ChildControl.Anchors := [akLeft, akTop];

//      if ([akTop, akBottom] * ChildControl.Anchors) = [akTop, akBottom] then
//          ChildControl.Anchors := ChildControl.Anchors - [akBottom];

        Inc(StartingIndex);
    end;

    //Add children
    for iCounter := 0 to ParentControl.ControlCount - 1 do
    begin
        ChildControl := ParentControl.Controls[iCounter];
        if ChildControl is TWinControl then
            DisableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex);
    end;
end;

И рекурсивное включение якорей:

procedure EnableAnchors(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray);
var
    StartingIndex: Integer;
begin
    StartingIndex := 0;
    EnableAnchors_Core(ParentControl, aAnchorStorage, StartingIndex);
end;


procedure EnableAnchors_Core(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray; var StartingIndex: Integer);
var
    iCounter: integer;
    ChildControl: TControl;
begin
    for iCounter := 0 to ParentControl.ControlCount - 1 do
    begin
        ChildControl := ParentControl.Controls[iCounter];
        ChildControl.Anchors := aAnchorStorage[StartingIndex];

        Inc(StartingIndex);
    end;

    //Restore children
    for iCounter := 0 to ParentControl.ControlCount - 1 do
    begin
        ChildControl := ParentControl.Controls[iCounter];
        if ChildControl is TWinControl then
            EnableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex);
    end;
end;

С работой по фактическому изменению элементов управленияоставленный шрифт:

procedure StandardizeFont_ControlFontCore(AControlFont: TFont; ForceClearType: Boolean;
        FontName: string; FontSize: Integer;
        ForceFontIfName: string; ForceFontIfSize: Integer);
const
    CLEARTYPE_QUALITY = 5;
var
    CanChangeName: Boolean;
    CanChangeSize: Boolean;
    lf: TLogFont;
begin
    if not Assigned(AControlFont) then
        Exit;

{$IFDEF ForceClearType}
    ForceClearType := True;
{$ELSE}
    if g_ForceClearType then
        ForceClearType := True;
{$ENDIF}

    //Standardize the font if it's currently
    //  "MS Shell Dlg 2" (meaning whoever it was opted into the 'change me' system
    //  "MS Sans Serif" (the Delphi default)
    //  "Tahoma" (when they wanted to match the OS, but "MS Shell Dlg 2" should have been used)
    //  "MS Shell Dlg" (the 9x name)
    CanChangeName :=
            (FontName <> '')
            and
            (AControlFont.Name <> FontName)
            and
            (
                (
                    (ForceFontIfName <> '')
                    and
                    (AControlFont.Name = ForceFontIfName)
                )
                or
                (
                    (ForceFontIfName = '')
                    and
                    (
                        (AControlFont.Name = 'MS Sans Serif') or
                        (AControlFont.Name = 'Tahoma') or
                        (AControlFont.Name = 'MS Shell Dlg 2') or
                        (AControlFont.Name = 'MS Shell Dlg')
                    )
                )
            );

    CanChangeSize :=
            (
                //there is a font size
                (FontSize <> 0)
                and
                (
                    //the font is at it's default size, or we're specifying what it's default size is
                    (AControlFont.Size = 8)
                    or
                    ((ForceFontIfSize <> 0) and (AControlFont.Size = ForceFontIfSize))
                )
                and
                //the font size (or height) is not equal
                (
                    //negative for height (px)
                    ((FontSize < 0) and (AControlFont.Height <> FontSize))
                    or
                    //positive for size (pt)
                    ((FontSize > 0) and (AControlFont.Size <> FontSize))
                )
                and
                //no point in using default font's size if they're not using the face
                (
                    (AControlFont.Name = FontName)
                    or
                    CanChangeName
                )
            );

    if CanChangeName or CanChangeSize or ForceClearType then
    begin
        if GetObject(AControlFont.Handle, SizeOf(TLogFont), @lf) <> 0 then
        begin
            //Change the font attributes and put it back
            if CanChangeName then
                StrPLCopy(Addr(lf.lfFaceName[0]), FontName, LF_FACESIZE);
            if CanChangeSize then
                lf.lfHeight := FontSize;

            if ForceClearType then
                lf.lfQuality := CLEARTYPE_QUALITY;
            AControlFont.Handle := CreateFontIndirect(lf);
        end
        else
        begin
            if CanChangeName then
                AControlFont.Name := FontName;
            if CanChangeSize then
            begin
                if FontSize > 0 then
                    AControlFont.Size := FontSize
                else if FontSize < 0 then
                    AControlFont.Height := FontSize;
            end;
        end;
    end;
end;

Это намного больше кода, чем вы думали;я знаю.Печально то, что на Земле нет разработчика Delphi, кроме меня, который действительно исправляет свои приложения.

Уважаемый Delphi Developer : установите для шрифта Windows значение Segoe UI 14pt и исправьте ваше ошибочное приложение

Примечание : любой код публикуется в открытом доступе.Указание авторства не требуется.

11 голосов
/ 29 ноября 2011

Вот мой подарок. Функция, которая может помочь вам с горизонтальным расположением элементов в макетах вашего GUI. Бесплатно для всех.

function CenterInParent(Place,NumberOfPlaces,ObjectWidth,ParentWidth,CropPercent: Integer): Integer;
  {returns formated centered position of an object relative to parent.
  Place          - P order number of an object beeing centered
  NumberOfPlaces - NOP total number of places available for object beeing centered
  ObjectWidth    - OW width of an object beeing centered
  ParentWidth    - PW width of an parent
  CropPercent    - CP percentage of safe margin on both sides which we want to omit from calculation
  +-----------------------------------------------------+
  |                                                     |
  |        +--------+       +---+      +--------+       |
  |        |        |       |   |      |        |       |
  |        +--------+       +---+      +--------+       |
  |     |              |             |            |     |
  +-----------------------------------------------------+
  |     |<---------------------A----------------->|     |
  |<-C->|<------B----->|<-----B----->|<-----B---->|<-C->|
  |                    |<-D>|
  |<----------E------------>|

  A = PW-C   B = A/NOP  C=(CP*PW)/100  D = (B-OW)/2
  E = C+(P-1)*B+D }

var
  A, B, C, D: Integer;
begin
  C := Trunc((CropPercent*ParentWidth)/100);
  A := ParentWidth - C;
  B := Trunc(A/NumberOfPlaces);
  D := Trunc((B-ObjectWidth)/2);
  Result := C+(Place-1)*B+D;
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...