Разработка пользовательского интерфейса на основе технологии Windows Presentation Foundation
Шрифт:
Прирученная кнопка пока ничего не делает. Это будет исправлено в следующем пункте.
Комментарии
1. В данном пункте демонстрируется возможность отсоединения метода-обработчика от события, с которым он ранее был связан. Для этого используется операция –=, слева от которой указывается событие, а справа – тот обработчик, который надо отсоединить от события.
2. В обработчике button2_MouseMove определяются текущие размеры компонента Canvas, чтобы обеспечить случайное перемещение дикой кнопки только в пределах этого компонента (метод r.NextDouble возвращает случайное вещественное число в полуинтервале [0; 1), при этом вычитание числа 5 гарантирует, что дикая кнопка будет видна
Поскольку мы не присвоили компоненту Canvas имя, нам пришлось обращаться к нему через его родителя, вызвав для окна его свойство Content и, кроме того, выполнив явное приведение типа с помощью операции as. Вместо приведения к типу Canvas можно было бы выполнить приведение к типу FrameworkElement – первому типу в иерархии наследования компонентов, в котором определены свойства, связанные с размерами. Можно было выполнить приведение к типу Panel – непосредственному потомку FrameworkElement, который является предком всех группирующих компонентов. Заметим, что выполнить приведение класса Canvas к типу Control не удастся, так как группирующие компоненты к данному типу не относятся (потомками Control являются, в частности, компоненты, имеющие свойство Content, например кнопки).
3. Следует обратить внимание на способ, с помощью которого в обработчике button2_MouseMove проверяется, нажата ли клавиша Ctrl. Обычно дополнительная информация о произошедшем событии передается в обработчик с помощью второго параметра e. Например, в обработчике button2_MouseMove с помощью данного параметра (типа MouseEventArgs) можно определить текущую позицию мыши (вызвав метод e.GetPosition) или состояние кнопок мыши (вызвав, например, свойство e.LeftButton и сравнив его с одним из его возможных значений – MouseButtonState.Pressed или MouseButtonState.Released). Однако информацию о нажатых в данный момент клавишах параметр e типа MouseEventArgs не содержит. Тем не менее подобную информацию можно получить с помощью статического свойства IsKeyDown класса Keyboard. Это же свойство мы использовали при исправлении обнаруженного недочета, чтобы в случае, если нажата клавиша пробела, обработчик button2_MouseMove не выполнял никаких действий.
4. Обратите также внимание на то, что поле r в классе WainWinbdow не только описывается, но и сразу инициализируется (с помощью конструктора Random без параметров). В какой момент выполняется данная инициализация? По правилам языка C#, явно указанные операторы инициализации всех полей класса автоматически помещаются в начало любого конструктора данного класса. Таким образом, поле r будет инициализировано в начале выполнения конструктора окна (перед выполнением оператора InitializeComponent, указанного в теле данного конструктора). Разумеется, можно было бы поступить иначе: описать поле r типа Random без его инициализации
и поместить в конструктор окна оператор
1.5. Присоединение к событию другого обработчика
Для того чтобы прирученная кнопка при нажатии на нее выполняла какие-либо действия, можно добавить эти действия к уже имеющемуся обработчику button2_Click. Однако в этом случае обработчик должен проверять, в каком состоянии находится кнопка – диком или прирученном. Поступим по-другому: свяжем событие Click для прирученной кнопки с новым обработчиком. Такой подход позволит продемонстрировать в нашем проекте ряд особенностей, связанных с действиями по присоединению и отсоединению обработчиков.
Новый обработчик (назовем его button2_Click2) создадим «вручную», не прибегая к услугам окна Properties или xaml-файла. Для этого в конце описания класса MainWindow в файле MainWindow.xaml.cs (перед двумя последними скобками «}») добавим описание этого обработчика:
Чтобы подчеркнуть, что в данном случае никакая часть обработчика не создается автоматически, мы выделили весь текст обработчика полужирным шрифтом.
В метод button2_Click добавьте следующие операторы (здесь и далее в книге предполагается, что если место добавления не уточняется, то операторы надо добавлять в конец метода):
В метод Canvas_MouseDown добавьте операторы:
Результат. Прирученная кнопка теперь выполняет полезную работу – щелчок на ней приводит к разворачиванию окна программы на весь экран, а новый щелчок восстанавливает первоначальное состояние окна. Если же щелкнуть мышью на окне (не на кнопке), то услужливая кнопка «Закрыть» прибежит на вызов, а прирученная кнопка «Изменить» снова одичает, потеряет текст своего заголовка и начнет убегать от мыши.
Комментарий
Приведенные
Если теперь после запуска программы несколько раз щелкнуть мышью на окне, а затем приручить кнопку button2, то при ее последующем нажатии окно несколько раз последовательно перейдет из развернутого состояния в стандартное и обратно. Это объясняется тем, что теперь каждый щелчок на окне присоединяет к событию Click кнопки button2 новый экземпляр обработчика button2_Click, и при нажатии на эту кнопку каждый экземпляр обработчика последовательно запускается на выполнение. Ситуация осложняется еще тем обстоятельством, что в программе невозможно выяснить, сколько и какие обработчики присоединены в настоящий момент к данному событию (а не зная этого, нельзя и обеспечить отсоединение от события всех его обработчиков).
Итак, к действиям по явному присоединению обработчика к событию и его последующему отсоединению следует подходить крайне осторожно.
Недочет. При выполнении программы может возникнуть ситуация, когда одна или обе кнопки не будут отображаться в окне (если, например, кнопки были перемещены на новое место при развернутом окне, после чего окно возвращено в исходное состояние).
Исправление. Определите для окна обработчик события SizeChanged:
Результат. Теперь в ситуации, когда при изменении размера окна его кнопки оказываются вне клиентской части, происходит перемещение этих кнопок на исходные позиции около левого верхнего угла окна.
Комментарии
1. В данном обработчике демонстрируется еще один способ доступа к компонентам окна, который удобен для организации перебора в цикле компонентов с похожими именами. Этот способ основан на применении метода FindName, который можно вызывать непосредственно для окна. Метод FindName возвращает компонент окна с указанным именем (или null, если компонент с таким именем в окне отсутствует).
2. Вместо статического метода GetLeft для получения значения присоединенного свойства Left можно было бы использовать более длинный, но и более универсальный вариант, использующий метод GetValue того компонента, к которому ранее было присоединено свойство: (double)b.GetValue(Canvas.LeftProperty). Аналогичным образом можно получить значение свойства Top (и любых других свойств зависимости, присоединенных к данному компоненту).
2. Работа с несколькими окнами: WINDOWS
Рис. 7. Окна приложения WINDOWS
2.1. Настройка визуальных свойств окон. Открытие окон в обычном и диалоговом режиме
После создания проекта к нему необходимо добавить два дополнительных окна. Для этого требуется выполнить команду Project | Add Window… и в появившемся диалоговом окне указать имя класса, который будет связан с новым окном. Достаточно использовать имена, предлагаемые по умолчанию – Window1 для первого окна, Window2 для второго.
Рис. 8. Макет окна MainWindow приложения WINDOWS
MainWindow.xaml (рис. 8):
Window1.xaml:
Window2.xaml: