Главная » WPF » Редактирование WPF

0

Чтобы редактировать значения, должен быть какой то способ узнать, что зна чение изменилось. Помимо разрешения изменять  свойство, существует несколь ко интерфейсов, которые позволяют объекту или списку рассылать извещения об изменении.  Если источник  данных уведомляет  об изменении,  то система связы вания  сможет  отреагировать на  модификацию данных.  Чтобы  наделить   наш класс Person способностью извещать об изменениях, у нас есть три возможности: (1)  реализовать интерфейс INotifyPropertyChanged, (2)  добавить события, с по мощью которых мы будем сообщать об изменении,  (3)  создать свойства,  произ водные от класса DependencyProperty.

Использование событий для извещения об изменениях свойств впервые было применено  в .NET 1.0 и поддерживается механизмами связывания в Windows Forms и ASP.NET.  Интерфейс INotifyPropertyChanged появился в .NET 2.0. Он оптимизирован для привязки к данным, обладает более высокой производитель ностью и проще как для авторов объектов, так и для самой системы связывания. Но использовать этот интерфейс в обычных сценариях,  когда необходимо  изве щать об изменениях, несколько  сложнее.

Применение свойств, производных от DependencyProperty, относительно просто, позволяет  объекту воспользоваться преимуществами, которые  дает раз реженное  хранилище,  и хорошо сопрягается с другими  службами  WPF (напри мер, с динамической привязкой к ресурсам  и стилизацией). Подробно  создание объектов  со свойствами,  производными от DependencyProperty, обсуждается  в приложении.

Все три способа во время  выполнения демонстрируют одинаковое  поведение. Вообще говоря, при создании модели данных лучше реализовать интерфейс INotifyPropertyChanged. Для использования свойств, производных от DependencyProperty, требуется, чтобы класс объекта наследовал DependencyObject, а это, в свою очередь, означает, что объект данных должен работать в STA потоке. Применение событий  для извещения об изменениях свойств  приводит  к разбуха нию объектной модели; в общем случае этот механизм лучше оставить для свойств, которые разработчики и так привыкли отслеживать:

public class Name : INotifyPropertyChanged {

public string First {

get {

return _first;

}

set {

_first = value;

NotifyChanged(«First»);

}

}

public event PropertyChangedEventHandler PropertyChanged;

void NotifyChanged(string property) {

if (PropertyChanged != null) { PropertyChanged(this,

new PropertyChangedEventArgs(property));

}

}

}

Раз мы применяем один и тот же паттерн ко всем трем объектам данным, то можем изменить пользовательский интерфейс и воспользоваться преимуществами новой ре ализации объектов. Мы заведем два неизменяемых текстовых поля (TextBlock) для отображения текущего имени и два объекта TextBox  для редактирования значений. Поскольку класс Name реализует интерфейс INotifyPropertyChanged, то при измене нии значений  система связывания получит  извещение,  что приведет  к обновлению объектов TextBlock  (рис. 6.9):

<Window … Text=’Object Binding’>

<StackPanel>

<TextBlock Text=’{Binding Path=Name.First}’ />

<TextBlock Text=’{Binding Path=Name.Last}’ />

<Grid>

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition />

<ColumnDefinition />

</Grid.ColumnDefinitions>

<Label Grid.Row=’0’ Grid.Column=’0’>First</Label>

<TextBox Grid.Row=’0’ Grid.Column=’1’ Text=’{Binding Path=Name.First}’ />

<Label Grid.Row=’1’ Grid.Column=’0’>Last</Label>

<TextBox Grid.Row=’1’ Grid.Column=’1’ Text=’{Binding Path=Name.Last}’ />

</Grid>

</StackPanel>

</Window>

Ввод в любое текстовое  поле обновляет  соответствующую область окна. От метим, что изменение происходит только при выходе из поля. По умолчанию эле мент TextBox обновляет данные только в момент потери фокуса. Чтобы изменить это поведение, нужно указать, что привязка должно обновляться при любом из менении значения свойства. Для этого служит свойство привязки UpdateSourceTrigger:

<TextBox Grid.Row=’1’ Grid.Column=’1’

Text=’{Binding Path=Name.Last,UpdateSourceTrigger=PropertyChanged}’

/>

Рис. 6.9. Редактирование объекта с помощью двустороннего связывания

Списки  сложнее простого изменения свойств. Для связывания необходимо знать, какие элементы были добавлены или удалены. С этой целью в WPF введен интерфейс INotifyCollectionChanged, в котором определено  единственное  собы тие CollectionChanged с аргументом  такого типа:

public class NotifyCollectionChangedEventArgs : EventArgs { public NotifyCollectionChangedAction Action { get; } public IList NewItems { get; }

public int NewStartingIndex { get; }

public IList OldItems { get; }

public int OldStartingIndex { get; }

}

public enum NotifyCollectionChangedAction { Add,

Remove, Replace, Move,

Reset,

}

В нашем примере  мы можем поддержать  динамическое  добавление  и удале ние адресов человека. Проще всего это реализуется в помощью класса ObservableCollection<T>, который наследует Collection<T> и дополнительно ре ализует интерфейс INotifyCollectionChanged:

public class Person : INotifyPropertyChanged

{

IList<Address> _addresses = new ObservableCollection<Address>();

}

Теперь можно модифицировать наше приложение так, чтобы все адреса отоб ражались  в списковом  поле, и реализовать пользовательский интерфейс для вво да новых адресов и добавления  их в список:

<!— window1.xaml —>

<Window … Text=’Object Binding’>

<StackPanel>

<StackPanel.Resources>

<!— шаблон для отображения списка адресов —>

<DataTemplate x:Key=’addressTemplate’>

<StackPanel Orientation=’Horizontal’>

<TextBlock Text=’{Binding Path=Street1}’ />

<TextBlock Text=’,’ />

<TextBlock Text=’{Binding Path=City}’ />

<TextBlock Text=’,’ />

<TextBlock Text=’{Binding Path=State}’ />

<TextBlock Text=’,’ />

<TextBlock Text=’{Binding Path=Zip}’ />

</StackPanel>

</DataTemplate>

</StackPanel.Resources>

<!— имя человека —>

<TextBlock Text=’{Binding Path=Name.First}’ />

<TextBlock Text=’{Binding Path=Name.Last}’ />

<!— список адресов —>

<TextBlock Margin=’5’ FontSize=’14pt’>Addresses:</TextBlock>

<ListBox ItemsSource=’{Binding Path=Addresses}’ ItemTemplate=’{DynamicResource addressTemplate}’ />

<!— добавляем новый ГИП для отображения адреса —>

<TextBlock      Margin=’5’       FontSize=’14pt’>Add       New

Address:</TextBlock>

<Grid Margin=’5’>

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition />

<RowDefinition />

<RowDefinition />

<RowDefinition />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width=’Auto’ />

<ColumnDefinition Width=’*’ />

</Grid.ColumnDefinitions>

<Label Grid.Row=’0’ Grid.Column=’0’>Street</Label>

<TextBox Grid.Row=’0’ Grid.Column=’1’ Name=’_street’ />

<Label Grid.Row=’1’ Grid.Column=’0’>City</Label>

<TextBox Grid.Row=’1’ Grid.Column=’1’ Name=’_city’ />

<Label Grid.Row=’2’ Grid.Column=’0’>State</Label>

<TextBox Grid.Row=’2’ Grid.Column=’1’ Name=’_state’ />

<Label Grid.Row=’3’ Grid.Column=’0’>Zip</Label>

<TextBox Grid.Row=’3’ Grid.Column=’1’ Name=’_zip’ />

<Button Click=’Add’ Grid.Row=’4’>Add</Button>

</Grid>

</StackPanel>

</Window>

// window1.xaml.cs

void Add(object sender, RoutedEventArgs e) { Address a = new Address();

a.Street1 = _street.Text; a.City = _city.Text; a.State = _state.Text; a.Zip = _zip.Text;

((Person)DataContext).Addresses.Add(a);

}

Эта программа выводит окно, показанное  на рис. 6.10.

В текстовые поля можно вводить новую информацию, а при нажатии  кнопки Add  список  адресов  обновляется. Поскольку свойство  Addresses  реализует  ин терфейс INotifyCollectionChanged, система связывания получает извещение о но вом адресе и корректно  обновляет  пользовательский интерфейс.

Рис. 6.10. Редактирование списка с использованием двустороннего связывания

Привязка к объектам CLR – процедура по большей части автоматическая. Ре ализовав  интерфейсы INotifyPropertyChanged и INotifyCollectionChanged, мы наделяем  источник  данных  «интеллектом». В WPF также  встроена  поддержка для привязки к XML6.

Источник: К. Андерсон  Основы  Windows Presentation Foundation. Пер. с англ. А. Слинкина — М.: ДМК Пресс, 2008 — 432 с.: ил.

По теме:

  • Комментарии