#line 20 "..\..\MainWindow.xaml"
this.MyCalendar.SelectedDatesChanged += new
System.EventHandler<System.Windows.Controls.SelectionChangedEventArgs>(
this.MyCalendar_OnSelectedDatesChanged);
Он сообщает инфраструктуре о том, что элементу управления в строке 20 файла XAML назначен обработчик события
SelectedDatesChanged
, как показано в предыдущем коде.
Наконец, класс
MainWindow
определяет и реализует метод по имени
InitializeComponent()
. Вы могли бы ожидать, что данный метод содержит код, который настраивает внешний вид и поведение каждого элемента управления, устанавливая его разнообразные свойства (
Height
,
Width
,
Content
и т.д.). Однако это совсем не так! Как тогда элементы управления получают корректный пользовательский интерфейс? Логика в методе
InitializeComponent()
выясняет местоположение встроенного в сборку ресурса, который именован идентично исходному файлу
*.xaml
:
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater =
new System.Uri("/WpfTesterApp;component/mainwindow.xaml",
System.UriKind.Relative);
#line 1 "..\..\MainWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
Здесь возникает вопрос: что собой представляет этот встроенный ресурс?
Роль BAML
Как и можно было предположить, формат BAML является компактным двоичным представлением исходных данных XAML. Файл
*.baml
встраивается в виде ресурса (через сгенерированный файл
*.g.resources
) в скомпилированную сборку. Ресурс BAML содержит все данные, необходимые для настройки внешнего вида и поведения виджетов пользовательского интерфейса (т.е. свойств вроде
Height
и
Width
).
Здесь важно понимать, что приложение WPF содержит внутри себя двоичное представление (BAML) разметки. Во время выполнения ресурс BAML извлекается из контейнера ресурсов и применяется для настройки внешнего вида и поведения всех окон и элементов управления.
Вдобавок запомните, что имена таких двоичных ресурсов идентичны именам написанных автономных файлов
*.xaml
. Тем не менее, отсюда вовсе не следует необходимость распространения файлов
*.xaml
вместе со скомпилированной программой WPF. Если только не строится приложение WPF, которое должно динамически загружать и анализировать файлы
*.xaml
во время выполнения, то поставлять исходную разметку никогда не придется.
Разгадывание загадки Main()
Теперь, когда известно, как работает процесс
msbuild.exe
, откройте файл
Арр.g.cs
. В нем обнаружится автоматически сгенерированный метод
Main()
, который инициализирует и запускает ваш объект приложения:
public static void Main() {
WpfTesterApp.App app = new WpfTesterApp.App();
app.InitializeComponent();
app.Run();
}
Метод
InitializeComponent()
конфигурирует свойства приложения, включая
StartupUri
и обработчики событий
Startup
и
Exit
:
public void InitializeComponent() {
#line 5 "..\..\App.xaml"
this.Startup += new System.Windows.StartupEventHandler(this.App_OnStartup);
#line default
#line hidden
#line 5 "..\..\App.xaml"
this.Exit += new System.Windows.ExitEventHandler(this.App_OnExit);
#line default
#line hidden
#line 5 "..\..\App.xaml"
this.StartupUri =
new System.Uri("MainWindow.xaml", System.UriKind.Relative);
#line default
#line hidden
}
Взаимодействие с данными уровня приложения
Вспомните, что в классе
Application
имеется свойство по имени
Properties
, которое позволяет определить коллекцию пар "имя/значение" посредством индексатора типа. Поскольку этот индексатор предназначен для оперирования на типе
System.Object
, в коллекцию можно сохранять элементы любого вида (в том числе экземпляры специальных классов) с целью последующего извлечения по дружественному имени. С использованием такого подхода легко разделять данные между всеми окнами в приложении WPF.
В целях иллюстрации вы обновите текущий обработчик события
Startup
, чтобы он проверял входящие аргументы командной строки на присутствие значения
/GODMODE
(распространенный мошеннический код во многих играх). Если оно найдено, тогда значение
bool
по имени
GodMode
внутри коллекции свойств устанавливается в
true
(в противном случае оно устанавливается в
false
).
Звучит достаточно просто, но как передать обработчику события
Startup
входные аргументы командной строки (обычно получаемые методом
Main()
)? Один из подходов предусматривает вызов статического метода
Environment.GetCommandLineArgs()
. Однако те же самые аргументы автоматически добавляются во входной параметр
StartupEventArgs
и доступны через свойство
Args
. Ниже приведена первая модификация текущей кодовой базы: