Подтвердить что ты не робот

Как написать код WinForms, который автоматически масштабируется до системных настроек шрифта и dpi?

Вступление: Там много комментариев, в которых говорится: "WinForms не автоматически масштабируется до настроек DPI/шрифта, переключитесь в WPF". Однако, я думаю, что это основано на .NET 1.1; похоже, они действительно неплохо выполнили автоматическое масштабирование в .NET 2.0. По крайней мере, основываясь на наших исследованиях и тестировании. Однако, если некоторые из вас знают лучше, мы будем рады услышать от вас. (Пожалуйста, не беспокойтесь, утверждая, что мы должны переключиться на WPF... это не вариант прямо сейчас.)

Вопросы:

  • Что в WinForms НЕ автоматическое масштабирование должным образом и поэтому следует избегать?

  • Какие руководящие принципы проектирования должны следовать программистам при написании кода WinForms, чтобы он автоматически масштабировался?

Рекомендации по дизайну, которые мы определили до сих пор:

См. ответ сообщества wiki ниже.

Являются ли какие-либо из этих неправильных или неадекватных? Любые другие рекомендации, которые мы должны принять? Есть ли другие шаблоны, которых нужно избегать? Любые другие рекомендации по этому вопросу будут очень оценены.

4b9b3361

Ответ 1

Элементы управления, которые не поддерживают масштабирование должным образом:

  • Label с AutoSize = False и Font унаследован. Явно установите Font на элемент управления, чтобы он был выделен жирным шрифтом в окне "Свойства". Ширины столбцов
  • ListView не масштабируются. Вместо этого замените форму ScaleControl. См. этот ответ
  • SplitContainer Panel1MinSize, Panel2MinSize и SplitterDistance свойства
  • TextBox с MultiLine = True и Font унаследован. Явно установите Font на элемент управления, чтобы он был выделен жирным шрифтом в окне "Свойства".
  • ToolStripButton изображение. В конструкторе формы:

    • Установить ToolStrip.AutoSize = False
    • Установите ToolStrip.ImageScalingSize в соответствии с CreateGraphics.DpiX и .DpiY
    • При необходимости установите ToolStrip.AutoSize = True.

    Иногда AutoSize можно оставить в True, но иногда он не может изменять размер без этих шагов. Работает без изменений с помощью .NET Framework 4.5.2 и EnableWindowsFormsHighDpiAutoResizing.

  • TreeView изображения. Установите ImageList.ImageSize в соответствии с CreateGraphics.DpiX и .DpiY. Работает без изменений с помощью .NET Framework 4.5.1 и EnableWindowsFormsHighDpiAutoResizing.
  • Form размер. Масштабировать фиксированный размер Form вручную после создания.

Рекомендации по дизайну:

  • Все контейнеры ContainerControls должны быть установлены на один и тот же AutoScaleMode = Font. (Шрифт будет обрабатывать как изменения DPI, так и изменения в системном шрифте настройка размера; DPI будет обрабатывать только изменения DPI, а не изменения в системный размер шрифта.)

  • Все ContainerControls также должны быть установлены с помощью AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);, предполагая 96dpi (см. следующий маркер). Это автоматически добавляется дизайнером основанный на DPI, вы открываете дизайнера в... но не хватает многие из наших старых файлов дизайнеров. Возможно, Visual Studio.NET( версия до VS 2005) не добавляла это правильно.

  • Работает ли ваш дизайнер в формате 96dpi (мы могли бы переключиться на 120dpi; но мудрость в Интернете говорит о том, чтобы придерживаться 96dpi; эксперименты в порядке; по дизайну, это не имеет значения, поскольку он просто меняет строку AutoScaleDimensions, которую вставляет конструктор). Чтобы настроить Visual Studio на виртуальный 96dpi на дисплее с высоким разрешением, найдите его .exe файл, щелкните правой кнопкой мыши, чтобы изменить свойства, и в разделе "Совместимость" выберите "Переопределить поведение масштабирования высокого DPI. Масштабирование выполняется с помощью: System".

  • Убедитесь, что вы никогда не устанавливали шрифт на уровне контейнера... только на контроль листьев. (Установка шрифта в контейнере, кажется, отключается автомасштабирование этого контейнера.)

  • НЕ используйте привязку Right или Bottom, привязанную к UserControl... ее позиционирование не будет автоматически масштабироваться; вместо этого отпустите панель или другую контейнер в свой UserControl и привязать ваши другие элементы управления к этой Группы; используйте панель Dock Right или Dock Bottom в своем UserControl.

  • Только элементы управления в списках управления, когда ResumeLayout в конце из InitializeComponent будет автоматически масштабироваться... если вы динамически добавлять элементы управления, тогда вам нужно SuspendLayout(); AutoScaleDimensions = new SizeF(6F, 13F); AutoScaleMode = AutoScaleMode.Font; ResumeLayout(); в этом элементе управления, прежде чем добавлять его. И ваш позиционирование также необходимо будет отрегулировать, если вы не используете Dock или диспетчер компоновки, например FlowLayoutPanel или TableLayoutPanel.

  • Базовые классы, полученные из ContainerControl, должны оставить AutoScaleMode установленным в Inherit (значение по умолчанию, установленное в классе ContainerControl, но НЕ по умолчанию, заданное дизайнером). Если вы установите его на что-нибудь еще, а затем ваш производный класс попытается установить его в Font (как следует), тогда действие установки этого параметра на Font очистит настройку конструктора AutoScaleDimensions, что приведет к фактическому переключению автоматическое масштабирование! (Это руководство в сочетании с предыдущим означает, что вы никогда не сможете создавать базовые классы в дизайнере... все классы должны быть либо разработаны как базовые классы, либо как классы листа!)

  • Избегайте использования Form.MaxSize статически/в конструкторе. MinSize и MaxSize по форме не масштабируются так же, как все остальное. Итак, если вы выполняете всю свою работу в 96dpi, тогда, когда в более высоком DPI ваш MinSize не вызовет проблем, но может быть не таким ограничивающим, как вы ожидали, но ваш MaxSize может ограничить масштабирование размера, что может вызвать проблемы. Если вы хотите MinSize == Size == MaxSize, не делайте этого в Дизайнере... сделайте это в своем конструкторе или OnLoad переопределите... установите как MinSize, так и MaxSize в ваш правильно масштабированный размер.

  • Все элементы управления на конкретном Panel или Container должны либо использовать привязку, либо докинг. Если вы их смешиваете, автомасштабирование, выполняемое этим Panel, будет часто ошибочным в тонких причудливых путях.

Ответ 2

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

Если вы создаете макет должным образом reflowable/auto-size, то почти все работает точно так же, как и автоматически, с настройками по умолчанию, используемыми Visual Studio (а именно, AutoSizeMode = Font в родительской форме и Inherit на все остальное).

Единственная проблема заключается в том, что вы задали свойство Font в форме в дизайнере. Сгенерированный код сортирует задания по алфавиту, что означает, что AutoScaleDimensions будет назначаться до Font. К сожалению, это полностью нарушает логику автоматического масштабирования WinForms.

Исправление просто. Либо не устанавливайте свойство Font в конструкторе вообще (установите его в конструкторе формы), либо вручную измените порядок этих назначений (но тогда вы должны продолжать делать это каждый раз, когда вы редактируете форму в дизайнере). Voila, почти идеальное и полностью автоматическое масштабирование с минимальными хлопотами. Даже размеры формы масштабируются правильно.


Ниже перечислены известные проблемы, с которыми я сталкиваюсь:

Ответ 3

Руководство, которое я написал на работе:

WPF работает в "независимых устройствах", что означает, что все шкалы управления идеально подходит для экранов с высоким разрешением. В WinForms это требует большей осторожности.

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

  • Где бы вы ни находили (метки, кнопки, панели), установите для свойства AutoSize значение True.
  • Для макета используйте FlowLayoutPanel (a la WPF StackPanel) и TableLayoutPanel (a la WPF Grid) для макета, а не ваниль Панель.
  • Если вы работаете на машине с высоким разрешением на дюйм, дизайнер Visual Studio может разочаровываться. Когда вы устанавливаете AutoSize = True, он изменит размер элемента управления на ваш экран. Если элемент управления имеет значение AutoSizeMode = GrowOnly, он останется таким размером для людей с обычным dpi, т.е. быть больше, чем ожидалось. Чтобы исправить это, откройте конструктор на компьютере с нормальным dpi и щелкните правой кнопкой мыши, reset.

Ответ 4

Мне показалось, что WinForms очень сложно играть с высоким DPI. Итак, я написал метод VB.NET, чтобы переопределить поведение формы:

 
Public Shared Sub ScaleForm(WindowsForm As System.Windows.Forms.Form)
    Using g As System.Drawing.Graphics = WindowsForm.CreateGraphics
        Dim sngScaleFactor As Single = 1
        Dim sngFontFactor As Single = 1
        If g.DpiX > 96 Then
            sngScaleFactor = g.DpiX / 96
            'sngFontFactor = 96 / g.DpiY
        End If
        If WindowsForm.AutoScaleDimensions = WindowsForm.CurrentAutoScaleDimensions Then
            'ucWindowsFormHost.ScaleControl(WindowsForm, sngFontFactor)
            WindowsForm.Scale(sngScaleFactor)
        End If
    End Using
End Sub

Ответ 5

Настройте приложение для .Net Framework 4.7 и запустите его под Windows 10 v1703 (Creators Update Build 15063). С помощью .NET 4.7 под Windows 10 (v1703), MS сделала много улучшений DPI.

Начиная с .NET Framework 4.7, Windows Forms включает усовершенствования для общих сценариев с высоким уровнем DPI и динамического DPI. Эти включают в себя:

  • Улучшения в масштабировании и компоновке нескольких элементов управления Windows Forms, таких как элемент управления MonthCalendar и Контроль CheckedListBox.

  • Однопроходное масштабирование. В .NET Framework 4.6 и более ранних версиях масштабирование выполнялось с помощью нескольких проходов, что вызвало некоторые элементы управления должны быть масштабированы больше, чем было необходимо.

  • Поддержка динамических сценариев DPI, в которых пользователь изменяет DPI или масштабный коэффициент после того, как приложение Windows Forms было запущен.

Чтобы поддержать его, добавьте манифест приложения в приложение и сообщите, что ваше приложение поддерживает Windows 10:

<compatibility xmlns="urn:schemas-microsoft.comn:compatibility.v1">
    <application>
        <!-- Windows 10 compatibility -->
        <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
</compatibility>

Затем добавьте app.config и объявите приложение Per Monitor Aware. Это выполняется сейчас в app.config и NOT в манифесте, как раньше!

<System.Windows.Forms.ApplicationConfigurationSection>
   <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection> 

Этот PerMonitorV2 является новым, поскольку обновление для разработчиков Windows 10:

DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2

Также известен как Per Monitor v2. Улучшение оригинала на уровне мониторинга DPI, который позволяет приложениям получать доступ новые связанные с DPI масштабы поведения на основе окна верхнего уровня.

  • Уведомления об изменении DPI дочернего окна. В контексте Per2 v2 все дерево окон уведомляется о любых изменениях DPI, которые происходят.

  • Масштабирование неклиентской области. Все окна автоматически будут иметь свою неклиентскую область, нарисованную чувствительным способом DPI. Звонки на EnableNonClientDpiScaling не требуется.

  • S настройка меню Win32. Все меню NTUSER, созданные в контекстах Per Monitor v2, будут масштабироваться для каждого монитора.

  • Диалоговое масштабирование. Диалоги Win32, созданные в контекстах Per Monitor v2, автоматически будут реагировать на изменения DPI.

  • Улучшено масштабирование элементов управления comctl32. Различные элементы управления comctl32 улучшили поведение масштабирования DPI в Per Monitor v2 контексты.

  • Улучшенное поведение в темах. Ручки UxTheme, открытые в контексте окна Per Monitor v2, будут работать с точки зрения DPI связанные с этим окном.

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

  • Control.DpiChangedAfterParent, который запускается. Происходит, когда параметр DPI для элемента управления изменяется программно после DPI изменить событие для его родительского элемента управления или формы.

  • Control.DpiChangedBeforeParent, который запускается, когда параметр DPI для элемента управления изменяется программно до изменения DPI событие для его родительского элемента управления или формы.

  • Form.DpiChanged, который запускается, когда параметр DPI изменяется на устройстве отображения, где отображается форма.

У вас также есть 3 вспомогательных метода обработки/масштабирования DPI:

  • Control.LogicalToDeviceUnits, который преобразует значение из логического в пиксели устройства.

  • Control.ScaleBitmapLogicalToDevice, который масштабирует растровое изображение для логического DPI для устройства.

  • Control.DeviceDpi, который возвращает DPI для текущего устройства.

Если вы все еще видите проблемы, вы можете отказаться от улучшений DPI через записи app.config.

Если у вас нет доступа к исходному коду, вы можете перейти к свойствам приложения в проводнике Windows, перейти к совместимости и выбрать System (Enhanced)

введите описание изображения здесь

который активирует масштабирование GDI, чтобы улучшить обработку DPI:

Для приложений, основанных на GDI, Windows теперь может масштабировать DPI для каждого монитора. Это означает, что эти приложения будут, волшебным образом, чтобы распознать DPI на мониторе.

Выполняйте все эти шаги, и вы должны получить лучший опыт работы с DPI для приложений WinForms. Но помните, что вам нужно настроить таргетинг на приложение .net 4.7 и вам понадобится хотя бы Windows 10 Build 15063 (Creators Update). В следующем обновлении Windows 10 1709 мы можем получить больше улучшений.

Ответ 6

В дополнение к якорям, которые не работают очень хорошо: я бы сделал шаг дальше и сказал, что точное позиционирование (иначе, используя свойство Location) не очень хорошо работает с масштабированием шрифта. Мне пришлось решить эту проблему в двух разных проектах. В обоих случаях нам пришлось преобразовать расположение всех элементов управления WinForms в использование TableLayoutPanel и FlowLayoutPanel. Использование свойства Dock (обычно устанавливается для Fill) внутри TableLayoutPanel работает очень хорошо и отлично масштабируется с помощью системного шрифта DPI.

Ответ 7

Недавно я столкнулся с этой проблемой, особенно в сочетании с масштабированием Visual Studio при открытии редактора в системе с высоким разрешением. Мне было лучше сохранить AutoScaleMode = Font, но установить шрифт шрифт по умолчанию, но указать размер в пикселе, а не то есть: Font = MS Sans; 11px. В коде я , затем reset шрифт по умолчанию: Font = SystemFonts.DefaultFont и все в порядке.

Просто мои два цента. Я думал, что я разделяю, потому что "сохранение AutoScaleMode = Font" и "Установить размер шрифта в пикселе для Дизайнера" было чем-то, что я не нашел в Интернете.

У меня есть более подробная информация о моем блоге: http://www.sgrottel.de/?p=1581&lang=en