Элементы ListBox, переставленные с помощью JavaScript, вызывают ошибку проверки события при обратной передаче

Я создал элемент управления заменой элементов, состоящий из двух списков и нескольких кнопок, которые позволяют мне менять элементы между двумя списками. Обмен осуществляется с помощью JavaScript. Я также перемещаю элементы вверх и вниз в списке. Обычно, когда я перемещаю элементы в поле списка справа, я храню ключи данных элементов (GUID) в скрытом поле. При обратной передаче я просто читаю GUID с поля. Все отлично работает, но при обратной передаче я получаю следующее исключение:

Неверный аргумент обратной передачи или обратного вызова. Проверка события включена с использованием в конфигурации или <% @ Page EnableEventValidation = "true"%> на странице. В целях безопасности эта функция проверяет, что аргументы для событий обратной передачи или обратного вызова исходят от серверного элемента управления, который первоначально их представил. Если данные действительны и ожидаемы, используйте метод ClientScriptManager.RegisterForEventValidation, чтобы зарегистрировать данные обратной передачи или обратного вызова для проверки.

Я подготовил тестовое приложение. Все, что вам нужно сделать, это загрузить архив и запустить проект. На веб-странице выберите 3 элемента, нажмите Добавить все, затем переместите третий элемент на один уровень вверх и нажмите "Кнопка". Ошибка появится. Отключение валидации событий ни в коем случае не допустимо. Может кто-нибудь помочь мне, я провел уже два дня, не находя решения.

ИСПЫТАНИЕ ПРИМЕНЕНИЯ

7 ответов

Решение

Первый вариант принесет значительные накладные расходы. Я определил свой собственный элемент управления listbox, полученный из класса listbox, и выполнил переопределение данных loadpostback:

public class CustomListBox : ListBox
{
    protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
    {
        return true;
    }
}

Использование этого вместо обычного списка в моем пользовательском контроле решило проблему, однако есть ли риски, связанные с моим подходом?

Проблема в том, что сохраненное состояние просмотра списка и данные, полученные при обратной передаче, не совпадают. Проблема проверки события, скорее всего, является лишь одной из возможных проблем, которые могут возникнуть из-за этого подхода. Архитектура веб-форм не допускает такого рода использования, и, скорее всего, с этим подходом будет больше проблем, даже если вам удастся избежать проблемы проверки события. У вас есть несколько вариантов:

1) Самое простое - это сделать логику обмена на сервере вместо использования JavaScript. Таким образом, состояние представления будет сохраняться между обратными передачами, и дополнительные издержки нескольких обращений к серверу могут не быть проблемой.

2) Если проблема заключается в нескольких обращениях к серверу, напишите серверный элемент управления, который обрабатывает свое собственное состояние просмотра. Это, конечно, очень интересный подход.

3) Средним подходом может быть использование двух простых html-списков (просто напишите html-тэги без использования элементов управления asp.net) и сохранение на стороне клиента из javascript списка идентификаторов в скрытом поле. При обратной записи просто проанализируйте скрытое поле и извлеките идентификатор, игнорирующий HTML-списки.

Я бы пошел с 1, если нет серьезных аргументов против этого.

Несколько возможных вариантов:

  • Если возможно, отключите ViewState в двух списках. Без ViewState сервер не будет знать, какие были исходные значения, и, следовательно, не будет ошибки. При таком подходе вам нужно будет заново заполнить списки (из-за отсутствия ViewState) и, возможно, потребуется отслеживать выбор вручную - или вам нужно будет заполнить списки на этапе OnInit.

  • Отключите проверку события (если можете)

  • Заполните оба списка полностью на стороне сервера и используйте клиентский сценарий (javascript) для удаления записей из двух списков по мере необходимости.

Это жалуется, потому что выбранного элемента в списке не было в списке, когда он был представлен. Подумайте об использовании PageMethods через AJAX, чтобы вернуть ваши данные в форму вместо PostBack. Или используйте элементы управления без ввода для хранения данных - как неупорядоченные списки, между которыми вы перемещаете элементы списка. Вы можете поместить GUID в скрытые области внутри элемента списка, где вы можете получить их в случае необходимости.

Вы можете переопределить событие Render, чтобы зарегистрировать все возможные элементы списка в обоих списках. Таким образом, независимо от того, какие элементы перемещены куда, проверка ожидает их.

protected override void Render(HtmlTextWriter writer)
{
  foreach (DictionaryEntry entry in ColumnConfig) {          
    Page.ClientScript.RegisterForEventValidation(lstbxColumnsToExport.UniqueID,(string)entry.Key);
    Page.ClientScript.RegisterForEventValidation(lstbxNonExportColumns.UniqueID,(string)entry.Key);
  }
  base.Render(writer);
}

В качестве альтернативы вы можете использовать серверный HtmlSelect вместо ListBox, чтобы обойти проблему проверки события. Лучше всего то, что вы можете оставить большую часть своего кода в неизменном виде (т.е. логика заполнения списка такая же, как ListBox).

<select runat="server" id="myList" multiple="true" />

Случайно, вы уже пробовали это? Делайте это всякий раз, когда вы гадите со списком любым способом.

document.getElementById("listbox").selectedIndex = -1;
Другие вопросы по тегам