Добавление автофильтра и сортировки приводит к сбою Excel

Я разрабатываю приложение, где вы можете экспортировать некоторые данные в файл Excel с помощью OpenXML. Все работает нормально, за исключением автофильтра. Идея состоит в том, чтобы добавить автофильтр в основной текст данных, чтобы пользователь автоматически имел элементы управления для фильтрации и сортировки данных. Так в коде, я делаю что-то вроде этого:

var filter = new AutoFilter() { Reference = string.Format("{0}:{1}", topLeftCellReference, bottomRightCellReference ) };
worksheet.AppendChild(filter);

в экспортированном XLSX появляется что-то вроде этого:

<x:autoFilter ref="A4:L33" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" />

и он добавлен в рабочий лист между sheetData и mergeCells.

затем я могу открыть этот фильтр в Excel и она отлично работает. Ожидайте, если вы пытаетесь отсортировать столбец, сортирует столбец, а затем Excel аварийно завершает работу. Сохранение и перезагрузка файла (что заставляет Excel очищать все) не устраняет проблему. Но, если вы сначала примените фильтр (скажем, фильтровать столбец в > 10, затем удалить этот фильтр, теперь вы можете сортировать без сбоев. Я сохранил файл после применения фильтра и его удаления, и теперь этот файл в порядке, но глядя на XML "отремонтированный" файл, я не вижу никакой очевидной разницы.

кто-нибудь знает, что может вызвать проблему? Есть ли что-то еще, что я должен делать при применении автоматического фильтра, кроме добавления его на рабочий лист?

Примечание: мы используем Excel 2010 (версия 14.0.7153.5000)

вот пример (нажмите "Загрузить", и он загрузится как .zip. Переименовать в .xlsx для открытия в Excel. Включить редактирование, выбрать один из столбцы и попытаться).

редактировать: поиграйте с этим еще немного. Если вы сохраните файл в Excel, он все еще сломан. Однако если сначала применить фильтр (а затем очистить его), а затем повторно сохранить в Excel, вы получите рабочий файл. Присмотревшись к двум файлам (все еще сломанный сохраненный файл и теперь рабочий файл), я замечаю, что этот дополнительный бит добавлен в книгу после применения фильтра (и очищен):

  <x:definedNames>
    <x:definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'Sheet 1'!$A:$E</x:definedName>
  </x:definedNames>

не уверен, что это может быть что-то или нет...

1 ответов


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

<x:definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'Sheet 1'!$A:$E</x:definedName>

видимо _xlmn._FilterDatabase необходим для работы автофильтра (по крайней мере, для сортировки). Я думаю, если его нет, когда вы фильтруете, он создается, но если его нет, когда вы сортируете, он взрывается Excel.

поэтому вам нужно имя листа и ссылка на ячейку, чтобы заполнить его.

просмотр стандарта Open XML в разделе 18.2.5 definedName, Я вижу это:

Фильтр И Расширенный Фильтр

_xlnm .Критерии: это определенное имя относится к диапазону, содержащему значения критериев используется при применении расширенного фильтра к диапазону данных.

_xlnm ._FilterDatabase: может быть одним из следующих

а. это определенное имя относится к диапазону, к которому был расширенный фильтр прикладная. Это представляет диапазон исходных данных, нефильтрованный.

b. Это определенное имя относится к диапазону, к которому был применен автофильтр прикладная.

таким образом, кажется, что вам нужно добавить _xlnm._FilterDatabase для каждого листа, который имеет фильтр (похоже, нет способа иметь более одного фильтра на одном листе). Имя то же самое _xlmn_FilterDatabase независимо от того, сколько листов у вас с фильтрами, потому что я думаю только сочетание имени и localSheetId должны быть уникальными.

так, в конце концов, у меня есть что-то вроде это:

var filter = new AutoFilter() { Reference = string.Format("{0}:{1}", topLeftCellReference, bottomRightCellReference ) };
worksheet.AppendChild(filter);

workbookPart.Wookbook.DefinedNames.AppendChild(new DefinedName(string.Format("'{0}'!$A:",
    sheet.Name,
    leftColumnLetter,
    topRowIndex,
    rightColumnLetter,
    bottomRowIndex))
{
    Name = "_xlnm._FilterDatabase",
    LocalSheetId = sheet.SheetId - 1,
    Hidden = true
});

похоже, что это работает вокруг ошибки в Excel. Excel должен проверить, определено ли имя перед сортировкой и создать его автоматически, если это необходимо (что, по-видимому, происходит, если вы фильтруете, а не сортируете).