Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
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. Ниже приведена первая модификация текущей кодовой базы:
private void App_OnStartup(object sender, StartupEventArgs e)
{
Application.Current.Properties["GodMode"] = false;
// Проверить входные аргументы командной строки
// на предмет наличия флага /GODMODE.
foreach (string arg in e.Args)
{
if (arg.Equals("/godmode",StringComparison.OrdinalIgnoreCase))
{
Application.Current.Properties["GodMode"] = true;
break;
}
}
}
Данные уровня приложения доступны из любого места внутри приложения WPF. Для обращения к ним потребуется лишь получить точку доступа к глобальному объекту приложения (через Application.Current) и просмотреть коллекцию. Например, обработчик события Click для кнопки можно было бы изменить следующим образом:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Указал ли пользователь /godmode?
if ((bool)Application.Current.Properties["GodMode"])
{
MessageBox.Show("Cheater!"); // Мошенник!
}
}
Если теперь ввести аргумент командной строки /godmode на вкладке Debug (Отладка) в окне свойств проекта и запустить программу, то отобразится окно сообщения и программа завершится. Можно также запустить программу из командной строки с помощью показанной ниже команды (предварительно открыв окно командной строки и перейдя в каталог bin/debug):
WpfAppAllCode.exe /godmode
Отобразится окно сообщения и программа завершится.
На заметку! Вспомните, что аргументы командной строки можно указывать внутри Visual Studio. Нужно просто дважды щелкнуть на значке Properties (Свойства) в окне Solution Explorer, в открывшемся диалоговом окне перейти на вкладку Debug (Отладка) и ввести /godmode в поле Command line arguments (Аргументы командной строки).
Обработка закрытия объекта Window
Конечные пользователи могут завершать работу окна с помощью многочисленных встроенных приемов уровня системы (например, щелкнув на кнопке закрытия X внутри рамки окна) или вызвав метод Close() в ответ на некоторое действие с интерактивным элементом (скажем, выбор пункта меню File Exit (Файл Выход)). Инфраструктура WPF предлагает два события, которые можно перехватывать для выяснения, действительно ли пользователь намерен закрыть окно и удалить его из памяти. Первое такое событие — Closing, которое работает в сочетании с делегатом CancelEventHandler.
Указанный делегат ожидает целевые методы, принимающие тип System.ComponentModel.CancelEventArgs во втором параметре. Класс CancelEventArgs предоставляет свойство Cancel, установка которого в true предотвращает фактическое закрытие окна (что удобно, когда пользователю должен быть задан вопрос о том, на самом ли деле он желает закрыть окно или сначала нужно сохранить результаты проделанной работы). Если пользователь действительно хочет закрыть окно, тогда свойство CancelEventArgs.Cancel можно установить в false (стандартное значение). В итоге сгенерируется событие Closed (которое работает с делегатом System.EventHandler), представляющее собой точку, где окно полностью и безвозвратно готово к закрытию.
Модифицируйте класс МаinWindow для обработки упомянутых двух событий, добавив в текущий код конструктора такие операторы:
public MainWindow()
{
InitializeComponent();
this.Closed+=MainWindow_Closed;
this.Closing += MainWindow_Closing;
}
Теперь реализуйте соответствующие обработчики событий:
private void MainWindow_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
// Выяснить, на самом ли деле пользователь хочет закрыть