Метод InitializeComponent()
В данный момент класс MainWindow не содержит реальной функциональности. Однако он включает одну важную деталь — конструктор по умолчанию, который вызывает метод InitializeComponent(), когда создается экземпляр класса.
Метод InitializeComponent() играет ключевую роль в приложениях WPF. Метод InitializeComponent() не видим в исходном коде, потому что генерируется автоматически при компиляции приложения. По существу все, что делает InitializeComponent() — это вызов метода LoadComponent() класса System.Windows.Application. Метод LoadComponent() извлекает код BAML(скомпилированный XAML) из сборки и использует его для построения пользовательского интерфейса.
При разборе BAML он создает объекты каждого элемента управления, устанавливает их свойства и присоединяет все обработчики событий.
Именование элементов
В классе отделенного кода часто требуется программно манипулировать элементами управления. Например, необходимо читать либо изменять свойства, а также присоединять или отсоединять обработчики событий на лету. Чтобы обеспечить такую возможность, элемент управления должен включать XAML-атрибут Name.
Можно внести это изменение в документ XAML вручную или выбрать элемент в визуальном конструкторе Visual Studio и установить свойство Name в окне Properties (Свойства).
В обоих случаях атрибут Name сообщит анализатору XAML о необходимости добавить поле следующего вида к автоматически сгенерированной части класса MainWindow:
private System.Windows.Controls.Grid MyGrid;
Теперь с этим элементом можно взаимодействовать в коде класса MainWindow указывая имя MyGrid:
MessageBox.Show(«Размер сетки: » + MyGrid.ActualWidth + «x» + MyGrid.ActualHeight);
Показанное ранее свойство Name является частью языка XAML и используется для того, чтобы помочь в интеграции класса отделенного кода. Из-за того, что многие классы определяют собственное свойство Name, происходит некоторая путаница. (Примером может служить базовый класс FrameworkElement, от которого наследуются все элементы WPF.) Анализаторы XAML элегантно справляются с этой проблемой. Можно либо установить XAML-свойство Name (используя префикс х:), либо свойство Name, относящееся к действительному элементу (опустив префикс).
В любом случае результат один и тот же — указанное имя используется в файле автоматически сгенерированного кода и применяется для установки свойства Name.
Такой трюк работает только в том случае, если включающий свойство Name класс оснащен атрибутом RuntimeNameProperty. Атрибут RuntimeNameProperty указывает на то, какое свойство должно трактоваться в качестве имени экземпляра этого типа. (Очевидно, обычно таким свойством является Name.) Класс FrameworkElement содержит атрибут RuntimeNameProperty, так что никаких проблем нет.
В традиционном приложении Windows Forms каждый элемент управления имеет имя. В приложении WPF такого требования нет. Однако при создании окна перетаскиванием элементов на поверхность визуального конструктора Visual Studio каждому элементу назначается автоматически сгенерированное имя. Таково соглашение.
Классы отделенного кода XAML
Язык XAML позволяет конструировать пользовательский интерфейс, но для создания функционирующего приложения необходим способ подключения обработчиков событий. XAML позволяет легко это сделать с помощью атрибута Class, показанного ниже:
Префикс пространства имен «x» помещает атрибут Class в пространство имен XAML, что означает более общую часть языка XAML. Фактически атрибут Class сообщает анализатору XAML, чтобы он сгенерировал новый класс с указанным именем. Этот класс наследуется от класса, именованного элементом XML. Другими словами, этот пример создает новый класс по имени MainWindow, который наследуется от базового класса Window.
Класс MainWindow генерируется автоматически во время компиляции. И здесь начинается самое интересное. Вы можете предоставить часть класса MainWindow, которая будет объединена с автоматически сгенерированной частью этого класса. Указанная вами часть — блестящий контейнер для кода обработки событий.
Эта «магия» возможна благодаря средству C#, известному под названием частичные классы (partial class). Частичные классы позволяют разделить класс на две или более отдельных части во время разработки, которые соединяются вместе в скомпилированной сборке. Частичные классы могут применяться во многих сценариях управления кодом, но более всего удобны, когда код должен объединяться с файлом, сгенерированным визуальным конструктором.
Среда Visual Studio оказывает помощь, автоматически создавая частичный класс, куда можно поместить код обработки событий. Например, при создании приложения по имени WpfApplication1, содержащего окно по имени MainWindow. Visual Studio начнет с создания следующего базового каркаса класса:
Во время компиляции приложения код XAML, определяющий пользовательский интерфейс (такой как MainWindow.xaml), транслируется в объявление типа CLR, объединенного с логикой файла класса отделенного кода (подобного MainWindow.xaml.сs), формируя один общий модуль.
Метод InitializeComponent()
В данный момент класс MainWindow не содержит реальной функциональности. Однако он включает одну важную деталь — конструктор по умолчанию, который вызывает метод InitializeComponent(), когда создается экземпляр класса.
Метод InitializeComponent() играет ключевую роль в приложениях WPF. По этой причине никогда не следует удалять вызов InitializeComponent() из конструктора окна. В случае добавления к классу окна другого конструктора обязательно предусмотрите в нем вызов InitializeComponent().
Метод InitializeComponent() не видим в исходном коде, потому что генерируется автоматически при компиляции приложения. По существу все, что делает InitializeComponent() — это вызов метода LoadComponent() класса System.Windows.Application. Метод LoadComponent() извлекает код BAML (скомпилированный XAML) из сборки и использует его для построения пользовательского интерфейса.
При разборе BAML он создает объекты каждого элемента управления, устанавливает их свойства и присоединяет все обработчики событий.
Именование элементов
Есть еще одна деталь, которая должна приниматься во внимание. В классе отделенного кода часто требуется программно манипулировать элементами управления. Например, необходимо читать либо изменять свойства, а также присоединять или отсоединять обработчики событий на лету. Чтобы обеспечить такую возможность, элемент управления должен включать XAML-атрибут Name. В предыдущем примере элемент Grid не содержит атрибут Name, поэтому манипулировать им в отделенном коде не получится.
Ниже показано, как назначить имя элементу Grid:
Можно внести это изменение в документ XAML вручную или выбрать элемент в визуальном конструкторе Visual Studio и установить свойство Name в окне Properties (Свойства).
В обоих случаях атрибут Name сообщит анализатору XAML о необходимости добавить поле следующего вида к автоматически сгенерированной части класса MainWindow:
Теперь с этим элементом можно взаимодействовать в коде класса MainWindow указывая имя MyGrid:
Такая техника мало что дает простому примеру, но становится намного важнее, когда требуется читать значения из элементов управления вводом, таких как текстовые поля и списки.
Показанное ранее свойство Name является частью языка XAML и используется для того, чтобы помочь в интеграции класса отделенного кода. Из-за того, что многие классы определяют собственное свойство Name, происходит некоторая путаница. (Примером может служить базовый класс FrameworkElement, от которого наследуются все элементы WPF.) Анализаторы XAML элегантно справляются с этой проблемой. Можно либо установить XAML-свойство Name (используя префикс х:), либо свойство Name, относящееся к действительному элементу (опустив префикс).
В любом случае результат один и тот же — указанное имя используется в файле автоматически сгенерированного кода и применяется для установки свойства Name.
Это значит, что следующая разметка эквивалентна тому, что вы уже видели:
Такой трюк работает только в том случае, если включающий свойство Name класс оснащен атрибутом RuntimeNameProperty. Атрибут RuntimeNameProperty указывает на то, какое свойство должно трактоваться в качестве имени экземпляра этого типа. (Очевидно, обычно таким свойством является Name.) Класс FrameworkElement содержит атрибут RuntimeNameProperty, так что никаких проблем нет.
В традиционном приложении Windows Forms каждый элемент управления имеет имя. В приложении WPF такого требования нет. Однако при создании окна перетаскиванием элементов на поверхность визуального конструктора Visual Studio каждому элементу назначается автоматически сгенерированное имя. Таково соглашение. Если вы не собираетесь взаимодействовать с элементом в коде, то можете удалить атрибут Name из кода разметки.
Управление объявлениями классов и переменных-членов
Многие из этих ключевых полей вы увидите в действии там, где они понадобятся. Давайте в качестве простого примера рассмотрим следующее определение XAML <Window>, в котором используются ключевые слова ClassModifier и FieldModifier, а также x:Name и x:Class:
По умолчанию все определения сгенерированных типов C#/XAML в WPF являются внутренними (internal), а члены — общедоступными (public). Однако на основе показанного определения XAML результирующий автоматически сгенерированный файл содержит тип класса internal с public-членом Button:
Введение в Windows Forms
Для создания графических интерфейсов с помощью платформы .NET применяются разные технологии — Window Forms, WPF, UWP. Однако наиболее простой и удобной платформой до сих пор остается Window Forms или сокращенно WinForms. Данное руководство ставит своей целью дать понимание принципов создания графических интерфейсов с помощью технологии WinForms и работы основных элементов управления.
Создадим первое приложение на C# и Windows Forms. Что нам для этого потребуется? Прежде всего необходим текстовый редактор для написания кода программы. Можно взять любой понравившийся текстовый редактор, например, Visual Studio Code
Также для компиляции и запуска программы нам потребуется .NET SDK. Для его установки перейдем на официальный сайт по ссылке .NET SDK

После установки .NET SDK для первого проекта определим какую-нибудь папку. Например, в моем случае это будет папка C:\dotnet\winforms\helloapp . Откроем терминал/командную строку и перейдем к созданной папке проекта с помощью команды cd
В данном случае мы для создания и запуска проекта мы будем использовать встроенную инфраструктуру .NET CLI, которая устанавливается вместе с .NET SDK.
Для создания проекта в .NET CLI применяется команда dotnet new , после которой указывается тип проWindows Formsекта. Для создания проекта Windows Forms применяется шаблон — winforms . Поэтому введем в терминале команду

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

Структура проекта Windows Forms
Рассмотрим базовую структуру простейшего стандартного проекта Windows Forms:
helloapp.csproj : стандартный файл проекта C#, который соответствует назанию проекта (по умолчанию названию каталога) и описывает все его настройки.
helloapp.csproj.user : дополнительный файл проекта C#, который хранит специфичные для текущего пользователя настройки.
Form1.cs : содержит класс формы, которая по умолчанию запускается при старте приложения
Form1.Designer.cs : он содержит определение компонентов формы, добавленных на форму в графическом дизайнере (графический дизайнер Windows Forms на данный момент официально доступен только в Visual Studio)
Program.cs : определяет класс Program, который запускается при старте приложения и запускает форму Form1
Например, посмотрим на содержимое файла helloapp.csproj
Для компиляции приложения Windows Forms указаны следующие настройки:
OutputType : определяет выходной тип проекта. Должен иметь значение WinExe — то есть выполняемое приложение с расширением exe под Windows
TargetFramework : определяет применяемую для компиляции версию фреймворка .NET. Поскольку при создании проекта была выбрана версия .NET 7, а сам проект зависит от компонентов Windows, то здесь должно быть значение net7.0-windows
Nullable : подключает в проект функционалность ссылочных nullable-типов
UseWindowsForms : указывает, будет ли проект использовать Windows Forms (для этого устанавливается значение true )
ImplicitUsings : подключает в проект функциональность неявно подключаемых глобальных пространств имен
Запуск проекта
Проект по умолчанию не представляет какой-то грандиозной функциональности, тем не менее этот проект мы уже можем запустить. Итак, запустим проект. Для этого выполним команду

При запуске запускается графическая форма, код которой определяет класс Form1:

Запуск приложения
Файл Program.cs определяет точку входа в приложение:
Метод Main снабжен атрибутом [STAThread] . Этот атрибут, грубого говоря, необходим для корректной работы компонентов Windows. В самом методе сначала вызывается метод
который устанавливает некоторую базовую конфигурацию приложения
Затем вызывается метод
в который передается объект отображаемой по умолчанию на экране формы.
То есть, когда мы запустим приложение, сработает метод Main , в котором будет вызван метод Application.Run(new Form1()) , благодаря чему мы увидим форму Form1 на экране.
Определение формы
Теперь посмотрим на определение формы и немного изменим его. Для этого откроем файл Form1.cs в текстовом редакторе. По умолчанию он выглядит следующим образом:
Класс формы — Form1 представляет графическую форму — фактически то окно, которое мы увидим на экране при запуске проекта.
Этот класс определяется как частичный (с модификатором partial ) и наследуется от встроенного класса Form , который содержит базовую функциональность форм.
В самом классе Form1 определен по умолчанию только конструктор, где вызывается метод InitializeComponent() , который выполняет инициализацию компонентов формы из файла дизайнера Form1.Designer.cs . По сути именно код этого файла передается выше через вызов InitializeComponent()
Теперь изменим его код следующим образом:
В данном случае на форму добавляется кнопка, и у нее настраиваются различные свойства, как текст, положение на форме, размер, а также обработчик нажатия. Заново запустим проект, и нам отобразится та же форма, которая теперь будет содержать кнопку. И по нажатию на кнопку появится маленькое окно с сообщением.
What does InitializeComponent() do, and how does it work in WPF?
What does InitializeComponent() do, and how does it work in WPF?
In general first, but I would especially be interested to know the gory details of order of construction, and what happens when there are Attached Properties.
![]()
2 Answers 2
The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl ) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).