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

Автоматическое масштабирование позволяет форме и ее элементам управления, разработанным на одном компьютере с использованием определенного разрешения или системных шрифтов, правильно отображаться на другом компьютере с другим разрешением или системным шрифтом. Оно гарантирует, что размеры формы и ее элементов управления будут изменяться автоматически в соответствии с приложениями Windows и другими приложениями на компьютерах пользователя и разработчика. Поддержка автоматического масштабирования и стилей оформления в .NET Framework позволяет приложениям .NET Framework сохранять согласованный внешний вид и поведение на различных компьютерах пользователей, так же как в случае с обычными приложениями Windows.

В большинстве случаев автоматическое масштабирование работает должным образом в .NET Framework 2.0 и более поздних версий. Однако внесение изменений в схему шрифтов может быть проблематичным. Пример того, как устранить эту проблему, см. в разделе как: реагирование на изменения схемы шрифтов в приложении Windows Forms.

Необходимость автоматического масштабирования

Без автоматического масштабирования приложения, разработанные для определенного разрешения экрана или шрифта, будут выглядеть либо слишком маленькими, либо слишком большими при изменении разрешения или шрифта. Например, если приложение разработано с использованием базового шрифта Tahoma размером 9 пунктов, то без коррекции оно будет выглядеть слишком маленьким при запуске на компьютере, на котором в качестве системного шрифта используется Tahoma размером 12 пунктов. Текстовые элементы, такие как заголовки, меню, содержимое текстовых полей и т. д., будут меньше, чем в других приложениях. Более того, размер содержащих текст элементов пользовательского интерфейса, таких как строки заголовков, меню и т. д., зависит от используемого шрифта. В рассматриваемом примере эти элементы также будут выглядеть относительно меньше.

Аналогичная ситуация возникает, когда приложение разработано для определенного разрешения экрана. Наиболее частое разрешение экрана — 96 точек на дюйм (DPI), который равен 100% масштаб отображения, но выше отображает разрешение поддержкой 125%, 150%, 200% (какие соответственно равен 120, 144 и 192 DPI) и более поздних версий более распространенными становятся. Без коррекции приложение, особенно графическое, разработанное для одного разрешения, будет отображаться либо слишком большим, либо слишком маленьким при запуске с другим разрешением.

Автоматическое масштабирование предназначено для решения таких проблем путем автоматического изменения размеров формы и ее дочерних элементов управления согласно относительному размеру шрифтов и разрешению экрана. Операционная система Windows поддерживает автоматическое масштабирование диалоговых окон с помощью относительной единицы измерения, называемой единицей размера диалогового окна. Единица размера диалогового окна основана на системном шрифте, а ее связь с пикселями можно определить с помощью функции GetDialogBaseUnits пакета Win32 SDK. При изменении темы, используемой Windows, все диалоговые окна автоматически настраиваются соответствующим образом. Кроме того .NET Framework поддерживает автоматическое масштабирование в соответствии со стандартным системным шрифтом или разрешением экрана. При необходимости автоматическое масштабирование можно отключить в приложении.

Исходная поддержка автоматического масштабирования

в .NET Framework 1.0 и 1.1 автоматическое масштабирование поддерживалось простым способом и зависело от шрифта Windows, используемого по умолчанию для пользовательского интерфейса. Этот шрифт представлен в пакете Win32 SDK значением DEFAULT_GUI_FONT. Обычно этот шрифт изменяется только при изменении разрешения экрана. Для реализации автоматического масштабирования применялся описанный ниже механизм.

  1. Во время разработки свойству AutoScaleBaseSize (которое теперь устарело) присваивалась высота и ширина системного шрифта по умолчанию на компьютере разработчика.

  2. Во время выполнения для инициализации свойства Font класса Form использовался стандартный системный шрифт компьютера пользователя.

  3. Перед отображением формы вызывался метод ApplyAutoScaling для ее масштабирования. Этот метод вычислял относительный масштаб с помощью свойств AutoScaleBaseSize и Font, а затем вызывал метод Scale для масштабирования формы и ее дочерних элементов.

  4. Значение свойства AutoScaleBaseSize обновлялось таким образом, чтобы последующие вызовы ApplyAutoScaling не продолжали изменять размер формы.

Этого механизма было достаточно для большинства целей, но он имел ряд ограничений.

  • Так как AutoScaleBaseSize свойство представляет базовый размер шрифта в виде целочисленные значения, погрешности округления становились заметны, когда форма проходила через ряд разрешений.

  • Автоматическое масштабирование было реализовано только в классе Form, но не в классе ContainerControl. В результате пользовательский элемент управления масштабировался корректно, только если он был разработан с тем же разрешением, что и форма, и был помещен в форму во время разработки.

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

  • Механизм несовместим с новыми диспетчерами макетов, добавленными в .NET Framework версии 2.0, такими как FlowLayoutPanel и TableLayoutPanel.

  • Механизм не поддерживает масштабирование непосредственно на основе разрешения экрана, необходимое для совместимости с .NET Compact Framework.

Хотя этот механизм сохранился в .NET Framework версии 2.0 для обеспечения обратной совместимости, на смену ему пришел более надежный механизм масштабирования, описанный ниже. Как следствие, методы AutoScale, ApplyAutoScaling, AutoScaleBaseSize и некоторые перегрузки Scale помечены как устаревшие.

Note

Вы можете спокойно удалить ссылки на эти члены при обновлении старого кода до .NET Framework версии 2.0.

Текущая поддержка автоматического масштабирования

В .NET Framework версии 2.0 ограничения предыдущих версий снимаются благодаря описанным ниже изменениям, внесенным в механизм автоматического масштабирования форм Windows Forms.

  • Базовая поддержка масштабирования была перемещена в класс ContainerControl, чтобы формы, собственные составные элементы управления и пользовательские элементы управления получали единообразную поддержку масштабирования. Были добавлены новые члены AutoScaleFactor, AutoScaleDimensions, AutoScaleMode и PerformAutoScale.

  • Класс Control также имеет несколько новых членов, которые позволяют ему участвовать в масштабировании и поддерживать смешанное масштабирование в одной и той же форме. В частности, члены Scale, ScaleChildren и GetScaledBounds поддерживают масштабирование.

  • В дополнение к поддержке системного шрифта была добавлена поддержка масштабирования на основе разрешения экрана в соответствии со значениями перечисления AutoScaleMode. Этот режим обеспечивает совместимость с автоматическим масштабированием, поддерживаемым .NET Compact Framework, что упрощает миграцию приложений.

  • В реализацию автоматического масштабирования была добавлена совместимость с диспетчерами макетов, такими как FlowLayoutPanel и TableLayoutPanel.

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

Caution

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

Автоматическое масштабирование в действии

В Windows Forms для автоматического масштабирования формы и ее содержимого теперь используется описанная ниже логика.

  1. Во время разработки каждый объект ContainerControl регистрирует режим масштабирования и его текущее разрешение в свойствах AutoScaleMode и AutoScaleDimensions соответственно.

  2. Во время выполнения фактическое разрешение хранится в свойстве CurrentAutoScaleDimensions. Свойство AutoScaleFactor динамически вычисляет отношение между разрешением во время выполнения и разрешением во время разработки.

  3. Если при загрузке формы значения CurrentAutoScaleDimensions и AutoScaleDimensions различны, то для масштабирования элемента управления и его дочерних элементов вызывается метод PerformAutoScale. Этот метод приостанавливает размещение и вызывает метод Scale для выполнения фактического масштабирования. Впоследствии значение AutoScaleDimensions обновляется во избежание прогрессивного масштабирования.

  4. Метод PerformAutoScale также вызывается автоматически в перечисленных ниже ситуациях.

    • В ответ на событие OnFontChanged, если используется режим масштабирования Font.

    • Если при возобновлении размещения элементов управления внутри контейнера обнаруживается изменение свойства AutoScaleDimensions или AutoScaleMode.

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

  5. Поведение дочерних элементов управления при масштабировании может изменяться несколькими способами.

    • Можно переопределить свойство ScaleChildren, чтобы указать, следует ли масштабировать дочерние элементы управления.

    • Можно переопределить метод GetScaledBounds для корректировки границ, до которых масштабируется элемент управления, но не логики масштабирования.

    • Можно переопределить метод ScaleControl для изменения логики масштабирования текущего элемента управления.

См. также

  • AutoScaleMode
  • Scale
  • PerformAutoScale
  • AutoScaleDimensions
  • Отрисовка элементов управления с применением визуальных стилей
  • Практическое руководство. Повышение производительности за счет отключения автоматического масштабирования

What is DPI? How is high DPI handled in Windows and WinForms? How can you scale WinForms apps today? Let’s explore the world of high DPI.

In these days of high-density pixel displays, scaling is becoming an increasingly important topic. Let’s dive into the specifics of high DPI and try to understand different problems that high DPI displays cause, and how to address them. We will go through the emergence of high DPI displays and how Windows handles high DPI. Then we will introduce the concept of DPI awareness and see the specifics for WinForms. We will see how scaling works in WinForms. And finally we’ll wrap up with some tips for designing scalable WinForms and an example.

Telerik UI for WinForms offers built-in HDPI support. Learn more or download a trial and see for yourself.

Understanding DPI

Dots per inch, more commonly known as DPI, represents the number of dots that can be placed in a one-inch line during printing. When it comes to monitors the proper term is PPI (pixels per inch), but the two terms are mostly interchangeable today.

At the beginning Microsoft Windows used a default display DPI of 96 PPI, and Apple/Macintosh used 72 PPI. While the reasoning behind this comes from the 72 points per inch standard and Microsoft wanting to have 33% more space for better image and font drawing, you can imagine the problems that arose with the new monitors that support higher pixel density.

The solution is the concept of logical PPI. The operating system creates a virtual screen with default DPI (let’s say 96). This virtual screen is used by the software to render its content on. Then the OS draws the virtual screen to the physical one.

Today there are many devices with different DPIs—there are many monitors with 200-300 DPI and smartphones going as high as 800 DPI. This means that if you draw a square of 4×4 pixels, it will look physically smaller on a higher DPI device than on a lower DPI device.

Our 4×4 pixel square looks quite smaller on the second display, where we have four times as many pixels as the first display. But if we are to scale our square by a factor of two on the second display, we will get our desired result.


Scaling in Windows

So how does Windows handle high DPI? Starting with Windows XP, Microsoft introduced the GDI+ library, which allows the scaling of fonts and some other UI elements. Unfortunately, this would mostly make the text bigger in a high DPI setting, which was problematic. Microsoft added some support for GDI/GDI+ scaling recently. It does not work flawlessly—it generally scales fonts and some graphics. When using it you should always be cautious and check if the result is better or not. 

Windows Vista introduced DPI awareness and DPI virtualization. An application can declare itself as DPI-aware and scale its own UI elements. If not, the OS will use DPI virtualization, which will render the application using a native 96 DPI as a bitmap and then scale the bitmap on the screen. This would produce correct, but fuzzy rendering of the application. There is also a global option to turn off the virtualization and use the old XP-style rendering. At the same time, WPF appeared with .NET Framework 3.0. Applications designed with WPF natively support DPI scaling. DirectX and vector graphics is used for drawing their elements and scaling comes out of the box. WPF has one limitation—it cannot scale hosted controls.

Windows 7 made DPI per user and enabled easier DPI switching with log off only. The OS also reads the monitor DPI and sets a recommended scaling. The standard settings are 100% (96 DPI), 125% (120 DPI), and 150% (144 DPI). Windows 8 removed the numerical values and added some minor improvements. With Windows 8.1 came per monitor DPI scaling, which was improved by the Windows 10 Creators Update (1703). More scale factors were added as well—175%, 200%, 225% etc. And the user can set a custom scale factor up to 500%.

DPI Awareness

So far so good. Windows has support for HDPI (high DPI) scaling. But how do we approach it? When building a DPI-aware application, firstly we must declare it as such, so the OS would know how to handle it. Furthermore, you can have DPI awareness per process or per thread.

There are four different DPI awareness modes in Windows.

  • Unaware – bitmap stretching is used.
  • System – Introduced in Windows Vista. The OS treats all displays with same DPI of the primary display. Bitmap stretching is used.
  • Per-Monitor – Introduced in Windows 8.1. Every display has its own DPI and the application window is notified of DPI change.
  • Per-Monitor V2 – Introduced with Windows 10 Creators Update (1703). On top of the previous mode there is automatic scaling of the non-client area and certain controls and dialogs.

Here is a list of the different ways you can use to choose DPI awareness mode for an application:

  • In the application manifest file
  • Native API
  • In app.config—only available for .NET Framework 4.7/4.8 Windows Forms
  • A static method in .NET Core 3.0/.NET5 for Windows Forms

Manifest File

The most common way to declare an application as DPI aware is through the application manifest file. There are two settings that you can use — <dpiAware> and <dpiAwareness>. Here is a small table that describes the different states that you can use with each setting.

Minimum Supported OS

States

DPI Mode

<dpiAware>

Windows Vista

false

Unaware

true

System aware

true/pm

Per-monitor aware

<dpiAwareness>

Windows 10, version 1607

unaware

Unaware

system

System aware

PerMonitor

Per-monitor aware

PerMonitorV2

Per-monitor-v2 aware

You can use both settings in the manifest file. If so, Windows 10, version 1607, will ignore <dpiAware> and use only the newer <dpiAwareness>. Older versions will ignore <dpiAwareness>. Here is how the manifest should look if you want to configure DPI awareness for different Windows versions.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">

  <asmv3:application>

    <asmv3:windowsSettings>

      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>

      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>

    </asmv3:windowsSettings>

  </asmv3:application>

</assembly>

Native API

There are three native API calls that can set awareness now:

  • SetProcessDpiAware — Windows Vista or later
  • SetProcessDpiAwareness — Windows 8.1 or later
  • SetProcessDpiAwarenessContext — Windows 10, version 1607 or later

The latest API offers PerMonitorV2 support, so it is the currently recommend one.

NB! You must set the DPI mode before your window (HWND) has been created.

App.config for Windows Forms Only

Another way that was introduced for Windows Forms in .NET Framework 4.7 is in the app.config file. Microsoft added a new element to add different features to a Windows Forms app called <System.Windows.Forms.ApplicationConfigurationSection>. In order to use it you need to do the following:

  • Declare compatibility with Windows 10 in the manifest file:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">

  <application>

    <!-- Windows 10 compatibility -->

    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

  </application>

</compatibility>

  • Use the new element in the app.config file:

<System.Windows.Forms.ApplicationConfigurationSection>

  <add key="DpiAwareness" value="PerMonitorV2" />

</System.Windows.Forms.ApplicationConfigurationSection>

  • Call the static method in your application’s entry point Application.EnableVisualStyles(); 

With the <System.Windows.Forms.ApplicationConfigurationSection> element you can set other DPI features as well. For more information you can check this article.

NB! Using <dpiAware>/<dpiAwareness> in the manifest file overrides the settings defined in the app.config file.

A Static Method in .NET Core 3.0/.NET5 Preview

With .NET Core 3.0 Microsoft introduced a new way to set a high DPI mode for Windows Forms. A static method called Application.SetHighDpiMode(HighDpiMode), where HighDpiMode is enum with the following values:

  • DpiUnaware
  • SystemAware
  • PerMonitor
  • PerMonitorV2
  • DpiUnawareGdiScaled

The call must be done at the application entry point. If you have used a manifest file to set the DPI awareness mode, this method call will fail.

Mixed-Mode DPI Scaling

Windows 10 Anniversary update, build 1607, added mixed-mode support for DPI scaling. The main idea is to set DPI scaling to top level windows that is different than the DPI scaling of the process. This is done by using the API SetThreadDpiAwarenessContext. You can call the API just before creating a new window. After the window has been created you can call the API again to revert to the original mode. For more information on the topic I would suggest reading this article.

DPI and WinForms

So, when it comes to Windows Forms, you can use a variety of techniques to define DPI awareness, depending on the framework you are using. With the development of .NET Core and the upcoming .NET5 Microsoft is moving towards using a static method inside your application to define HDPI support. But if you are using an older framework, you can safely use the manifest file or the app.config file. Native calls are generally not recommended. Just keep in mind that the manifest file declaration overrides the other declarations.

At the beginning Windows Forms did not support modern scaling. Starting with .NET Framework 4.5.1, Microsoft made improvements for HDPI scenarios in Windows Forms. In .NET Framework 4.5.2 the following controls can be resized using the system DPI setting:

  • PropertyGrid
  • TreeView
  • ComboBox
  • ToolStripComboBox
  • ToolStripMenuItem
  • Cursor
  • DataGridView
  • DataGridViewComboBoxColumn

Version 4.6 added support for the following controls:

  • DomainUpDown
  • NumericUpDown
  • DataGridViewComboBoxColumn
  • DataGridViewColumn
  • ToolStripSplitButton

This behavior can be enabled with a setting in the app.config file:

<appSettings>

    <add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />

</appSettings>

.NET Framework 4.7 introduced several quality improvements to the DPI scaling in Windows Forms.

  • Single-pass scaling, which would ensure that the controls are scaled only once. Scaling was performed in several passes before and some controls were not scaled correctly.
  • More controls are now supported, like MonthCalendar and CheckedListBox.
  • New events for handling dynamic DPI changes – DpiChanged, DpiChangedAfterParent, DpiChangedBeforeParent.
  • New methods – DeviceDpi, ScaleBitmapLogicalToDevice, LogicalToDeviceUnits.

DPI Scaling in WinForms

The WinForms platform has its own scaling mechanism, which calculates the scaling difference between the system that the form has been designed on and the system it is running on. Then it modifies the size and the location of all controls according to the calculated factor. Note that this scaling will only trigger if your application declares to be DPI-aware, otherwise it will be rendered in the 96 DPI sandbox and the bitmap scaling of the OS will be used.

You have probably noticed the following two properties in you designer generated files:

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

In the AutoScaleDimensions property the Visual Studio designer will serialize the dimensions of the unit used for comparison (either Font or DPI). These are the dimensions of the unit on the system the form is being designed on. When you run the form on a system with different settings, its dimensions are obtained and compared against the serialized dimensions. The scaling factor is computed based on that and then it is applied.

The AutoScaleMode property indicates the method of calculating the scale factor. Depending on it, the scaling mechanism will calculate the scale factor according to the dimensions of the system font or the system DPI. If you set it to None, no scaling will be performed at all.

NB! All containers must use the same AutoScaleMode. Mixing different scale modes is not supported and can lead to unexpected results.

When the scale factor is calculated, the framework calls the Scale method of the form which basically recalculates the Size and the Location of all child controls on it. Then their Scale method is also called so they can properly scale.

Unfortunately, this mechanism does not work as seamlessly as the one in WPF where you don’t even have to worry about scaling. But with the improvements that started with .NET Framework 4.5.1 the scaling has been improved.

For more information check out this article.

HDPI Support with Telerik UI for WinForms

As of 2017, Telerik UI for WinForms offers built-in HDPI support. The good news is that you don’t need the latest framework—the feature is supported with .NET Framework 2.0 and up. Check out the blog post and the documentation for more information.

The Telerik document processing library (RadSpreadProcessing, RadWordProecessing or RadPdfProcessing) is referencing assemblies which are used in WPF. All WPF-based applications are DPI-aware by default and this is declared in the manifests of the WPF assemblies. Therefore, if you use the document processing library in WinForms applications that are not DPI-aware, they might suddenly become DPI-aware at run time when you instantiate a type from the DPL assemblies (when the DPL assemblies are loaded by the CLR, this will also load the WPF assemblies which they depend on, which in turn will make the application DPI-aware). If you intend to use your application on machines where the DPI scaling is larger than 100 percent, you should explicitly set the application to be DPI-unaware:

private void workbookTestButton_Click(object sender, EventArgs e)

{

    SetProcessDpiAwareness(_Process_DPI_Awareness.Process_DPI_Unaware);

    Workbook wb = new Workbook();

}

[DllImport("shcore.dll")]

static extern int SetProcessDpiAwareness(_Process_DPI_Awareness value);

enum _Process_DPI_Awareness

{

    Process_DPI_Unaware = 0,

    Process_System_DPI_Aware = 1,

    Process_Per_Monitor_DPI_Aware = 2

}

General Tips for Designing Scalable WinForms

If you are designing a scalable application there are few simple guidelines that will help a lot:

  • Design your forms under 96 DPI (100%) – as mentioned above, Visual Studio will serialize the scaling size of the form at design time and there are often problems when the form has been designed under higher DPI.
  • Always test different scenarios – running the application at different DPI settings; changing the DPI while it is running; moving the application to another monitor with different DPI.
  • Design the interface of your forms so that it can “reflow” – use Anchored, Docked, AutoSized controls where possible.
  • All containers must use the same AutoScaleMode.
  • Use default font size (8.25 px) on all containers. If you need custom font size for a specific control, set it on that control instead on the container class.
  • Pay special attention to whether the font size scales correctly. If not, you will have to manually scale the font size for specific controls.
  • If you have some custom layout logic, always keep in mind that the sizes and the locations of the controls will be different if the form is scaled. Also keep in mind that you should manually scale any constants you use if they denote pixels.

Example with Telerik UI for WinForms

When using Telerik UI for WinForms, you have automatic scaling out of the box even for .NET Framework 2.0. You will just have to mark your application as DPI-aware. You can look at the example here: C#/VB . The examples work for any .NET Framework after version 2.0. You will need to install a Telerik UI for WinForms trial, which you can do through the link below.

Try Telerik UI for WinForms

Conclusion

WinForms has come a long way since its emergence. At its early days there was no need for DPI scaling. Nowadays, with the development of better displays, scaling is becoming more and more mandatory. And since there are a lot of applications written back in the day using WinForms, there is a need for the platform to grow. Today WinForms has a good HDPI support and with Telerik UI on top you can have seamless HDPI application out of the box.

Editor’s Note: This post was originally published in 2014 by Ivan Todorov, and has since been revised and updated for accuracy and completeness.

Размещение в пользовательском окне элементов управления

Будем называть наш класс, наследующий от Form, пользовательским окном. Разместим в окне элемент управления типа кнопки, которая порождается классом Button. Все элементы управления являются визуальными и наследуют от класса Control. В следующей программе показано, как создать объект Button, поместить его на поверхность формы и подключить обработчик к событию Click этой кнопки. Продемонстрированы разные способы настройки и управления кнопками.

using System;
using System.Windows.Forms;
using System.Drawing;
    
namespace MyApp
{
    // Наследует библиотечному классу
    class MyForm : Form
    {
        // Конструктор с настройками
        public MyForm()
        {
            // Начальная настройка окна
            this.Text = "Форма с кнопками";
    
            // Создание и настройка первой кнопки
            Button btn = new Button(); // Создаем первую объект-кнопку
            btn.Text = "Кнопка 1"; // Надпись на первой кнопке
            btn.AutoSize = true; // Подстройка размеров под надпись
            int left = (this.ClientSize.Width - btn.Width) / 2;
            int top = (this.ClientSize.Height - btn.Height) / 2;
            btn.Location = new Point(left, top);   // Координаты кнопки в 
                                                // клиентской области формы
            btn.Click += ButtonOnClick;// Подключение обработчика к событию Click
            btn.Parent = this;  // Присоединяем к форме первую кнопку
                                // и освобождаем ссылочную переменную
    
            // Создание и настройка второй кнопки
            btn = new Button(); // Используем освободившуюся адресную переменную
            btn.Text = "Кнопка 2";
            btn.AutoSize = true;
            top += (this.ClientSize.Height - top) / 2;  // Расположим ниже
            btn.Location = new Point(left, top);
            btn.Click += ButtonOnClick; // Присоединяем тот же обработчик
            btn.Enabled = false;    // Сначала недоступна
            this.Controls.Add(btn); // Другой способ присоединения к форме
            btn = null; // Освобождаем ссылку
        }
    
        // Флаг состояния доступности (закрытое поле класса)
        private bool enableButton = true;
    
        void ButtonOnClick(object sender, EventArgs e)
        {
            if (enableButton)
                MessageBox.Show("Произошел щелчок на первой кнопке!");
            else
                MessageBox.Show("Произошел щелчок на второй кнопке!");
    
            // Перебираем коллекцию из двух кнопок и меняем доступность
            foreach (Control ctrl in this.Controls)
                ctrl.Enabled = !ctrl.Enabled;
    
            enableButton = !enableButton;
        }
    }
}


Листинг
6.15 .
Код файла MyForm.cs

using System.Windows.Forms;
// Лишние пространства имен удалили
    
namespace MyApp
{
    class EntryPoint
    {
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MyForm());
        }
    }
}


Листинг
6.16 .
Код файла Program.cs

Использованный в функции Main() метод Application.EnableVisualStyles() класса позволяет задать современный внешний вид для некоторых элементов управления. Созданная форма с кнопками будет такой

Размещенные на форме кнопки являются ее дочерними объектами и размещаются в коллекции Controls. Учитывая, что в нашем примере дочерними элементами являются только две кнопки, мы не принимали специальные меры для их распознавания. Но в более сложных случаях элементам нужно присваивать имена в их свойстве Name и распознавать уже по значению этого свойства. Для поиска конкретного объекта Control годится как простой перебор всех элементов коллекции Controls формы, так и метод System.Windows.Forms.Control.ControlCollection.Find(string, bool).

Все дочерние элементы формы располагаются в Z-порядке ( Z -последовательности), определяющим их видимость на экране, и могут перекрывать друг друга. Начальный Z -порядок определается порядком добавления элементов к форме и противоположен индексам элементов в коллекции формы: более поздние элементы отображаются верхними. Для изменения Z -порядка в процессе выполнения программы используются методы BringToFront() и SendToBack() класса Control.

Создание расширений элементов управления

В объектно-ориентированном программировании существует два способа передачи кода одного класса другому: это композиция и наследование. Композицией называется создание экземпляра одного класса внутри другого с последующим использованием объекта, а наследование передает код базового класса в производный неявно, сливая вместе два или целую цепочку классов.

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

Для примера создадим класс, расширяющий библиотечный класс Button, в котором определим такое поведение кнопки, что если надпись на ней не превышает заданную длину, то она остается недоступной на форме. Для изменения надписи кнопки предусмотрим текстовое поле ввода типа TextBox.

using System;
using System.Windows.Forms;
using System.Drawing;
    
namespace MyApp
{
    // Наследует библиотечному классу
    class MyForm : Form
    {
        // Локальные поля формы
        TextBox txtbox;
        ExtensionButton btn;
    
        // Конструктор с настройками
        public MyForm()
        {
            // Начальная настройка окна
            this.Text = "Форма с \"умной\" кнопкой";
    
            // Создание и настройка объекта TextBox
            txtbox = new TextBox();
            txtbox.Text = "Кнопка";
            txtbox.CharacterCasing = CharacterCasing.Upper;
            txtbox.Width = 150;
            int left = (this.ClientSize.Width - txtbox.Width) / 2;
            int top = (this.ClientSize.Height - txtbox.Height) / 3;
            txtbox.Location = new Point(left, top);
            txtbox.Focus();
            txtbox.Parent = this;
            // Подписка объекта TextBox на событие 
            txtbox.TextChanged += new EventHandler(txtbox_TextChanged);
    
            // Создание и настройка объекта умной кнопки
            btn = new ExtensionButton(); // Создаем умную кнопку
            btn.StrTextBox = txtbox.Text; // Начальная надпись на кнопке
            btn.AutoSize = true; // Подстройка размеров под надпись
            left = (this.ClientSize.Width - btn.Width) / 2;
            top += (this.ClientSize.Height - top) / 2;
            btn.Location = new Point(left, top);
            btn.Parent = this;  // Присоединяем кнопку к форме
        }
    
        void txtbox_TextChanged(object sender, EventArgs e)
        {
            // Меняем значение надписи на кнопке
            // с одновременным контролем доступности
            btn.StrTextBox = txtbox.Text;
        }
    }
    
    // Класс расширения кнопки
    class ExtensionButton : Button
    {
        // Локальное поле-константа
        private const int MIN_DISABLED = 5;
    
        // Публичное свойство
        public string StrTextBox
        {
            set 
            { 
                this.Text = value;
                bool enabled = value != null && 
      value.Length > MIN_DISABLED;
                this.Enabled = enabled;
            }
        }
    }
}


Листинг
6.17 .
Код файла MyForm.cs

using System.Windows.Forms;
// Лишние пространства имен удалили
    
namespace MyApp
{
    class EntryPoint
    {
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MyForm());
        }
    }
}


Листинг
6.18 .
Код файла Program.cs

Результат выполнения программы будет таким

Автоматическое масштабирование окон

Проектируя приложение, разработчик не знает, на экранах с каким разрешением оно будет работать. Если не принять специальных мер, то при установке малых разрешений окно будет казаться большим, а при установке больших — маленьким относительно экрана. Для автоматического масштабирования окон приложения в зависимости от разрешения экрана в классе Form имеются два свойства: AutoScaleDimensions и AutoScaleMode, наследуемые от класса ContainerControl, которые и решают эту задачу.

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

AutoScaleDimensions = new Size(4, 8);
AutoScaleMode = AutoScaleMode.Font;

или

AutoScaleDimensions = new SizeF(96, 96);
AutoScaleMode = AutoScaleMode.Dpi

или

AutoScaleDimensions = new SizeF(120, 120);
AutoScaleMode = AutoScaleMode.Dpi

Перечисление AutoScaleMode имеет значения: Dpi, Font, Inherit, None. Значение Font устанавливает привязку к размерам стандартного значения шрифта Windows, значение Dpi — к разрешению экрана.

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

Чем асинхронная логика (схемотехника) лучше тактируемой, как я думаю, что помимо энергоэффективности — ещё и безопасность.

Hrethgir 14.05.2025

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

Многопоточные приложения на C++

bytestream 14.05.2025

C++ всегда был языком, тесно работающим с железом, и потому особеннно эффективным для многопоточного программирования. Стандарт C++11 произвёл революцию, добавив в язык нативную поддержку потоков,. . .

Stack, Queue и Hashtable в C#

UnmanagedCoder 14.05.2025

Каждый опытный разработчик наверняка сталкивался с ситуацией, когда невинный на первый взгляд List<T> превращался в узкое горлышко всего приложения. Причина проста: универсальность – это прекрасно,. . .

Как использовать OAuth2 со Spring Security в Java

Javaican 14.05.2025

Протокол OAuth2 часто путают с механизмами аутентификации, хотя по сути это протокол авторизации. Представьте, что вместо передачи ключей от всего дома вашему другу, который пришёл полить цветы, вы. . .

Анализ текста на Python с NLTK и Spacy

AI_Generated 14.05.2025

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

Реализация DI в PHP

Jason-Webb 13.05.2025

Когда я начинал писать свой первый крупный PHP-проект, моя архитектура напоминала запутаный клубок спагетти. Классы создавали другие классы внутри себя, зависимости жостко прописывались в коде, а о. . .

Обработка изображений в реальном времени на C# с OpenCV

stackOverflow 13.05.2025

Объединение библиотеки компьютерного зрения OpenCV с современным языком программирования C# создаёт симбиоз, который открывает доступ к впечатляющему набору возможностей. Ключевое преимущество этого. . .

POCO, ACE, Loki и другие продвинутые C++ библиотеки

NullReferenced 13.05.2025

В C++ разработки существует такое обилие библиотек, что порой кажется, будто ты заблудился в дремучем лесу. И среди этого многообразия POCO (Portable Components) – как маяк для тех, кто ищет. . .

Паттерны проектирования GoF на C#

UnmanagedCoder 13.05.2025

Вы наверняка сталкивались с ситуациями, когда код разрастается до неприличных размеров, а его поддержка становится настоящим испытанием. Именно в такие моменты на помощь приходят паттерны Gang of. . .

Создаем CLI приложение на Python с Prompt Toolkit

py-thonny 13.05.2025

Современные командные интерфейсы давно перестали быть черно-белыми текстовыми программами, которые многие помнят по старым операционным системам. CLI сегодня – это мощные, интуитивные и даже. . .

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как выключить погашение экрана на компьютере windows 10
  • Windows символическая ссылка на файл
  • Проводник windows стандартные приложения windows
  • 0x80004005 windows 10 ltsc доступ по сети
  • Аварийная перезагрузка windows 10