Добавление автофильтра и сортировки приводит к сбою 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 должен проверить, определено ли имя перед сортировкой и создать его автоматически, если это необходимо (что, по-видимому, происходит, если вы фильтруете, а не сортируете).