Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В предыдущих руководствах мы узнали, как элемент управления GridView упрощает редактирование и удаление текстовых данных. В этом руководстве показано, как элемент управления GridView также позволяет изменять и удалять двоичные данные, сохраняемые в базе данных или хранящиеся в файловой системе.
Введение
За последние три руководства мы добавили довольно много функций для работы с двоичными данными. Мы начали с добавления столбца BrochurePath
в Categories
таблицу и соответствующим образом обновили архитектуру. Мы также добавили методы уровня доступа к данным и уровня бизнес-логики для работы с существующим Picture
столбцом таблицы "Категории", в которой содержится двоичное содержимое файла изображения. Мы создали веб-страницы, чтобы представить двоичные данные в GridView и обеспечить ссылку для скачивания брошюры, с изображением категории, показанным в элементе <img>
, и добавили DetailsView, чтобы пользователи могли добавить новую категорию и загрузить её брошюру и данные изображения.
Все, что осталось реализовать, — это возможность изменять и удалять существующие категории, которые мы будем выполнять в этом руководстве с помощью встроенных функций редактирования и удаления GridView. При редактировании категории пользователь сможет при необходимости отправить новое изображение или продолжить использовать существующую. Для брошюры они могут использовать существующую брошюру, отправить новую брошюру или указать, что категория больше не связана с ней. Давайте приступим!
Шаг 1. Обновление уровня доступа к данным
DAL имеет автоматически созданные методы Insert
, Update
и Delete
, но эти методы были созданы на основе основного запроса CategoriesTableAdapter
, который не включает столбец Picture
. Поэтому методы Insert
и Update
не включают параметры для указания двоичных данных для рисунка категории. Как и в предыдущем руководстве, необходимо создать новый метод TableAdapter для обновления Categories
таблицы при указании двоичных данных.
Откройте типизированный DataSet и в конструкторе щелкните правой кнопкой мыши на заголовке CategoriesTableAdapter
, затем выберите "Добавить запрос" в контекстном меню, чтобы запустить мастер настройки запросов TableAdapter. Этот мастер начинается с вопроса о том, как запрос TableAdapter должен получить доступ к базе данных. Нажмите кнопку "Использовать инструкции SQL" и нажмите кнопку "Далее". Следующий шаг запрашивает тип создаваемого запроса. Так как мы создадим запрос для добавления новой записи в Categories
таблицу, нажмите кнопку UPDATE и нажмите кнопку "Далее".
Рис. 1. Выберите параметр UPDATE (щелкните, чтобы просмотреть изображение полного размера)
Теперь необходимо указать инструкцию UPDATE
SQL. Мастер автоматически предлагает оператор UPDATE
, который соответствует основному запросу TableAdapter (и обновляет значения CategoryName
, Description
и BrochurePath
). Измените оператор, чтобы Picture
столбец был включен вместе с параметром @Picture
, например:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
Последний экран мастера просит нас назвать новый метод TableAdapter. Введите UpdateWithPicture
и нажмите кнопку "Готово".
Рис. 2. Назовите метод UpdateWithPicture
New TableAdapter (щелкните, чтобы просмотреть изображение полного размера)
Шаг 2. Добавление методов уровня бизнес-логики
Помимо обновления DAL, необходимо обновить BLL, чтобы включить методы обновления и удаления категории. Это методы, которые будут вызываться из слоя презентации.
Для удаления категории можно использовать автоматически созданный метод CategoriesTableAdapter
Delete
. Добавьте приведенный ниже метод в класс CategoriesBLL
:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteCategory(int categoryID)
{
int rowsAffected = Adapter.Delete(categoryID);
// Return true if precisely one row was deleted, otherwise false
return rowsAffected == 1;
}
В этом руководстве мы создадим два метода для обновления категории: один, который ожидает данные двоичного изображения и вызывает метод UpdateWithPicture
который мы только что добавили в CategoriesTableAdapter
, и другой, который принимает только значения CategoryName
, Description
и BrochurePath
и использует автоматически созданный оператор CategoriesTableAdapter
класса Update
. Обоснование использования двух методов заключается в том, что в некоторых случаях пользователь может потребовать обновить рисунок категории вместе с другими полями, в этом случае пользователю придется отправить новое изображение. Затем двоичные данные загруженного рисунка можно использовать в инструкции UPDATE
. В других случаях пользователь может быть заинтересован только в обновлении, например, имени и описания. Но если UPDATE
инструкция ожидает двоичные данные для Picture
столбца, то необходимо также предоставить эти сведения. Для этого потребуется дополнительный запрос к базе данных, чтобы извлечь данные изображения для редактируемой записи. Поэтому мы хотим два UPDATE
метода. Уровень бизнес-логики определяет, какой из них следует использовать на основе того, предоставляются ли данные рисунка при обновлении категории.
Чтобы упростить эту задачу, добавьте два метода в CategoriesBLL
класс с именем UpdateCategory
. Первый должен принимать три string
, byte
массив и int
в качестве одного из входных параметров; второй — всего три string
и int
. Входные string
параметры предназначены для имени, описания категории и пути к файлу брошюры, byte
массив предназначен для двоичного содержимого изображения категории, а int
определяет CategoryID
записи для обновления. Обратите внимание, что первая перегрузка вызывает вторую, если переданный byte
массив является null
.
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateCategory(string categoryName, string description,
string brochurePath, byte[] picture, int categoryID)
{
// If no picture is specified, use other overload
if (picture == null)
return UpdateCategory(categoryName, description, brochurePath, categoryID);
// Update picture, as well
int rowsAffected = Adapter.UpdateWithPicture
(categoryName, description, brochurePath, picture, categoryID);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateCategory(string categoryName, string description,
string brochurePath, int categoryID)
{
int rowsAffected = Adapter.Update
(categoryName, description, brochurePath, categoryID);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
Шаг 3. Копирование функций вставки и просмотра
В предыдущем руководстве мы создали страницу с именем UploadInDetailsView.aspx
, которая перечислила все категории в GridView и предоставила DetailsView для добавления новых категорий в систему. В этом руководстве мы расширим GridView и добавим возможности редактирования и удаления. Вместо того чтобы продолжать работу с UploadInDetailsView.aspx
, лучше внесем изменения этого руководства на страницу UpdatingAndDeleting.aspx
в той же папке ~/BinaryData
. Скопируйте и вставьте декларативную разметку и код из UploadInDetailsView.aspx
до UpdatingAndDeleting.aspx
.
Начните с открытия UploadInDetailsView.aspx
страницы. Скопируйте весь декларативный синтаксис элемента <asp:Content>
, как показано на рис. 3. Затем откройте UpdatingAndDeleting.aspx
и вставьте эту разметку в его <asp:Content>
элемент. Аналогичным образом скопируйте код из класса связанного кода страницы UploadInDetailsView.aspx
в UpdatingAndDeleting.aspx
.
Рис. 3. Копирование декларативной разметки из UploadInDetailsView.aspx
(щелкните, чтобы просмотреть изображение полного размера)
После копирования декларативной разметки и кода посетите UpdatingAndDeleting.aspx
страницу. Вы должны увидеть те же выходные данные и иметь такой же интерфейс пользователя, как на странице UploadInDetailsView.aspx
из предыдущего руководства.
Шаг 4. Добавление поддержки удаления в ObjectDataSource и GridView
Как уже обсуждалось в руководстве по обзору вставки, обновления и удаления данных, GridView предоставляет встроенные возможности удаления, и их можно включить, поставив галочку в флажке, если источник данных GridView поддерживает удаление. В настоящее время GridView привязан к ObjectDataSource (CategoriesDataSource
), который не поддерживает удаление.
Чтобы устранить эту проблему, нажмите параметр "Настроить источник данных" в смарт-теге ObjectDataSource, чтобы запустить мастер. На первом экране показано, что ОбъектDataSource настроен для работы с классом CategoriesBLL
. Нажмите "Далее". В настоящее время указаны только свойства InsertMethod
и SelectMethod
объекта ObjectDataSource. Однако мастер автоматически заполнил раскрывающиеся списки на вкладках UPDATE и DELETE соответственно методами UpdateCategory
и DeleteCategory
. Это связано с тем, что в CategoriesBLL
классе мы помечаем эти методы, используя DataObjectMethodAttribute
методы по умолчанию для обновления и удаления.
Теперь установите раскрывающийся список вкладки UPDATE в положение "(Нет)", но оставьте раскрывающийся список вкладки DELETE установленным на DeleteCategory
. Мы вернемся к этому мастеру на шаге 6, чтобы добавить поддержку обновления.
Рис. 4. Настройка ObjectDataSource для использования DeleteCategory
метода (щелкните, чтобы просмотреть изображение полного размера)
Замечание
После завершения работы мастера Visual Studio может попросить обновить поля и ключи, которые будут повторно создавать поля веб-элементов управления данными. Выберите "Нет", так как выбор "Да" перезаписывает любые настройки полей, которые вы могли сделать.
Объект ObjectDataSource теперь будет содержать значение для его свойства DeleteMethod
, а также DeleteParameter
. Помните, что при использовании мастера для указания методов Visual Studio присваивает свойству ObjectDataSource OldValuesParameterFormatString
значение original_{0}
, в результате чего возникают проблемы с вызовами метода обновления и удаления. Таким образом, удалите это свойство полностью или сбросите его до значения по умолчанию {0}
. Если необходимо обновить память в этом свойстве ObjectDataSource, ознакомьтесь с руководством по вставке, обновлению и удалению данных .
После завершения работы мастера и исправления OldValuesParameterFormatString
декларативная разметка ObjectDataSource должна выглядеть примерно так:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
После настройки ObjectDataSource добавьте возможности удаления в GridView, установив флажок "Включить удаление" из смарт-тега GridView. Это добавит CommandField в GridView, для свойства которого ShowDeleteButton
установлено значение true
.
Рис. 5. Включение поддержки удаления в GridView (щелкните, чтобы просмотреть изображение полного размера)
Уделите мгновение, чтобы проверить функцию удаления. Существует внешний ключ между Products
таблицами CategoryID
и Categories
таблицами CategoryID
, поэтому вы получите исключение нарушения ограничения внешнего ключа при попытке удалить любую из первых восьми категорий. Чтобы проверить эту функциональность, добавьте новую категорию, предоставляя как брошюру, так и рисунок. Моя категория теста, показанная на рис. 6, содержит тестовый файл брошюры с именем Test.pdf
и тестовый рисунок. На рисунке 7 показан GridView после добавления тестовой категории.
Рис. 6. Добавление категории теста с помощью брошюры и изображения (щелкните, чтобы просмотреть изображение полного размера)
Рис. 7. После вставки категории теста он отображается в GridView (щелкните, чтобы просмотреть изображение полного размера)
В Visual Studio обновите обозреватель решений. Теперь в папке ~/Brochures
Test.pdf
должен появиться новый файл (см. рис. 8).
Затем щелкните ссылку "Удалить" в строке "Категория теста", что вызовет обратную отправку страницы и сработает метод CategoriesBLL
из класса DeleteCategory
. Метод DAL Delete
будет вызван, что приведет к отправке соответствующего оператора DELETE
в базу данных. Затем данные возвращаются к GridView, а разметка отправляется клиенту с категорией тестирования больше не присутствует.
Хотя рабочий процесс удаления успешно удалил запись тестовой категории из таблицы Categories
, он не удалил файл брошюры из файловой системы веб-сервера. Обновите обозреватель решений, и вы увидите, что Test.pdf
все еще находится в папке ~/Brochures
.
Рис. 8.Test.pdf
Файл не был удален из файловой системы веб-сервера
Шаг 5. Удаление файла брошюры из удаленной категории
Одним из недостатков хранения двоичных данных, внешних для базы данных, является то, что необходимо выполнить дополнительные действия, чтобы очистить эти файлы при удалении связанной записи базы данных. GridView и ObjectDataSource предоставляют события, которые выполняются как до, так и после выполнения команды удаления. Нам необходимо создать обработчики событий как для предварительных, так и для последующих действий.
Categories
Перед удалением записи необходимо определить путь к PDF-файлу, но мы не хотим удалить PDF-файл перед удалением категории в случае, если существует некоторое исключение, а категория не удаляется.
Событие GridView RowDeleting
запускается до вызова команды удаления ObjectDataSource, а его RowDeleted
событие возникает после. Создайте обработчики событий для этих двух событий с помощью следующего кода:
// A page variable to "remember" the deleted category's BrochurePath value
string deletedCategorysPdfPath = null;
protected void Categories_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// Determine the PDF path for the category being deleted...
int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories =
categoryAPI.GetCategoryByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
if (category.IsBrochurePathNull())
deletedCategorysPdfPath = null;
else
deletedCategorysPdfPath = category.BrochurePath;
}
protected void Categories_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
// Delete the brochure file if there were no problems deleting the record
if (e.Exception == null)
{
// Is there a file to delete?
if (deletedCategorysPdfPath != null)
{
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
}
}
}
В обработчике событий RowDeleting
извлекается CategoryID
удаляемой строки из коллекции GridView DataKeys
, к которой можно получить доступ через коллекцию e.Keys
. Затем вызывается класс CategoriesBLL
, чтобы GetCategoryByCategoryID(categoryID)
вернуть информацию о записи, которая удаляется. Если возвращаемый CategoriesDataRow
объект имеет незначноеNULL``BrochurePath
значение, он хранится в переменной deletedCategorysPdfPath
страницы, чтобы файл можно было удалить в обработчике RowDeleted
событий.
Замечание
Вместо получения сведений для удаляемой записи BrochurePath
в обработчике события Categories
, мы могли бы также добавить ее в свойство RowDeleting
GridView и получить доступ к значению записи через коллекцию BrochurePath
. Это приведет к незначительному увеличению размера состояния представления GridView, но уменьшит объем необходимого кода и сохранит поездку в базу данных.
После вызова базовой команды удаления ObjectDataSource запускается обработчик событий GridView RowDeleted
. Если при удалении данных не было исключений и имеется значение deletedCategorysPdfPath
, pdf-файл удаляется из файловой системы. Обратите внимание, что этот дополнительный код не требуется для очистки двоичных данных категории, связанных с изображением. Это связано с тем, что данные изображения хранятся непосредственно в базе данных, поэтому удаление строки Categories
также удаляет данные изображения категории.
После добавления двух обработчиков событий снова запустите этот тестовый случай. При удалении категории связанный PDF-файл также удаляется.
Обновление связанных двоичных данных существующей записи обеспечивает некоторые интересные проблемы. Оставшаяся часть этого руководства подробно описывает добавление возможностей обновления в брошюру и рисунок. Шаг 6 изучает методы обновления информации о брошюре, а шаг 7 изучает методы обновления изображения.
Шаг 6. Обновление брошюры категории
Как описано в руководстве по вставке, обновлению и удалению данных , GridView предлагает встроенную поддержку редактирования на уровне строк, которая может быть реализована с помощью галочки флажка, если его базовый источник данных настроен соответствующим образом. В настоящее время объект ObjectDataSource еще не настроен на поддержку обновления, поэтому давайте добавим его CategoriesDataSource
.
Щелкните ссылку "Настройка источника данных" мастера ObjectDataSource и перейдите к второму шагу. Из-за использования DataObjectMethodAttribute
в CategoriesBLL
раскрывающийся список UPDATE должен автоматически заполняться UpdateCategory
перегрузкой, принимающей четыре входных параметра (для всех столбцов, кроме Picture
). Измените это так, чтобы он использовал перегрузку с пятью параметрами.
Рис. 9. Настройка ObjectDataSource для использования UpdateCategory
метода, включающего параметр Picture
(щелкните, чтобы просмотреть изображение полного размера)
Объект ObjectDataSource теперь будет содержать значение для его UpdateMethod
свойства, а также соответствующие UpdateParameter
значения. Как отмечалось на шаге 4, Visual Studio задает свойство ObjectDataSource OldValuesParameterFormatString
равным original_{0}
, когда используется мастер настройки источника данных. Это приведет к проблемам с вызовами метода обновления и удаления. Таким образом, удалите это свойство полностью или сбросите его до значения по умолчанию {0}
.
После завершения работы мастера и исправления OldValuesParameterFormatString
, декларативная разметка ObjectDataSource должна выглядеть следующим образом:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory" UpdateMethod="UpdateCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
<asp:Parameter Name="categoryID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Чтобы включить встроенные функции редактирования GridView, проверьте параметр "Включить редактирование" из смарт-тега GridView. При этом свойству CommandField ShowEditButton
будет присвоено значение true
, что приведет к добавлению кнопки "Изменить" (и кнопок "Обновить" и "Отменить" для редактируемой строки).
Рис. 10. Настройка GridView для поддержки редактирования (щелкните, чтобы просмотреть изображение полного размера)
Перейдите на страницу через браузер и нажмите одну из кнопок редактирования строки. Поля CategoryName
и Description
BoundFields представлены в виде текстовых полей. В BrochurePath
TemplateField отсутствует EditItemTemplate
, поэтому он продолжает отображать свою ItemTemplate
ссылку на брошюру. ImageField Picture
отрисовывается как TextBox, свойство которого Text
присваивается значению значения ImageField DataImageUrlField
в данном случае CategoryID
.
Рис. 11. Элемент GridView не имеет интерфейса BrochurePath
редактирования (щелкните, чтобы просмотреть изображение полного размера)
Настройка интерфейса редактированияBrochurePath
Нам нужно создать интерфейс редактирования для BrochurePath
TemplateField, который позволяет пользователю либо:
- Оставьте брошюру категории as-is,
- Обновите брошюру категории, загрузив новую брошюру, или
- Удалите брошюру категории полностью (в случае, если категория больше не имеет связанной брошюры).
Нам также нужно обновить Picture
интерфейс редактирования ImageField, но мы перейдем к этому на шаге 7.
В смарт-теге GridView щелкните ссылку "Изменить шаблоны" и выберите BrochurePath
templateField в EditItemTemplate
раскрывающемся списке. Добавьте веб-элемент управления RadioButtonList в этот шаблон, задав его ID
свойство BrochureOptions
и его AutoPostBack
свойство true
. В окне "Свойства" щелкните на многоточие в свойстве Items
, что откроет Редактор коллекции ListItem
. Добавьте следующие три выбора с Value
: 1, 2 и 3 соответственно.
- Использование текущей брошюры
- Удаление текущей брошюры
- Отправка новой брошюры
Присвойте первому ListItem
Selected
свойству значение true
.
Рис. 12.: Добавьте три элемента ListItem
в список RadioButtonList
Под элементом RadioButtonList добавьте элемент управления FileUpload с именем BrochureUpload
. Задайте для свойства Visible
значение false
.
Рис. 13. Добавьте элемент управления RadioButtonList и FileUpload в элемент управления EditItemTemplate
(нажмите, чтобы просмотреть изображение полного размера)
Этот RadioButtonList предоставляет три варианта для пользователя. Идея заключается в том, что элемент управления FileUpload будет отображаться только в том случае, если выбран последний параметр , отправка новой брошюры. Для этого создайте обработчик событий для события RadioButtonList SelectedIndexChanged
и добавьте следующий код:
protected void BrochureOptions_SelectedIndexChanged(object sender, EventArgs e)
{
// Get a reference to the RadioButtonList and its Parent
RadioButtonList BrochureOptions = (RadioButtonList)sender;
Control parent = BrochureOptions.Parent;
// Now use FindControl("controlID") to get a reference of the
// FileUpload control
FileUpload BrochureUpload =
(FileUpload)parent.FindControl("BrochureUpload");
// Only show BrochureUpload if SelectedValue = "3"
BrochureUpload.Visible = (BrochureOptions.SelectedValue == "3");
}
Так как элементы управления RadioButtonList и FileUpload находятся в шаблоне, необходимо написать немного кода для программного доступа к этим элементам управления. Обработчик SelectedIndexChanged
событий передает ссылку на RadioButtonList в входном параметре sender
. Чтобы использовать элемент FileUpload, необходимо получить родительский элемент для RadioButtonList и применять метод FindControl("controlID")
из него. После того как у нас есть ссылка на элементы управления RadioButtonList и FileUpload, свойство элемента управления FileUpload имеет значение Visible
только в том случае, если элемент управления true
RadioButtonList SelectedValue
равен 3, который является Value
для новой брошюры ListItem
Upload.
Для тестирования интерфейса редактирования в этом коде потребуется некоторое время. Нажмите кнопку "Изменить" для строки. Изначально следует выбрать параметр "Использовать текущую брошюру". Изменение выбранного индекса вызывает обратный вызов. Если выбран третий параметр, отображается элемент управления FileUpload, в противном случае он скрыт. На рисунке 14 показан интерфейс редактирования при первом нажатии кнопки "Изменить"; На рисунке 15 показан интерфейс после выбора параметра "Отправить новую брошюру".
Рис. 14. Изначально выбран параметр "Использовать текущую брошюру" (щелкните, чтобы просмотреть изображение полного размера)
Рис. 15. Выбор параметра "Отправить новую брошюру" отображает элемент управления FileUpload (щелкните, чтобы просмотреть изображение полного размера)
Сохранение файла брошюры и обновление столбцаBrochurePath
При нажатии кнопки обновления в GridView вызывается событие RowUpdating
. Вызывается команда обновления ObjectDataSource, а затем срабатывает событие GridViewRowUpdated
. Как и при удалении рабочего процесса, необходимо создать обработчики событий для обоих этих событий. В обработчике RowUpdating
событий необходимо определить, какие действия следует предпринять на основе SelectedValue
BrochureOptions
RadioButtonList:
- Если значение
SelectedValue
равно 1, мы хотим использовать тот жеBrochurePath
параметр. Поэтому необходимо задать параметр ObjectDataSourcebrochurePath
для существующегоBrochurePath
значения обновляемой записи. Параметр ObjectDataSourcebrochurePath
можно задать с помощьюe.NewValues["brochurePath"] = value
. -
SelectedValue
Если значение равно 2, то необходимо задать для записиBrochurePath
значениеNULL
. Это можно сделать, задав параметр ObjectDataSourcebrochurePath
равнымNothing
, что приводит к использованию базы данныхNULL
в инструкцииUPDATE
. Если существует существующий файл брошюры, который удаляется, необходимо удалить существующий файл. Однако мы хотим сделать это только в том случае, если обновление завершается без вызова исключения. -
SelectedValue
Если значение равно 3, то мы хотим убедиться, что пользователь загрузил PDF-файл, а затем сохраните его в файловой системе и обновите значение столбцаBrochurePath
записи. Кроме того, если есть существующий файл брошюры, который заменяется, необходимо удалить предыдущий файл. Однако мы хотим сделать это только в том случае, если обновление завершается без вызова исключения.
Действия, необходимые для выполнения, когда значение RadioButtonList SelectedValue
равно 3, практически идентичны тем, которые используются обработчиком событий DetailsView ItemInserting
. Этот обработчик событий выполняется при добавлении новой записи категории из элемента управления DetailsView, добавленного в предыдущем руководстве. Таким образом, нам следует вынести эту функциональность в отдельные методы. В частности, я переместил общие функциональные возможности в два метода:
-
ProcessBrochureUpload(FileUpload, out bool)
принимает в качестве входного экземпляра элемента управления FileUpload и логическое значение, указывающее, должна ли операция удаления или редактирования продолжаться или если она должна быть отменена из-за некоторой ошибки проверки. Этот метод возвращает путь к сохраненного файла илиnull
если файл не был сохранен. -
DeleteRememberedBrochurePath
удаляет файл, указанный в переменнойdeletedCategorysPdfPath
страницы посредством пути, еслиdeletedCategorysPdfPath
не равенnull
.
Ниже приведен код для этих двух методов. Обратите внимание на сходство между ProcessBrochureUpload
и обработчиком событий ItemInserting
DetailsView из предыдущего руководства. В этом руководстве я обновил обработчики событий DetailsView, чтобы использовать эти новые методы. Скачайте код, связанный с этим руководством, чтобы просмотреть изменения обработчиков событий DetailsView.
private string ProcessBrochureUpload
(FileUpload BrochureUpload, out bool CancelOperation)
{
CancelOperation = false; // by default, do not cancel operation
if (BrochureUpload.HasFile)
{
// Make sure that a PDF has been uploaded
if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName),
".pdf", true) != 0)
{
UploadWarning.Text =
"Only PDF documents may be used for a category's brochure.";
UploadWarning.Visible = true;
CancelOperation = true;
return null;
}
const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension =
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension,
"-", iteration, ".pdf");
iteration++;
}
// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
return brochurePath;
}
else
{
// No file uploaded
return null;
}
}
private void DeleteRememberedBrochurePath()
{
// Is there a file to delete?
if (deletedCategorysPdfPath != null)
{
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
}
}
Обработчики событий GridView RowUpdating
и RowUpdated
используют методы ProcessBrochureUpload
и DeleteRememberedBrochurePath
, как показано в следующем коде:
protected void Categories_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// Reference the RadioButtonList
RadioButtonList BrochureOptions =
(RadioButtonList)Categories.Rows[e.RowIndex].FindControl("BrochureOptions");
// Get BrochurePath information about the record being updated
int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories =
categoryAPI.GetCategoryByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
if (BrochureOptions.SelectedValue == "1")
{
// Use current value for BrochurePath
if (category.IsBrochurePathNull())
e.NewValues["brochurePath"] = null;
else
e.NewValues["brochurePath"] = category.BrochurePath;
}
else if (BrochureOptions.SelectedValue == "2")
{
// Remove the current brochure (set it to NULL in the database)
e.NewValues["brochurePath"] = null;
}
else if (BrochureOptions.SelectedValue == "3")
{
// Reference the BrochurePath FileUpload control
FileUpload BrochureUpload =
(FileUpload)Categories.Rows[e.RowIndex].FindControl("BrochureUpload");
// Process the BrochureUpload
bool cancelOperation = false;
e.NewValues["brochurePath"] =
ProcessBrochureUpload(BrochureUpload, out cancelOperation);
e.Cancel = cancelOperation;
}
else
{
// Unknown value!
throw new ApplicationException(
string.Format("Invalid BrochureOptions value, {0}",
BrochureOptions.SelectedValue));
}
if (BrochureOptions.SelectedValue == "2" ||
BrochureOptions.SelectedValue == "3")
{
// "Remember" that we need to delete the old PDF file
if (category.IsBrochurePathNull())
deletedCategorysPdfPath = null;
else
deletedCategorysPdfPath = category.BrochurePath;
}
}
protected void Categories_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
// If there were no problems and we updated the PDF file,
// then delete the existing one
if (e.Exception == null)
{
DeleteRememberedBrochurePath();
}
}
Обратите внимание, что RowUpdating
обработчик событий использует ряд условных инструкций для выполнения соответствующего действия на BrochureOptions
основе значения свойства RadioButtonList SelectedValue
.
С помощью этого кода можно изменить категорию и использовать ее текущую брошюру, не использовать брошюру или загрузить новую. Идите вперед и попробуйте его. Задайте точки останова в RowUpdating
обработчиках событий и RowUpdated
получите представление о рабочем процессе.
Шаг 7. Отправка нового рисунка
Интерфейс редактирования Picture
ImageField отображается в виде текстового поля, в которое подставляется значение его свойства DataImageUrlField
. Во время рабочего процесса редактирования GridView передает параметр объекту ObjectDataSource, имя которого является значением свойства DataImageUrlField
ImageField, а значение параметра — это значение, введенное в текстовое поле в интерфейсе редактирования. Это поведение подходит при сохранении образа в виде файла в файловой системе и DataImageUrlField
содержит полный URL-адрес образа. При таких обстоятельствах интерфейс редактирования отображает URL-адрес изображения в текстовом поле, которое пользователь может изменить и сохранить обратно в базу данных. Этот интерфейс по умолчанию не позволяет пользователю отправлять новый образ, но позволяет им изменить URL-адрес изображения с текущего значения на другое. Однако в этом руководстве интерфейс редактирования ImageField по умолчанию недостаточен, так как Picture
двоичные данные хранятся непосредственно в базе данных, а свойство DataImageUrlField
содержит только CategoryID
.
Чтобы лучше понять, что происходит в нашем руководстве, когда пользователь редактирует строку с полем ImageField, рассмотрим следующий пример: пользователь редактирует строку с CategoryID
10, и в результате Picture
ImageField отображается в виде текстового поля со значением 10. Представьте, что пользователь изменяет значение в этом текстовом поле на 50 и нажимает кнопку "Обновить". Происходит обратная передача, и GridView изначально создает параметр с именем CategoryID
и значением 50. Однако перед отправкой этого параметра (и параметров CategoryName
и Description
) GridView добавляет значения из коллекции DataKeys
. Поэтому он перезаписывает CategoryID
параметр с базовым CategoryID
значением текущей строки 10. Короче говоря, интерфейс редактирования ImageField не имеет влияния на процесс редактирования для этого руководства, так как имена свойства ImageField и значения сетки DataImageUrlField
одинаковы.
Хотя ImageField упрощает отображение изображения на основе данных базы данных, мы не хотим предоставить текстовое поле в интерфейсе редактирования. Вместо этого мы хотим предложить элемент управления FileUpload, который пользователь может использовать для изменения рисунка категории. В отличие от BrochurePath
значения, в этих учебниках мы решили, что каждая категория должна иметь изображение. Поэтому нам не нужно разрешать пользователю указывать, что не существует связанного рисунка, пользователь может отправить новое изображение или оставить текущее изображение as-is.
Чтобы настроить интерфейс редактирования ImageField, необходимо преобразовать его в TemplateField. В смарт-теге GridView щелкните ссылку "Редактировать столбцы", выберите ImageField и щелкните ссылку "Преобразовать это поле в TemplateField".
Рис. 16. Преобразование ImageField в templateField
Преобразование ImageField в TemplateField таким образом создает TemplateField с двумя шаблонами. Как показано в следующем декларативном синтаксисе, ItemTemplate
содержит элемент управления Image Web, свойство которого ImageUrl
устанавливается с помощью синтаксиса привязки данных на основе свойств DataImageUrlField
и DataImageUrlFormatString
ImageField.
EditItemTemplate
содержит TextBox, значение свойства Text
которого привязано к указанному свойством DataImageUrlField
.
<asp:TemplateField>
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Eval("CategoryID") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Image ID="Image1" runat="server"
ImageUrl='<%# Eval("CategoryID",
"DisplayCategoryPicture.aspx?CategoryID={0}") %>' />
</ItemTemplate>
</asp:TemplateField>
Нам нужно обновить EditItemTemplate
, чтобы использовать элемент управления «FileUpload». В смарт-теге GridView щелкните ссылку "Изменить шаблоны", а затем выберите Picture
templateField s EditItemTemplate
в раскрывающемся списке. В шаблоне вы увидите текстовое поле, удалите его. Затем перетащите элемент управления FileUpload из панели элементов в шаблон, задав его значение ID
PictureUpload
. Кроме того, добавьте текст, чтобы изменить рисунок категории, укажите новое изображение. Чтобы изображение категории оставалось прежним, оставьте поле пустым в шаблоне.
Рис. 17. Добавление элемента управления FileUpload в EditItemTemplate
(нажмите, чтобы увидеть изображение в полном размере)
После настройки интерфейса редактирования просмотрите ход выполнения в браузере. При просмотре строки в режиме только для чтения изображение категории отображается как раньше, но при нажатии кнопки "Изменить" столбец рисунка отображается как текст с элементом управления FileUpload.
Рис. 18. Интерфейс редактирования включает элемент управления FileUpload (щелкните, чтобы просмотреть изображение полного размера)
Помните, что ObjectDataSource настроен для вызова метода класса CategoriesBLL
, который принимает двоичные данные для рисунка в виде массива UpdateCategory
в качестве входных. Если этот массив имеет null
значение, вызывается альтернативная UpdateCategory
перегрузка, которая выдает UPDATE
инструкцию SQL, которая не изменяет Picture
столбец, тем самым оставляя текущую картину категории нетронутой. Поэтому в обработчике событий GridView RowUpdating
необходимо программно ссылаться на PictureUpload
элемент управления FileUpload и определить, был ли отправлен файл. Если он не был отправлен, мы не хотим указывать значение для picture
параметра. С другой стороны, если файл был загружен в элемент управления PictureUpload
FileUpload, мы хотим убедиться, что это файл формата JPG. Если это так, мы можем отправить его двоичное содержимое в ObjectDataSource с помощью picture
параметра.
Как и в коде, используемом на шаге 6, большая часть кода, необходимого здесь, уже существует в обработчике событий DetailsView ItemInserting
. Поэтому я рефакторинговал общие функциональные возможности в новый метод ValidPictureUpload
и обновил ItemInserting
обработчик событий для использования этого метода.
Добавьте следующий код в начало обработчика событий GridView RowUpdating
. Важно, чтобы этот код пришел к коду, который сохраняет файл брошюры, так как мы не хотим сохранить брошюру в файловой системе веб-сервера, если недопустимый файл рисунка отправлен.
// Reference the PictureUpload FileUpload
FileUpload PictureUpload =
(FileUpload)Categories.Rows[e.RowIndex].FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
// Make sure the picture upload is valid
if (ValidPictureUpload(PictureUpload))
{
e.NewValues["picture"] = PictureUpload.FileBytes;
}
else
{
// Invalid file upload, cancel update and exit event handler
e.Cancel = true;
return;
}
}
Метод ValidPictureUpload(FileUpload)
принимает элемент управления FileUpload в качестве единственного входного параметра и проверяет расширение отправленного файла, чтобы убедиться, что отправленный файл является JPG; он вызывается только в том случае, если файл рисунка отправлен. Если файл не загружен, параметр рисунка не задан и поэтому использует его значение null
по умолчанию. Если было загружено изображение, и ValidPictureUpload
возвращает true
, параметру picture
назначаются двоичные данные загруженного изображения; если метод возвращает false
, рабочий процесс обновления отменяется, и обработчик событий завершается.
Код ValidPictureUpload(FileUpload)
метода, рефакторинг которого был выполнен из обработчика событий ItemInserting
DetailsView, следует:
private bool ValidPictureUpload(FileUpload PictureUpload)
{
// Make sure that a JPG has been uploaded
if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpg", true) != 0 &&
string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpeg", true) != 0)
{
UploadWarning.Text =
"Only JPG documents may be used for a category's picture.";
UploadWarning.Visible = true;
return false;
}
else
{
return true;
}
}
Шаг 8. Замена исходных изображений категорий на JPG
Помните, что исходные восемь категорий рисунков представляют собой растровые файлы, упакованные в заголовок OLE. Теперь, когда мы добавили возможность изменить существующее изображение записи, уделите время, чтобы заменить эти битмапы на JPG. Если вы хотите продолжить использовать изображения текущей категории, их можно преобразовать в JPG, выполнив следующие действия.
- Сохраните растровые изображения на жестком диске. Перейдите на
UpdatingAndDeleting.aspx
страницу в браузере и для каждой из первых восьми категорий щелкните изображение правой кнопкой мыши и сохраните рисунок. - Откройте изображение в выбранном редакторе изображений. Например, можно использовать Microsoft Paint.
- Сохраните растровое изображение в виде изображения JPG.
- Обновите изображение категории через редакторский интерфейс, используя JPG-файл.
После редактирования категории и отправки изображения JPG изображение не будет отображаться в браузере, так как DisplayCategoryPicture.aspx
страница удаляет первые 78 байт с рисунков первых восьми категорий. Это исправлено путем удаления кода, выполняющего очистку заголовка OLE. После этого DisplayCategoryPicture.aspx``Page_Load
обработчик событий должен иметь только следующий код:
protected void Page_Load(object sender, EventArgs e)
{
int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
// Get information about the specified category
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories = _
categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
// For new categories, images are JPGs...
// Output HTTP headers providing information about the binary data
Response.ContentType = "image/jpeg";
// Output the binary data
Response.BinaryWrite(category.Picture);
}
Замечание
Интерфейсам UpdatingAndDeleting.aspx
вставки и редактирования на странице требуется немного доработки. Поля CategoryName
и Description
boundFields в DetailsView и GridView должны быть преобразованы в TemplateFields. Поскольку значения CategoryName
не допускаются в NULL
, необходимо добавить RequiredFieldValidator. И текстовое Description
поле, вероятно, должно быть преобразовано в многострочное текстовое поле. Я оставляю эти завершающие штрихи в качестве упражнения для вас.
Сводка
В этом руководстве мы рассмотрим работу с двоичными данными. В этом руководстве и предыдущих трех мы узнали, как двоичные данные могут храниться в файловой системе или непосредственно в базе данных. Пользователь предоставляет двоичные данные в систему, выбрав файл с жесткого диска и отправив его на веб-сервер, где его можно сохранить в файловой системе или вставить в базу данных. ASP.NET 2.0 содержит элемент управления FileUpload, который делает предоставление такого интерфейса простым, как перетаскивание и вставка. Однако, как отмечалось в руководстве по отправке файлов , элемент управления FileUpload подходит только для относительно небольших отправки файлов, в идеале не превышающий мегабайт. Мы также изучили, как связать отправленные данные с базовой моделью данных, а также как редактировать и удалять двоичные данные из существующих записей.
В следующем наборе учебников рассматриваются различные методы кэширования. Кэширование позволяет повысить общую производительность приложения, принимая результаты из дорогостоящих операций и сохраняя их в расположении, к которому можно быстрее получить доступ.
Счастливое программирование!
Сведения о авторе
Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Учись ASP.NET 2.0 за 24 часа. С ним можно связаться по адресу mitchell@4GuysFromRolla.com.
Особое спасибо кому
Эта серия учебников была проверена многими полезными рецензентами. Ведущий рецензент этого руководства — Тереса Мерфи. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.