Calc Автофильтр: баг или не баг?

Автор eeigor, 5 февраля 2022, 17:41

0 Пользователи и 1 гость просматривают эту тему.

sokol92

#30
Спасибо, дошло.  :beer:
При прямом обращении к свойству ThisComponent.UnnamedDatabaseRanges выдает сообщение о ненайденном свойстве. Через getPropertyValue находит.

Первый раз такое вижу.  ???
Владимир.

mikekaganski

С уважением,
Михаил Каганский

sokol92

А я настроился, что вот-вот узнаю о каких-то таинственных свойствах (силах гемоглобина).
Владимир.

eeigor

#33
Как-то так...
Sub TestUnnamedDatabaseRanges()
Const DATA = "MyData"
Dim doc As Object, oUnnamedDBRanges As Object, oAnonymousSheet As Object
Dim oDataRange As Object
Dim nSheet%, sName$
Dim bMode As Boolean

doc = ThisComponent
oDataRange = doc.NamedRanges.getByName(DATA).ReferredCells
nSheet = oDataRange.RangeAddress.Sheet

oUnnamedDBRanges = doc.getPropertyValue("UnnamedDatabaseRanges")
oAnonymousSheet = oUnnamedDBRanges.getByTable(nSheet)

sName = oAnonymousSheet.Name
bMode = oAnonymousSheet.AutoFilter
MsgBox "AnonymousSheet.Name = " & sName & Chr(10) _
& "AnonymousSheet.AutoFilter = " & bMode & Chr(10) _
& "Sheet.Name = " & doc.Sheets(nSheet).Name
End Sub


Edit 1:
oAnonymousSheet.AutoFilter = Not bMode  'read/write

Владимир, это тот же доступ к свойству листа типа "AutoFilterMode", но без ExcelApp и диспетчера команд.

Edit 2:
Несмотря на то, что мы запрашиваем лист oAnonymousSheet, его свойство DataArea ссылается на весь диапазон листа, если на нём нет ни одного именованного диапазона. Если есть, то на него (на именованный диапазон). Если же именованных диапазонов несколько, то на тот, у которого кнопки автофильтра отображаются, иначе... на какой-то. Автофильтр может быть установлен только на одном из диапазонов одновременно. С ним (у которого кнопки автофильтра отображются) и работает свойство DataArea. Если на листе несколько диапазонов, а кнопки автофильтра не отображаются, то DataArea возвращает какой-то диапазон (счётчика нет, неясно какой). Видимо, это и неважно: мы убираем кнопки автофильтра на листе, то есть на произвольном диапазоне, а отображаем на том, который нам нужен.

Edit 3:
Function GetAutoFilterMode(Optional nSheet%) As Boolean
''' Returnes: True if the AutoFilter drop-down arrows are currently displayed on the sheet.

On Local Error GoTo Failed
Dim oUnnamedDBRanges As Object, oAnonSheetDB As Object  'anonymous DatabaseRange

If IsMissing(nSheet) Then
nSheet = ThisComponent.CurrentController.ActiveSheet.RangeAddress.Sheet
End If
oUnnamedDBRanges = ThisComponent.getPropertyValue("UnnamedDatabaseRanges")
oAnonSheetDB = oUnnamedDBRanges.getByTable(nSheet)  '<- ScDatabaseRangeObj
GetAutoFilterMode = oAnonSheetDB.AutoFilter

Failed:
End Function

Sub Test_GetAutoFilterMode()
Print "ActiveSheet.AutoFilterMode = " & GetAutoFilterMode()
End Sub

Sub SetAutoFilterMode(bValue As Boolean, Optional nSheet%)
''' Display of hide the drop-down arrows.
''' Remarks: In Excel, you can set the AutoFilterMode property to False
''' to remove the arrows, but you cannot set it to True. Here you can do both.

On Local Error GoTo Failed
Dim oUnnamedDBRanges As Object, oAnonSheetDB As Object  'anonymous DatabaseRange

If IsMissing(nSheet) Then
nSheet = ThisComponent.CurrentController.ActiveSheet.RangeAddress.Sheet
End If
oUnnamedDBRanges = ThisComponent.getPropertyValue("UnnamedDatabaseRanges")
oAnonSheetDB = oUnnamedDBRanges.getByTable(nSheet)  '<- ScDatabaseRangeObj
oAnonSheetDB.AutoFilter = bValue

Failed:
End Sub

Sub Test_SetAutoFilterMode()
Dim bMode As Boolean: bMode = GetAutoFilterMode()
Print "ActiveSheet.AutoFilterMode = " & bMode

Call Common.SetAutoFilterMode(Not bMode)
Print "ActiveSheet.AutoFilterMode = " & GetAutoFilterMode()
End Sub
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

#34
tdf#147257

Цитата: eeigor от  7 февраля 2022, 17:38это тот же доступ к свойству листа типа "AutoFilterMode", но без ExcelApp и диспетчера команд.

Да, и это позволяет "квалифицированно" удалить автофильтр листа (если он есть). Где я вчера только не искал "UnnamedDatabaseRanges".

Кстати, MRI это свойство тоже не видит. Проблемы с интроспекцией? А я в баге виню Basic.
Владимир.

eeigor

#35
Я разместил код получения/присвоения значения свойства AutoFilter в ответе #33: Edit 3
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#36
Это, скорее, не ошибка (bug), а исключительная ситуация (exception). В реальной жизни я так делать не буду.
Итак, выводим режим автофильтра (отображения кнопок раскрывающихся списков) из рабочего состояния.
Заметим сразу, что команда Ctrl+Shift+L с этой ситуацией справляется правильно. А наши макросы - нет. Причём и те, что с oExcelApp (oExcelApp.ActiveSheet.AutoFilterMode), и другие - с UnnamedDatabaseRanges, приведённые в этой теме.

На листе есть именованный диапазон данных.
1. Выбираем не его, а одинокую (окружённую пустыми столбцами/строками) пустую ячейку в свободной части листа и присваиваем ей любое значение, например "AutoFilter". Включаем отображение кнопки автофильтра на этой ячейке и очищаем ячейку. Кнопка останется.
2. Перемещаем фокус на наш именованный диапазон данных (выбираем любую ячейку).
Если сейчас нажать на клавиши Ctrl+Shift+L, то у той одинокой и пустой ячейки кнопка автофильтра исчезнет, а в первой строке нашего диапазона данных кнопки отобразятся. Всё верно.
Но при использовании макросов (и с oExcelApp, и с UnnamedDatabaseRanges) начинается неразбериха.
Я хочу отобразить кнопки на нашем диапазоне данных, а не могу, потому что та пустая ячейка уже отображает эту кнопку и функция GetAutoFilterMode() возвращает True (автофильтр включен, хоть и неизвестно где). Вторая методика с UnnamedDatabaseRanges выдаёт подобный казус: DataArea ссылается на наш именованный диапазон, кнопки автофильтра у которого, как мы понимаем, не отображаются, но свойство AutoFilter в то же время показывает True, потому что "где-то там вдалеке" осталась пустая ячейка с кнопкой на этом листе (именно на листе, ибо диапазон с автофильтром может быть один). Значения свойств DataArea и AutoFilter анонимного диапазона базы данных рассогласованы, однако.

Знать бы, какой код выполняется при нажатии на клавиши Ctrl+Shift+L, вот в чём вопрос. :)

Edit:
Ctrl+Shift+L добавляет к той одинокой ячейке с кнопкой свои кнопки к именованному диапазону данных, но они... оказываются заблокированными*: их нельзя даже раскрыть. На скришноше одиноко стоящая ячейка с кнопкой справа внизу. Она-то и портит всё дело. Кнопки выше заблокированы. Надо удалить автофильтр и включить снова: Ctrl+Shift+L два раза. Вот с этим эта команда справляется. Макросы нет.

______
* Заблокированы, если я наложил свой фильтр (ситуация отображена на скриншоте). Можно наложить простой стандартный фильтр вручную.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#37
В ситуации, описанной выше (см. скриншот), следующая процедура будет управлять отображением кнопки отдельной ячейки, а не кнопок строки заголовков диапазона данных, где установлен курсор. Я писал о рассогласовании свойств DataArea и AutoFilter анонимного диапазона.
oAnonSheetDB.AutoFilter = Not oAnonSheetDB.AutoFilter
Отображает кнопки и скрывает их, но не там, где нужно.
Sub ToggleAutoFilter(Optional nSheet%)
''' Display or hide the drop-down arrows.
''' Remarks: In Excel, you can set the AutoFilterMode property to False
''' to remove the arrows, but you cannot set it to True. Here you can do both.

On Local Error GoTo Failed
Dim oUnnamedDBRanges As Object, oAnonSheetDB As Object  'anonymous DatabaseRange

If IsMissing(nSheet) Then
nSheet = ThisComponent.CurrentController.ActiveSheet.RangeAddress.Sheet
End If
oUnnamedDBRanges = ThisComponent.getPropertyValue("UnnamedDatabaseRanges")
oAnonSheetDB = oUnnamedDBRanges.getByTable(nSheet)  '<- ScDatabaseRangeObj
oAnonSheetDB.AutoFilter = Not oAnonSheetDB.AutoFilter

Failed:
End Sub
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

Нужно задачи решать по отдельности.
1. Удалить макросом автофильтр с листа (если он есть) и все его "визуальные следы".
2. Добавить макросом автофильтр на лист (на котором нет автофильтра)

Первая задача.
Option Explicit

' lang:ru
' Удаляет (авто)фильтр из листа или диапазона (DatabaseRange).
' - oDoc     документ Calc.
' - oRange   диапазон (DatabaseRange) или лист документа Calc.
'
' Изменяет выделенные ячейки листа.
Sub RemoveFilter(Byval oDoc, Byval oRange)
  Dim oDisp
  If HasUnoInterfaces(oRange, "com.sun.star.sheet.XSpreadsheet") Then
    oRange=GetSheetFilterRange(oDoc, oRange)
  ElseIf Not HasUnoInterfaces(oRange, "com.sun.star.sheet.XDatabaseRange") Then
    Exit Sub
  End If   
 
  If oRange Is Nothing Then Exit Sub
  If Not oRange.AutoFilter Then Exit Sub
  If Not HasUnoInterfaces(oDoc.CurrentController, "com.sun.star.view.XSelectionSupplier") Then Exit Sub
 
  With oDoc.CurrentController
    .select oRange.ReferredCells.getCellByPosition(0,0)
    Wait 50
    oDisp=createUnoService("com.sun.star.frame.DispatchHelper")
    oDisp.executeDispatch(.Frame, ".uno:DataFilterRemoveFilter", "", 0, Array())
    Wait 50
    oDisp.executeDispatch(.Frame, ".uno:DataFilterHideAutoFilter", "", 0, Array())   
  End With 
End Sub

' Возвращает диапазон автофильтра листа или Nothing.
' - oDoc   документ Calc.
' - oSheet лист документа.
Function GetSheetFilterRange(oDoc, oSheet) as Object
  Dim i as Long
  i=oSheet.RangeAddress.Sheet
  With oDoc.getPropertyValue("UnnamedDatabaseRanges")
    If .hasByTable(i) Then
      GetSheetFilterRange=.getByTable(i)
    Else
      GetSheetFilterRange=Nothing
    End If
  End With
End Function

' Удаляет автофильтр с первого листа ThisComponent.
Sub TestRemoveFilter()
  RemoveFilter ThisComponent, ThisComponent.Sheets(0)
End Sub


Проверяем (TestRemoveFilter).
Владимир.