Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
Конечно, фактический ответ зависит от обстоятельств. Реализация классами моделей интерфейсов INotifyPropertyChanged, IDataErrorInfо и INotifyDataErrorInfo гарантирует, что соответствующий код близок к своей цели (как вы увидите далее в главе) и реализован только однократно для каждой модели. Другими словами, есть ситуации, когда сами классы моделей представлений необходимо разрабатывать как наблюдаемые. По большому счету вы должны самостоятельно выяснить, что имеет больший смысл для приложения, не приводя к чрезмерному усложнению кода и не принося в жертву преимущества MWM.
На заметку! Для WPF доступны многочисленные инфраструктуры MWM, такие как MWMLite, Caliburn.Micro и Prism (хотя Prism — нечто намного большее, чем просто инфраструктура MWM). В настоящей главе обсуждается паттерн MWM и функциональные средства WPF, которые поддерживают его реализацию. Исследование других инфраструктур и выбор среди них наиболее подходящей для нужд приложения остается за вами как разработчиком.
Система уведомлений привязки WPF
Значительным недостатком системы привязки Windows Forms является отсутствие уведомлений. Если находящиеся внутри представления данные модифицируются в коде, то пользовательский интерфейс также должен обновляться программно, чтобы оставаться в синхронном состоянии с ними. Итогом будет большое количество вызовов метода Refresh() на элементах управления, обычно превышающее абсолютно необходимое для обеспечения безопасности. Наряду с тем, что наличие слишком многих обращений к Refresh() обычно не приводит к серьезной проблеме с производительностью, недостаточное их число может отрицательно повлиять на пользовательский интерфейс.
Система привязки, встроенная в приложения на основе XAML, устраняет указанную проблему за счет того, что позволяет привязывать объекты данных и коллекции к системе уведомлений, разрабатывая их как наблюдаемые. Всякий раз, когда изменяется значение свойства в наблюдаемой модели либо происходит изменение в наблюдаемой коллекции (например, добавление, удаление или переупорядочение элементов), инициируется событие (NotifyPropertyChanged либо NotifyCollectionChanged). Инфраструктура привязки автоматически прослушивает такие события и в случае их появления обновляет привязанные элементы управления. Более того, разработчики имеют контроль над тем, для каких свойств выдаются уведомления. Выглядит безупречно, не так ли? На самом деле все не настолько безупречно. Настройка наблюдаемых моделей вручную требует написания довольно большого объема кода. К счастью, как вы вскоре увидите, существует инфраструктура с открытым кодом, которая значительно упрощает работу.
Наблюдаемые модели и коллекции
В этом разделе вы построите приложение, в котором используются наблюдаемые модели и коллекции. Для начала создайте новый проект приложения WPF по имени WpfNotifications. В приложении будет применяться форма "главная-подробности", которая позволит пользователю выбирать объект автомобиля в элементе управления ComboBox и просматривать детальную информацию о нем в расположенных ниже элементах управления TextBox. Поместите в файл MainWindow.xaml следующую разметку:
<Grid IsSharedSizeScope="True" Margin="5,0,5,5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"
SharedSizeGroup="CarLabels"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Vehicle"/>
<ComboBox Name="cboCars" Grid.Column="1"
DisplayMemberPath="PetName" />
</Grid>
<Grid Grid.Row="1" Name="DetailsGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"
SharedSizeGroup="CarLabels"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="Id"/>
<TextBox Grid.Column="1" Grid.Row="0" />
<Label Grid.Column="0" Grid.Row="1" Content="Make"/>
<TextBox Grid.Column="1" Grid.Row="1" />
<Label Grid.Column="0" Grid.Row="2" Content="Color"/>
<TextBox Grid.Column="1" Grid.Row="2" />
<Label Grid.Column="0" Grid.Row="3" Content="Pet Name"/>
<TextBox Grid.Column="1" Grid.Row="3" />
<StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4"
HorizontalAlignment="Right" Orientation="Horizontal" Margin="0,5,0,5">
<Button x:Name="btnAddCar" Content="Add Car" Margin="5,0,5,0" Padding="4, 2" />
<Button x:Name="btnChangeColor" Content="Change Color" Margin="5,0,5,0"
Padding="4, 2"/>
</StackPanel>
</Grid>
</Grid>
Окно должно напоминать показанное на рис. 28.1.
Свойство IsSharedSizeScope элемента управления Grid заставляет дочерние сетки разделять размеры. Элемент ColumnDefinitions, помеченный как SharedSizeGroup, автоматически получит ту же самую ширину без каких-либо потребностей в программировании. В рассматриваемом примере, если размер метки Pet Name (Дружественное имя) изменяется из-за более длинного значения, тогда соответствующим образом корректируется и размер колонки Vehicle (Автомобиль), который находится в другом элементе управления Grid, сохраняя аккуратный внешний вид окна.
Щелкните правой кнопкой мыши на имени проекта в окне Solution Explorer, выберите в контекстном меню пункт Add►New Folder (Добавить►Новая папка) и назначьте новой папке имя Models. Создайте в новой папке файл класса Car.cs. Первоначально код класса выглядит так:
public class Car
{
public int Id { get; set; }
public string Make { get; set; }
public string Color { get; set; }
public string PetName { get; set; }
}
Добавление привязок и данных
Следующий шаг заключается в создании операторов привязки для элементов управления. Вспомните, что конструкции привязки данных вращаются вокруг контекста данных, который может быть установлен в самом элементе управления или в родительском элементе управления. Здесь контекст будет установлен в элементе DetailsGrid, так что каждый содержащийся внутри него элемент управления унаследует результирующий контекст данных.
Установите свойство DataContext в свойство SelectedItem элемента ComboBox. Модифицируйте определение элемента Grid, содержащего элементы управления с информацией об автомобиле, следующим образом:
<Grid Grid.Row="1" Name="DetailsGrid"
DataContext="{Binding ElementName=cboCars, Path=SelectedItem}">
Текстовые поля в элементе DetailsGrid будут отображать индивидуальные характеристики выбранного автомобиля. Добавьте подходящие атрибуты Text и привязки к элементам управления TextBox:
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Path=Id}" />
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Path=Make}" />
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Path=Color}" />
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Path=PetName}" />
Наконец, поместите нужные данные в элемент управления ComboBox. В файле MainWindow.xaml.cs создайте новый список записей