Главная » Silverlight » Привязка к коллекции объектов

0

Привязка к одному объекту — довольно простая операция. Более сложная задача — привязка к коллекции объектов, например к информации о всех продуктах, хранящейся в таблице.

Каждое зависимое свойство поддерживает только привязку к одному объекту, поэтому для привязки к коллекции необходим более сложный элемент. В Silverlight каждый эле­мент управления, выводящий список, наследует класс ItemsControl. Для поддержки при­вязки к коллекции в классе ItemsControl определен ряд ключевых свойств (табл. 16.2).

Таблица 16.2. Свойства класса ItemsControl, предназначенные для связывания данных

Имя свойства

Описание

ItemsSource

Указывает на коллекцию всех объектов, которые должны быть выведены в список

DisplayMemberPath

Идентифицирует свойство, используемое для создания текстового содержимого каждого элемента списка

ItemTemplate

Предоставляет шаблон данных, используемый для создания визуального представления каждого элемента списка; это свойство является более мощным вариантом свойства DisplayMemberPath

ItemsPanel

Предоставляет шаблон, используемый для создания контейнера, содержащего все элементы списка

В свойстве ItemsSource можно использовать не только коллекции, но и любые объ­екты, поддерживающие интерфейс IEnumerable, включая массивы и другие специали­зированные объекты, служащие оболочкой для группы элементов. Однако интерфейс IEnumerable поддерживает только связывание в режиме чтения. Чтобы изменить кол­лекцию (например, разрешить пользователю вставлять или удалять элементы списка, а не только изменять содержимое элементов), нужно применить более сложную инфра­структуру, как описано далее.

Вывод и редактирование элементов коллекции

Рассмотрим страницу (рис. 16.5) со списком продуктов. При выборе продукта инфор­мация о нем появляется в нижней области страницы. Информацию о продукте можно редактировать. В данном примере элемент GridSplitter позволяет настраивать раз­меры нижней области путем перетаскивания.

Рис. 16.5. Список продуктов

Метод StoreDb.GetProducts () извлекает список продуктов из базы данных Get- Products с помощью хранимой процедуры GetProducts. Объект Product создается для каждой записи и добавляется в обобщенную коллекцию List. Вместо коллекции можно использовать любой набор данных, например массив или объект типа ArrayList. Ниже приведен код метода GetProduct ().

[OperationContract ()]

public List<Product> GetProducts() {

SqlConnection con = new SqlConnection(connectionString);

SqlCommand cmd = new SqlCommand("GetProducts", con) ; cmd.CommandType = CommandType.StoredProcedure;

List<Product> products = new List<Product>();

try {

con.Open ();

SqlDataReader reader = cmd.ExecuteReader();

while (reader.Read()) {

// Создание объекта Product как оболочки // текущей записи Product product = new

Product ((string)reader["ModelNumber"], (string)reader["ModelName"], Convert.ToDouble(reader["UnitCost"]), (string)reader["Description"], (string)reader["CategoryName"]);

// Добавление в коллекцию products.Add(product) ;

}

}

finally {

con.Close () ;

}

return products;

}

При щелчке на кнопке Выбрать продукты код обработки события щелчка асинхронно вызывает метод GetProducts О .

private void cmdGetProducts_Click(object sender, RoutedEventArgs e) {

StoreDbClient client = new StoreDbClient();

client-GetProductsCompleted += client_GetProductsCompleted; client.GetProductsAsync() ;

}

После получения от веб-службы списка продуктов код сохраняет коллекцию в классе страницы для облегчения доступа к ней в приложении. Затем код присваивает коллек­цию свойству ItemsSource списка.

private Productf] products;

private void client_GetProductsCompleted (object sender, GetProductsCompletedEventArgs e)

‘ {

try {

products = e.Result;

IstProducts.ItemsSource = products;

}

catch (Exception err) {

lblError.Text = "Неуспешное обращение к службе.";

}

}

Примечание. Веб-служба возвращает массив объектов Product, однако клиентское приложение получает результат в пакете другого типа — коллекции ObservableCollection. Зачем нужен этот трюк, рассматривается в следующем разделе

Рассмотренный процесс заполняет список объектами Product. Однако список не знает, как вывести объект Product, поэтому он вызывает метод ToString (). В классе Product метод ToString () не переопределен, поэтому результат неудовлетворительный: в список выводятся полностью квалифицированные имена каждого элемента (рис. 16.6).

‘Рис. 16.6. Связанный список бесполезен

Существует три способа решения проблемы.

•       Установите свойство DisplayMemberPath списка. Например, присвойте ему значение ModelName, чтобы получить результат, аналогичный показанному на рис. 16.5.

•       Переопределите метод Product. ToString {) таким образом, чтобы он возвращал более полезную информацию. Например, задайте возвращение номера и назва­ния модели для каждого элемента списка. Тогда можно будет выводить в список более одного свойства (например, можно объединить свойства FirstName и Last- name в классе Customer).

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

Исправив список, переходите к выводу информации о текущем элементе в решет­ке, расположенной под списком. Для этого необходимо запрограммировать реакцию на событие SelectionChanged. Обработчик изменяет свойство DataContext решетки Grid, содержащей информацию о продукте.

private void lstProducts_SelectionChanged(object sender, SelectionChangedEventArgs e)

{ / gridProductDetails.DataContext = IstProducts.Selectedltem;

}

Совет. Чтобы запретить редактирование поля, присвойте свойству isReadOnly текстового поля значение true. А еще лучше — примените элемент управления TextBlock, работающий в режиме "только чтение".

Экспериментируя с примером, вы увидите, что все его функции работоспособны. Можно редактировать свойства продукта, переходить к другому продукту. При возвра­щении к предыдущему продукту видно, что изменения успешно зафиксированы. Можно даже изменять значение, влияющее на вывод текста в списке. Например, если изме­нить название модели, а затем перейти к другому элементу управления, соответствую­щий пункт списка будет автоматически обновлен.

Однако существует серьезная проблема. Изменение фиксируется, только когда эле­мент управления теряет фокус. Если изменить значение в текстовом поле, а затем пе­рейти к другому текстовому полю, объект данных будет обновлен, как и ожидается. Но если изменить значение и щелкнуть на другом элементе списка, измененное значение будет отброшено и будет загружена информация выделенного объекта данных. Для устранения этой проблемы добавьте код, который явно фиксирует изменение. В отли­чие от WPF, платформа Silverlight не предоставляет непосредственного способа решения этой задачи. Можно только программно передать фокус другому элементу управления (в частности, невидимому), вызвав метод Focus О . Эта операция зафиксирует измене­ние в объекте данных. Затем можно вернуть фокус исходному текстовому полю, опять вызвав метод Focus (). Код передачи фокуса может находиться в обработчике события TextChanged. Можно также добавить кнопку Save (Сохранить) или Update (Обновить). Тогда не нужен даже код, потому что при щелчке на кнопке она получает фокус, в ре­зультате чего обновление выполняется автоматически.

Вставка и удаление элементов коллекции

Как показано в предыдущем разделе, Silverlight выполняет изменение во время ге­нерации кода на стороне клиента для коммуникации с веб-службой. Служба может воз­вратить массив или коллекцию List, однако клиентский код размещает полученные объекты в коллекции ObservableCollection. Такой же этап трансляции выполняется при возвращении объекта со свойством коллекции.

Трансляция необходима потому, что клиент не знает, какой тип коллекции будет возвращен сервером. Надстройка Silverlight считает, что для надежности она должна использовать коллекцию ObservableCollection, потому что она поддерживает больше средств, чем массив или обычная коллекция List.

Как и List, коллекция ObservableCollection поддерживает добавление и удаление элементов. Например, при щелчке на кнопке Delete (Удалить) приведенный ниже обра­ботчик удаляет элемент из коллекции.

private void cmdDeleteProduct_Click(object sender,

RoutedEventArgs e)

{

products.Remove((Product)IstProducts.Selectedltem);

Для массива этот способ, естественно, непригоден. Он работоспособен в коллекции List, но существует одна проблема: элемент, хоть и удаляется из коллекции, остается видимым в связанном списке.

Чтобы коллекция могла отслеживать изменения, она должна реализовать интер­фейс INotifyCollectionChanged. В Silverlight его реализует единственная коллекция — класс ObservableCollection. При выполнении приведенного выше кода с коллекцией ObservableCollection (например, с коллекцией продуктов, возвращенных службой) связанный список обновляется немедленно. Можно создать код доступа к данным, фиксирующий изменение в таблице базы данных. Код может быть оформлен как метод службы, вставляющий записи о продуктах в таблицу или удаляющий их из таблицы.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

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