Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
// в нем только текстовые файлы.
var openDlg = new OpenFileDialog { Filter = "Text Files |*.txt"};
// Был ли совершен щелчок на кнопке ОК?
if (true == openDlg.ShowDialog())
{
// Загрузить содержимое выбранного файла.
string dataFromFile = File.ReadAllText(openDlg.FileName);
// Отобразить строку в TextBox.
txtData.Text = dataFromFile;
}
}
private void SaveCmdExecuted(object sender, ExecutedRoutedEventArgs e)
{
var saveDlg = new SaveFileDialog { Filter = "Text Files |*.txt"};
// Был ли совершен щелчок на кнопке ОК?
if (true == saveDlg.ShowDialog())
{
// Сохранить данные из TextBox в указанном файле.
File.WriteAllText(saveDlg.FileName, txtData.Text);
}
}
На заметку! Система команд WPF более подробно рассматривается в главе 28, где будут создаваться специальные команды на основе ICommand и RelayCommands.
Итак, пример и начальное знакомство с элементами управления WPF завершены. Вы узнали, как работать с базовыми командами, системами меню, строками состояния, панелями инструментов, вложенными панелями и несколькими основными элементами пользовательского интерфейса (вроде TextBox и Expander). В следующем примере вы будете иметь дело с более экзотическими элементами управления, а также с рядом важных служб WPF.
Понятие маршрутизируемых событий
Вы могли заметить, что в предыдущем примере кода передавался параметр RoutedEventArgs, а не EventArgs. Модель маршрутизируемых событий является усовершенствованием стандартной модели событий CLR и спроектирована для того, чтобы обеспечить возможность обработки событий в манере, подходящей описанию XAML дерева объектов. Предположим, что имеется новый проект приложения WPF по имени WpfRoutedEvents. Модифицируйте описание XAML начального окна, добавив следующий элемент управления Button, который определяет сложное содержимое:
<Window ...
<Grid>
<Button Name="btnClickMe" Height="75" Width = "250"
Click ="btnClickMe_Clicked">
<StackPanel Orientation ="Horizontal">
<Label Height="50" FontSize ="20">
Fancy Button!</Label>
<Canvas Height ="50" Width ="100" >
<Ellipse Name = "outerEllipse" Fill ="Green"
Height ="25" Width ="50" Cursor="Hand"
Canvas.Left="25" Canvas.Top="12"/>
<Ellipse Name = "innerEllipse" Fill ="Yellow"
Height = "15" Width ="36"
Canvas.Top="17" Canvas.Left="32"/>
</Canvas>
</StackPanel>
</Button>
</Grid>
</Window>
Обратите внимание, что в открывающем определении элемента Button было обработано событие Click за счет указания имени метода, который должен вызываться при возникновении события. Событие Click работает с делегатом RoutedEventHandler, который ожидает обработчик события, принимающий object в первом параметре и System.Winodws.RoutedEventArgs во втором. Реализуйте такой обработчик:
public void btnClickMe_Clicked(object sender, RoutedEventArgs e)
{
// Делать что-нибудь, когда на кнопке произведен щелчок.
MessageBox.Show("Clicked the button");
}
После запуска приложения окно сообщения будет отображаться независимо от того, на какой части содержимого кнопки был выполнен щелчок (зеленый элемент Ellipse, желтый элемент Ellipse, элемент Label или поверхность элемента Button). В принципе это хорошо. Только представьте, насколько громоздким оказалась бы обработка событий WPF, если бы пришлось обрабатывать событие Click для каждого из упомянутых подэлементов. Дело не только в том, что создание отдельных обработчиков событий для каждого аспекта Button — трудоемкая задача, а еще и в том, что в результате получился бы сложный в сопровождении код.
К счастью, маршрутизируемые события WPF позаботятся об автоматическом вызове единственного обработчика события Click вне зависимости от того, на какой части кнопки был совершен щелчок. Выражаясь просто, модель маршрутизируемых событий автоматически распространяет событие вверх (или вниз) по дереву объектов в поисках подходящего обработчика.
Точнее говоря, маршрутизируемое событие может использовать три стратегии маршрутизации. Если событие перемещается от точки возникновения вверх к другим областям определений внутри дерева объектов, то его называют пузырьковым событием. И наоборот, если событие перемещается от самого внешнего элемента (например, Window) вниз к точке возникновения, то его называют туннельным событием. Наконец, если событие инициируется и обрабатывается только элементом, внутри которого оно возникло (что можно было бы описать как нормальное событие CLR), то его называют прямым событием.
Роль пузырьковых маршрутизируемых событий
В текущем примере, когда пользователь щелкает на внутреннем овале желтого цвета, событие Click поднимается на следующий уровень области определения (Canvas), затем на StackPanel и в итоге на уровень Button, где обрабатывается. Подобным же образом, если пользователь щелкает на Label, то событие всплывает на уровень StackPanel и, в конце концов, попадает в элемент Button.
Благодаря такому шаблону пузырьковых маршрутизируемых событий не придется беспокоиться о регистрации специфичных обработчиков события Click для всех членов составного элемента управления. Однако если необходимо выполнить специальную логику обработки щелчков для нескольких элементов внутри того же самого дерева объектов, то это вполне можно делать.
В целях иллюстрации предположим, что щелчок на элементе управления outerEllipse должен быть обработан в уникальной манере. Сначала обработайте событие MouseDown для этого подэлемента (графически визуализируемые типы вроде Ellipse не поддерживают событие Click, но могут отслеживать действия кнопки мыши через события MouseDown, MouseUp и т.д.):
<Button Name="btnClickMe" Height="75" Width = "250"
Click ="btnClickMe_Clicked">
<StackPanel Orientation ="Horizontal">
<Label Height="50" FontSize ="20">Fancy Button!</Label>
<Canvas Height ="50" Width ="100" >
<Ellipse Name = "outerEllipse" Fill ="Green"
Height ="25" MouseDown ="outerEllipse_MouseDown"
Width ="50" Cursor="Hand" Canvas.Left="25" Canvas.Top="12"/>
<Ellipse Name = "innerEllipse" Fill ="Yellow" Height = "15" Width ="36"
Canvas.Top="17" Canvas.Left="32"/>
</Canvas>
</StackPanel>
</Button>
Затем реализуйте подходящий обработчик событий, который в демонстрационных целях будет просто изменять свойство Title главного окна:
public void outerEllipse_MouseDown(object sender, MouseButtonEventArgs e)
{
// Изменить заголовок окна.
this.Title = "You clicked the outer ellipse!";
}
Далее можно выполнять разные действия в зависимости от того, на чем конкретно щелкнул конечный пользователь (на внешнем эллипсе или в любом другом месте внутри области кнопки).
На заметку! Пузырьковые маршрутизируемые события всегда перемещаются из точки возникновения до следующей определяющей области.