ID tematu: 73468
 |
Tymczasowe ukrywanie formuł |
Autor |
Wiadomość |
Maciej Gonet
Excel Expert

Wersja: Win Office 365
Pomógł: 3616 razy Posty: 10635
|
Wysłany: 09-10-2022, 01:22 Tymczasowe ukrywanie formuł
|
|
|
Czasem dobrze by było mieć w tej samej komórce wartość stałą i formułę.
Może to odnosić się do sytuacji, gdy normalnie korzystamy z wartości typowej obliczonej formułą, ale w pewnych okolicznościach chcemy mieć możliwość ręcznej korekty tej wartości.
Bywa też tak, że formuły są złożone i chcemy na pewien czas zrezygnować z ich aktualizacji (przeliczania), ale nie w całym arkuszu, tylko w wybranych komórkach.
Innym razem w komórce odliczamy czas i chcemy ten stoper na chwilę zatrzymać, ale z możliwością kontynuacji.
Osiągnięcie takich celów wprost nie jest możliwe, bo zawsze wprowadzenie stałej do komórki nadpisuje formułę, jeśli tam wcześniej była. Można jednak wykorzystać pewne sposoby, aby przechować formułę z komórki przez pewien czas w innym miejscu, a później odtworzyć ją, gdy zajdzie potrzeba.
Znam cztery takie sposoby, ale każdy z nich ma jakieś wady. Pewnie znalazłoby się i więcej bardziej wyrafinowanych pomysłów, ale liczę, że i te cztery mogą być przydatne w sprzyjających okolicznościach.
Dokładniejszy opis i proste przykłady w załączonych plikach.
Tymczasowe ukrywanie formuł.docx
|
Pobierz Plik ściągnięto 167 raz(y) 23.05 KB |
Ukrywanie formuł.xlsm
|
Pobierz Plik ściągnięto 154 raz(y) 32.05 KB |
|
|
 | ID posta:
421121
|
|
|
 |
|
|
|
Rafał B.
ExcelSpec


Wersja: Win Office 2021
Pomógł: 91 razy Posty: 524
|
Wysłany: 09-10-2022, 12:00
|
|
|
Czy analizował Kolega możliwość dopisania customego węzła do XML? Wydaje się, że to powinno się udać. W przypadku sukcesu nie wiem jak ewentualnie z utrzymaniem tych danych w binarnym .xlsb |
_________________ W erze zaawansowanych narzędzi LLM takich jak GPT 50% problemów użytkowników forum może być efektywnie rozwiązanych przez nich samych za pomocą tych narzędzi. Jednak często bardziej cenią swój czas niż czas tych, którzy oferują tutaj pomoc. |
|
 | ID posta:
421122
|
|
|
 |
|
|
Maciej Gonet
Excel Expert

Wersja: Win Office 365
Pomógł: 3616 razy Posty: 10635
|
Wysłany: 09-10-2022, 15:28
|
|
|
Obawiam się, że na te pytania nie potrafię odpowiedzieć. W ogóle nie bardzo rozumiem, o co pytasz. Ja nie jestem profesjonalnym informatykiem, zajmowałem się Excelem o tyle, o ile było mi to potrzebne w pracy. XML nie był obiektem mojego zainteresowania, również z plików .xlsb korzystam sporadycznie i nigdy nie analizowałem niuansów związanych z tymi plikami.
Napisałem to, co gdzieś znalazłem, sprawdziłem, zebrałem i przystosowałem do swoich potrzeb i tyle. Każdy może to teraz dalej testować i oswajać według woli i potrzeb. A ja nie mam wiele więcej do dodania, chyba że byłyby jakieś sprawy wymagające bardziej szczegółowego wyjaśnienia. |
|
 | ID posta:
421125
|
|
|
 |
|
|
Rafał B.
ExcelSpec


Wersja: Win Office 2021
Pomógł: 91 razy Posty: 524
|
Wysłany: 10-10-2022, 00:28
|
|
|
Nie chodziło mi o żadne wymyślne koncepty, ale faktycznie może napisałem w sposób niejasny.
Wiadomo, że upraszczając .xlsx i .xlsm są zestawami plików xml. I potencjalnie można spróbować do takiego XMLa dodać własny węzeł (o ile nie ma jakiejś wyrafinowanej walidacji przez Excela) lub twórcy nawet może przewidzieli jakiś klucz, gdzie można customowe dane dodać(?). Mamy bezpośredni dostęp do takiej formy poprzez właściwość Value z parametrem 11 (enumka XlRangeValueDataType.xlRangeValueXMLSpreadsheet) co m.in. poruszyłem w tym wątku całkiem niedawno https://www.excelforum.pl...lue-vt72373.htm
Co do pytania o .xlsb, to oczywiście format własny Excela, binarny, więc potencjalnie przy konwersji .xlsm do .xlsb takie "nadmiarowe" dane mogą ulegać usunięciu.
Ale z odpowiedzi Kolegi rozumiem, że temat otwarty pod tym względem; w wolnej chwili spróbuję (chyba, że mnie ktoś uprzedzi i powiadomi o wyniku na forum), bo takie potencjalne rozwiązanie byłoby czystsze i ogólniej nieco lepsze od zaproponowanych w tym wątku, bo nie zabierałoby żadnej funkcjonalności. No chyba że przeoczyliśmy jakąś właściwość nie typu `read-only` obiektu Range i da się prościej |
_________________ W erze zaawansowanych narzędzi LLM takich jak GPT 50% problemów użytkowników forum może być efektywnie rozwiązanych przez nich samych za pomocą tych narzędzi. Jednak często bardziej cenią swój czas niż czas tych, którzy oferują tutaj pomoc. |
|
 | ID posta:
421133
|
|
|
 |
|
|
Rafał B.
ExcelSpec


Wersja: Win Office 2021
Pomógł: 91 razy Posty: 524
|
Wysłany: 29-12-2023, 01:54
|
|
|
Dawno nie miałem okazji pisać czegoś ambitniejszego w VBA poza tzw. utrzymywaniem kodu i małymi przeróbkami, więc korzystając z przerwy świątecznej coś tam nareszcie wykodziłem nowego.
W temacie wątka okazało się, że intuicja i tym razem mnie nie zawiodła: skoro współczesny plik excela jest de facto paczką XML, to przypuszczałem, że w VBA przewidziano możliwość dopisania swoich węzłów do tego dokumentu. Zamiast przekopywać dokumentację, zapytałem model GPT4 i raczył był mi zasugerować użycie nieznanej mi kompletnie wcześniej metody `Workbook.CustomXMLParts.Add`.
Niestety chyba (?) nie ma możliwości przyporządkowania takiego węzła bezpośrednio do XML obiektu range, co chwilowo ostudziło moją euforię. Ale z powodu pilnej potrzeby opracowania rozwiązania optymalizacyjnego (włączanie/wyłączanie kalkulacji nie wchodziło w rachubę) powróciłem do tematu i tak powstało rozwiązanie oparte na dwóch napisanych przeze mnie klasach.
Podstawą jest obiekt klasy CustomXmlData. Pomaga w dodawaniu danych do XML skoroszytu. Zamiast opisu zacytuję przykład użycia z załączonego pliku:
Kod: |
' CustomXmlData - przykład wykorzystania klasy do zapisu danych do skoroszytu
' bez użycia żadnych dzieci klasy Worksheet jak np. Range
''
' Przykład - zapis
'
Public Sub Example0PrepareTestCase()
With CustomXmlData
' simple key-value data
.Add data:="4.34", id:="RateEUR", namespace:="BuyRate"
.Add data:="3.94", id:="RateUSD", namespace:="BuyRate"
.Add data:="4.00", id:="RateUSD", namespace:="SellRate"
' przy braku przestrzeni nazw, będzie użyta przestrzeń domyślna
.Add data:="Titelitury", id:="fancyName"
' 2D array
.Add data:=Array( _
Array("name", "Jan"), _
Array("lastname", "Iksiński"), _
Array("age", "62")), _
id:="p0187", _
namespace:="personalData"
End With
End Sub
''
' Przykład - odczyt
'
Public Sub Example0ReadSimpleData()
With CustomXmlData
' odczyt prostych wartości:
MsgBox .GetValueById(id:="fancyName")
' alternatywny sposób:
' MsgBox .GetDataAsCollection(id:="fancyName")("value")
MsgBox .GetValueById(id:="RateUSD", namespace:="SellRate")
' alternatywny sposób:
' .GetDataAsCollection(id:="RateUSD", namespace:="SellRate")("value")
MsgBox "Wiek zanonimizowanowego pacjenta id=`p0187` wynosi " & _
.GetValuesById(id:="p0187", namespace:="personalData")("age") & _
" lat(a)"
' przykład usunięcie wartości
.Delete id:="fancyName"
MsgBox .GetValueById(id:="fancyName") ' rzuca wyjątek
End With
End Sub
|
|
_________________ W erze zaawansowanych narzędzi LLM takich jak GPT 50% problemów użytkowników forum może być efektywnie rozwiązanych przez nich samych za pomocą tych narzędzi. Jednak często bardziej cenią swój czas niż czas tych, którzy oferują tutaj pomoc. |
Ostatnio zmieniony przez Rafał B. 29-12-2023, 02:08, w całości zmieniany 2 razy |
|
 | ID posta:
432431
|
|
|
 |
|
|
Rafał B.
ExcelSpec


Wersja: Win Office 2021
Pomógł: 91 razy Posty: 524
|
Wysłany: 29-12-2023, 01:58
|
|
|
I tutaj wchodzi już docelowe rozwiązanie: na tę klasę CustomXmlData utworzyłem wrapper w postaci klasy RangeFormula, która już wykonuje właściwe zadanie- czyli ułatwia przechowanie i zapis formuł do arkusza. Przykład użycia w kontekście tabeli:
Kod: |
'''
' Odpalane jednorazowo przy działającej formule.
' Zapisze istniejącą formułę z pierwszej komórki podanego zakresu,
' którą potem będzie można odtworzyć.
'
Public Sub Test2PrepareTestCase()
' "test_suma" i "test_potega" to pomocnicze nazwy, można je pominąć
' (wówczas do uruchamiania będzie potrzeba
' przy użyciu podawać obiekt Range)
With TestSheet2.ListObjects(1)
RangeFormula.SaveByRange .ListColumns("suma").DataBodyRange, , "test_suma"
RangeFormula.SaveByRange .ListColumns("potega").DataBodyRange, , "test_potega"
End With
End Sub
'''
'
'
Public Sub Test2values()
With RangeFormula
.ApplyValues "test_suma"
.ApplyValues "test_potega"
End With
End Sub
'''
'
'
Public Sub Test2formulas()
With RangeFormula
.ApplyFormulas "test_suma"
.ApplyFormulas "test_potega"
End With
End Sub
|
Reasumując nie ma potrzeby operowania na klasie CustomXmlData, jedynie RangeFormula (ale obie muszę być dołączone w projekcie). Te klasy rozdzieliłem głównie z powodu bałaganu jaki powstał przy braku ich separacji.
To przesłane rozwiązanie to na razie szkic pisany dość szybko "na kolanie" i zawiera kilka mniej lub bardziej poważnych spraw do poprawy, które zazwyczaj opisałem gdzieś tam w formie TODO w komentarzach.
Na pewno wymagałoby także optymalizacji, moze też zawierać jakieś "śmieci" ze starszych wersji, którcyh nie wychwyciłem, a które powinny zostać usunięte.
Zadanie początkowo proste okazało się mieć wiele pułapek, stąd koncepcja podczas pisania zmieniała się kilkukrotnie i stąd brak pewnej elegancji. Kod jest "niepudrowany", więc puryści jak np. master_mix może być zdegustowany prostym wykorzystaniem w klasie RangeFormula innej klasy (CustomXmlData), gdzie oczywiście powinien być zastosowany jako warstwa pośrednia interfejs eksponujący metody. Mam świadomość wielu mankamentów, po prostu takie rozwiązania można poprawiać w nieskończoność, a chciałem już jakiś zarys zaprezentować i udowodnić na zasadzie proof of concept od pomysłu do realizacji, że idąc tą drogą można wyeliminować w zasadzie wszystkie poważne wady innych rozwiązań w temacie przechowywania formuł.
RangeFormulaClass.xlsm
|
Pobierz Plik ściągnięto 71 raz(y) 95.85 KB |
|
_________________ W erze zaawansowanych narzędzi LLM takich jak GPT 50% problemów użytkowników forum może być efektywnie rozwiązanych przez nich samych za pomocą tych narzędzi. Jednak często bardziej cenią swój czas niż czas tych, którzy oferują tutaj pomoc. |
|
 | ID posta:
432432
|
|
|
 |
|
|
Maciej Gonet
Excel Expert

Wersja: Win Office 365
Pomógł: 3616 razy Posty: 10635
|
Wysłany: 29-12-2023, 02:23
|
|
|
Bardzo dziękuję za szczegółowe przedstawienie swojego pomysłu i koncepcji rozwiązania. Jest to już jednak dość wysoki poziom i trzeba się temu przyjrzeć z lekką głową.
Będę miał o czym myśleć w Nowy Rok.
A tymczasem życzę Tobie i nam wszystkim, żebyś częściej tu do nas zaglądał, póki Forum jeszcze istnieje. Zawsze można się czegoś ciekawego od Ciebie dowiedzieć. |
|
 | ID posta:
432433
|
|
|
 |
|
|
master_mix
Excel Expert


Wersja: Win Office 365
Pomógł: 1293 razy Posty: 2639
|
Wysłany: 30-12-2023, 22:22
|
|
|
Rafał B. miło Cie widzieć , już się martwiłem że utknąłeś pod stertą pieluch i się szybko nie pokażesz, a ludzi z ciętym językiem mało na tym forum
Ciekawy przykład i ciekawe podejście.
Już kiedyś rozważałem wykorzystanie tych nowych klas z serii CustomXML..., które głownie dla zastosowań VSTO się pojawiły, ale myślałem pod kątem zapamiętywania ostatnich wpisywanych wartości - temat porzuciłem, trochę mnie znudził a trochę lipa z tym zapisywaniem binarnym.
Znam sporo osób które notorycznie zapisują xlsb, lub nawet xls. - Edit: W xlsb działa wszystko prawidłowo, jednak jak ktoś zapisze xls, to lipa. Na szczęście już rzadkość ...
Klasy ciekawe i pożyteczne, zastosowań można znaleźć pewnie jeszcze kilka. Sporo metod własności i zdarzeń do wykorzystania.
Rafał B. napisał/a: | master_mix może być zdegustowany prostym wykorzystaniem w klasie RangeFormula |
Nie , no przestań
niechlujny kod to moja specjalność, innych tylko pouczam, czyli bardziej hipokryta niż purysta
Kod całkiem zgrabny i widać że się napracowałeś
Masz rację że takiego typu projekty można poprawiać w nieskończoność, właściwie każde projekty ...
Czepię się oczywiście czegoś:
sprawa jeden:
Jak wiesz nie jestem zwolennikiem "wymuszania" klas statycznych w VB, po to język VB, VB.NET mają moduły, których inne języki-platformy nie mają i tylko dlatego w innych językach są klasy statyczne. Ale to jak kiedyś wyjaśniliśmy sprawa poglądów religijnych :)
Sprawa dwa:
Projektując taki model danych, zakładamy że wykorzysta go jakiś inny programista i wcale nie musi on być bardzo zaawansowany w pisaniu, co za tym idzie wykorzystanie Twoich klas jest nieco skomplikowane i może być potraktowane mniej atrakcyjnie.
Sprawa trzy:
Myślę że niepotrzebnie ładujesz cały dokument do klas z biblioteki MS XML a poźniej nadpisujesz cały plik customowy, przecież masz do niego dostęp przez klasy office.CustomXML...
sprawa czterty:
Zapisywałbym customowe XML jako 1:1 => arkusz:XML
coś takiego:
Kod: | <rangeData xmlns="Arkusz1">
<range xmlns="A1" xmlns:ns2="formula" xmlns:ns3="value" ns2:formula="=D1" ns3:value="450">A1</range>
<range xmlns="A2" xmlns:ns2="formula" xmlns:ns3="value" ns2:formula="=E2" ns3:value="620">A2</range>
<range xmlns="A3" xmlns:ns2="formula" xmlns:ns3="value" ns2:formula="=G3*E2" ns3:value="483600">A3</range>
</rangeData> |
Moja propozycja jest taka (moduł standardowy - można przerobić na klasę):
Kod: | Public Type PropertyRange
value As Variant
formula As Variant
End Type
Public Enum ReadWrite
eRead = 0
eWrite = 1
End Enum
Private exRange As Excel.Range
Private myWorkbook As Excel.Workbook
Private myWorksheet As Excel.Worksheet
Public Function ExtendedRange(ByVal eRange As Excel.Range, _
Optional ByVal eReadWrite As ReadWrite = 0) As PropertyRange
Set exRange = eRange(1)
Set myWorksheet = eRange.Parent
Set myWorkbook = myWorksheet.Parent
Dim cPart As Office.customXMLPart
Set cPart = GetCustomXMLPart
If cPart Is Nothing Then
If eReadWrite = eRead Then
Exit Function
Else
Set cPart = CreateCustomXMLPart
End If
End If
Dim nd As Office.CustomXMLNode
Set nd = GetCustomXMLNode(cPart)
If nd Is Nothing Then
If eReadWrite = eRead Then
Exit Function
Else
Set nd = CreateCustomXMLNode(cPart)
ExtendedRange = GetAttributeNode(nd)
End If
Else
If eReadWrite = eRead Then
ExtendedRange = GetAttributeNode(nd)
Else
ExtendedRange = ReplaceAttributeNode(nd)
End If
End If
End Function
Private Function GetCustomXMLPart() As Office.customXMLPart
On Error Resume Next
Set GetCustomXMLPart = myWorkbook.CustomXMLParts.SelectByNamespace(myWorksheet.CodeName)(1)
End Function
Private Function GetCustomXMLNode(ByVal cXMLPart As Office.customXMLPart) As Office.CustomXMLNode
Dim rngAddress As String
Dim xpathQuery As String
rngAddress = exRange.address(False, False)
xpathQuery = "//*[namespace-uri()='" & rngAddress & "']"
Set GetCustomXMLNode = cXMLPart.SelectSingleNode(xpathQuery)
End Function
Private Function CreateCustomXMLPart() As Office.customXMLPart
Dim mMap As String
mMap = _
"<rangeData xmlns =""" & myWorksheet.CodeName & """ />"
Set CreateCustomXMLPart = myWorkbook.CustomXMLParts.Add(mMap)
End Function
Private Function CreateCustomXMLNode(ByVal cXMLPart As Office.customXMLPart) As Office.CustomXMLNode
Dim rngAddress As String
rngAddress = exRange.address(False, False)
cXMLPart.DocumentElement.AppendChildNode "range", rngAddress, msoCustomXMLNodeElement, rngAddress
Set CreateCustomXMLNode = cXMLPart.DocumentElement.LastChild
cXMLPart.addNode CreateCustomXMLNode, "formula", "formula", , msoCustomXMLNodeAttribute, exRange.FormulaLocal
cXMLPart.addNode CreateCustomXMLNode, "value", "value", , msoCustomXMLNodeAttribute, exRange.value
End Function
Private Function GetAttributeNode(ByVal node As Office.CustomXMLNode) As PropertyRange
Dim att
Dim retPropertyRange As PropertyRange
For Each att In node.Attributes
Select Case att.BaseName
Case "value"
retPropertyRange.value = att.NodeValue
Case "formula"
retPropertyRange.formula = att.NodeValue
End Select
Next
GetAttributeNode = retPropertyRange
End Function
Private Function ReplaceAttributeNode(ByVal node As Office.CustomXMLNode) As PropertyRange
Dim att
Dim retPropertyRange As PropertyRange
For Each att In node.Attributes
Select Case att.BaseName
Case "value"
att.NodeValue = exRange.value
retPropertyRange.value = att.NodeValue
Case "formula"
att.NodeValue = exRange.FormulaLocal
retPropertyRange.formula = att.NodeValue
End Select
Next
ReplaceAttributeNode = retPropertyRange
End Function |
Praktycznie jedna prosta publiczna metoda ogarnia temat.
Łatwy w implementacji nawet dla niedoświadczonych. Patrz *jpg
Test:
Kod: | Sub WykorzystajDoWatku_ExForum()
With Arkusz1
'zapisz Rnage:A1 - lub w pętli większy zakres
ExtendedRange .Range("A1"), eWrite
'użyj wartosci
.Range("A1").value = ExtendedRange(.Range("A1")).value
Stop 'luknij czy ok
''użyj formuły
.Range("A1").FormulaLocal = ExtendedRange(.Range("A1")).formula
End With
End Sub |
przykład w załączniku.
EDIT:
jeszcze dorzuciłbym bym publiczną metodę do sprawdzenia czy Range zapisane w xml
czyli będą 2 publicznie łącznie
Kod: | Public Function IsSavedRange(ByVal eRange As Excel.Range) As Boolean
Set exRange = eRange(1)
Set myWorksheet = eRange.Parent
Set myWorkbook = myWorksheet.Parent
Dim rngAddress As String
rngAddress = exRange.address(False, False)
Dim cPart As Office.customXMLPart
Dim nd As Office.CustomXMLNode
Set cPart = GetCustomXMLPart
If cPart Is Nothing Then
IsSavedRange = False
Exit Function
Else
Set nd = GetCustomXMLNode(cPart)
If nd Is Nothing Then
IsSavedRange = False
Exit Function
End If
End If
IsSavedRange = True
End Function |
scr1.jpeg
|
 |
Plik ściągnięto 8 raz(y) 13.52 KB |
scr2.jpeg
|
 |
Plik ściągnięto 8 raz(y) 17.96 KB |
scr3.jpeg
|
 |
Plik ściągnięto 4 raz(y) 22.77 KB |
scr4.jpeg
|
 |
Plik ściągnięto 8 raz(y) 36.33 KB |
RangeFormulaClass m_mModule.xlsm
|
Pobierz Plik ściągnięto 66 raz(y) 33.05 KB |
|
_________________
Podejmę współpracę (pracę)
Programowanie C#, Android, iOS, VB.NET, VBA, ASP.NET Core, WPF, Xamarin, Power Platforms, XAML, MVC, LINQ, Entity Framework. Bazy danych SQL Server, Oracle, MySQL, Firebird
Wrocław i okolice …lub zdalnie. |
|
 | ID posta:
432444
|
|
|
 |
|
|
Rafał B.
ExcelSpec


Wersja: Win Office 2021
Pomógł: 91 razy Posty: 524
|
Wysłany: 31-12-2023, 15:15
|
|
|
Dzięki za wskazówki i spostrzeżenia!
Ad 1. Co do klas statycznych, to faktycznie ciężko mnie przekonać do zmiany na moduły ze względu na a'la enkapsulację i Intellisense; nie lubię jak w wielkim projekcie IDE podpowiada mi tysiące zbędnych funkcji w złym kontekście, gdzie powinno podpowiadać tylko standardowe, przez co spowalnia mi to pisanie kodu.
Stosuję klasy statyczne w zasadzie tylko do narzędzi, które nie mają swojego stanu (pól). Wielu uznaje to za `code smell`, ale moja praktyka akurat nie podziela obaw co do tego zagadnienia. Modułów używam głównie do skryptów, które korzystają oczywiście ze stosownych klas.
Z wad klas statycznych (i ogólnie klas) widzę tylko jedną i jest to błąd projektowy VBA (może VB6 też?), że brak public const i public enum. Stałe i enumy nie są eksponowane i dostępne bezpośrednio (bez żadnych Property Get i powoływania instancji). Jako obejście załączam stałe/enumy zwykle w zwykłym module, stosując dobre nazewnictwo.
Ad 2.
Pisząc te klasy starałem się mieć to na uwadze, żeby ktoś mógł skorzystać z owoców mojej pracy. Skoro zwróciłeś uwagę, że ich stosowanie jest dość trudne, to jednak chyba na tym polu niestety poległem., chociaż to był jeden z moich głównych celów- mało metod, wygoda i ekspresywne nazwy.
Co do pozostałych punktów to zawierają cenne wskazówki, ale muszę zrozumieć o co chodzi, przysiądę do tego jeszcze po Nowym Roku. MSXML stosowałem tylko do parsowania węzła, nie wiedziałem że można inaczej pomijając manipulacje stringami. Nie użylem worksheet.codename, bo chciałem pierwotnie, by można było używać klasy na innym pliku niż bieżący (poza ThisWorkbook). Alle po zastanowieniu to chyba byłby niepotrzebny overkill z mojej strony i ciężko wyobrazić sobie taki scenariusz w praktyce.
PS
Co do mojego kodu- jakby ktoś testował- to wymagany jest hotfix (metoda getFullAdress powinna otaczać apostrofami nazwę arkusza, żeby nie rzucało wyjątkiem przy arkuszach, których nazwa zawiera spację).
Poza tym rozwiązanie w tej wersji "beta" działa produkcyjnie zadziwiająco dobrze i bezproblemowo: na arkuszach bardzo obciążonych formułami, które nie wymagają częstego odświeżania, cały kod na zdarzenie Activate
Kod: |
With RangeFormula
.ApplyFormulas (...)
Me.Calculate
.ApplyValues (...)
End With |
Ale ulepszę klasy za kilka dni stosując wybrane wskazówki od master_mix i ewentualnie innych użytkowników, którzy podzielą się swoimi spotrzeżeniami, bo kilka fajnych wskazówek już dostałem.
PPS
co do ilości metod, to raczej podejście stosowałbym w myśl upraszczania rzeczy jak się da, ale nie bardziej. Zamiast Do(Byval AorB as something) lepsze jest DoA() i DoB(). Łatwiej taki kod przetestować, jest czytelniejszy i łatwiejszy w stosowaniu. Ale może jestem za bardzo pod wpływem Wujka Boba po przeczytaniu słynnego Czystego Kodu
Po raz któryś brakuje na forum opcji edycji postów po 30 minutach, w jednym poście mógłbym aktualizować wersję rozwiązania. Dla fioletowych można byłoby rozważyć taki przywilej... |
_________________ W erze zaawansowanych narzędzi LLM takich jak GPT 50% problemów użytkowników forum może być efektywnie rozwiązanych przez nich samych za pomocą tych narzędzi. Jednak często bardziej cenią swój czas niż czas tych, którzy oferują tutaj pomoc. |
|
 | ID posta:
432452
|
|
|
 |
|
|
master_mix
Excel Expert


Wersja: Win Office 365
Pomógł: 1293 razy Posty: 2639
|
Wysłany: 02-01-2024, 16:23
|
|
|
Pisałem tego posta nad ranem w nowy rok , i już kończyłem, kiedy wywaliło forum w kosmos ...
Nie byłem pijany i na pewno to nie moja wina :D
Więc piszę od nowa.
Rafał B. napisał/a: | Z wad klas statycznych (i ogólnie klas) widzę tylko jedną i jest to błąd projektowy VBA (może VB6 też?), że brak public const i public enum |
Enumy publiczne można śmiało stosować.
Const, Type (struktury), już nie, ale lepiej utworzyć osobną klasę którą można wykorzystać wyłącznie jako pola.
Rafał B. napisał/a: | ciężko mnie przekonać do zmiany na moduły ze względu na a'la enkapsulację i Intellisense |
intellisense jest też i dla modułu:
Kod: |
With NazwaModulu
.Podpowiadana metoda
End With
|
Jeżeli chcesz koniecznie bardziej zhermetyzować kod, to używasz 2 klas z publicznymi metodami, a niekoniecznie wszystkie powinny być widocznie,
tym bardziej że się powtarzają, a jest lub będzie potrzeba też wykorzystać metodę z klasy 2 w klasie 1 ale tylko na potrzeby klasy 1 i tym bardziej nie powinna być publiczna.
Wiem że nigdy nie kończy się na jednej klasie, tym bardziej że refaktoryzacja jest ważna.
Jest to nieduży projekt, więc metody publiczne moim zdaniem powinny być tylko w jednej klasie, chociażby po to żeby podczas korzystania z Twojego projektu nie myśleć jaka metoda w klasie 1 i co robi, a jaka w klasie 2, dlaczego "Delete" jest tu i tu ?.
Wiem że VBA dla OOP to mordęga, ale coś się tam da wymodzić.
Można dla tego typu scenariusza wykorzystać efekt "polimorfizmu przez interfejsy".
Robię to w prostych 3 krokach:
1. tworzymy klasę interfejsu InterfaceClass, gdzie deklarujemy tylko puste metody które będą później publiczne.
(możemy tworzyć dowolną ilość interfejsów i implementować wszystkie nawet w tej samej klasie)
Kod: |
Public Sub IMetodaSub()
End Sub
|
2. Teraz tworzę klasę "prywatną" PrivateClass w sensie: będą w niej tylko prywatne metody, ale będziemy mogli jej metody prywatne użyć w klasie publicznej i też ewentualnie z tej klasy publicznej udostępnić.
Po wpisaniu Implements InterfaceClass na początku klasy, pierwsza metoda z klasy interfejsu zostanie automatycznie dodana,
resztę należy dodać standardowo z comboboxów na na górnym pasku edytora VBA, podobnie jak przy Eventach.
Automatycznie też do nazwy metody z interfejsu doda się nazwa klasy z której implementujemy (nazwa interfejsu).
WAŻNE - wszystkie metody z interfejsu muszą zostać zaimplementowane, inaczej kompilator zgłosi błąd. Uzupełnianie ciała metod nie jest wymagane we wszystkich, ale jeżeli taka sytuacja nastąpi to znaczy że trzeba refaktoryzować interfejs, czyli rozbić go na mniejsze elementy, bo żle był przemyślany.
Teraz wystarczy dopisać ciała metod.
Kod: |
Implements InterfaceClass
Private Sub InterfaceClass_IMetodaSub()
Debug.Print "Prywatna metoda (Sub)"
End Sub
|
3. Teraz dodajemy klasę publiczną, która udostępni metody z klasy prywatnej na zewnątrz, lub wykorzysta je tylko dla siebie.
Ja po prostu kopiuje potrzebne metody z klasy interfejsu (usuwam przedrostek "I" - dla lepszej konwencji nazewnictwa) i uzupełniam ciało "polimorficzne"
czyli do interfejsu przypisuję obiekt PrivateClass -> rzutowanie.
PublicClass
Kod: |
Private clsPriv As PrivateClass
Private InerFac As InterfaceClass
Public Sub MetodaSub()
'polimorficzne wykorzystanie interfejsu
'---------------------------------
InerFac.IMetodaSub
End Sub
Private Sub Class_Initialize()
'polimorficzne przypisanie-rzutowanie na interfejs
'pamiętać należy że rzutowane są tylko te metody z klasy PrivateClass
'które udostępnia interfejs
'---------------------------------
Set clsPriv = New PrivateClass
Set InerFac = clsPriv
End Sub
Private Sub Class_Terminate()
Set clsPriv = Nothing
End Sub
|
Pozostaje nam sprawdzić jak to działa:
Kod: |
Sub Test()
Dim clsPublic As New PublicClass
clsPublic.MetodaSub
''sprawdzam czy na pewno ukryta jest klasa prywatna:
''kompilator zwróci błąd bo nic z tej klasy nie jest dostępne
'Dim clsPriv As New PrivateClass
'clsPriv.IMetodaSub
End Sub
|
O kilka metod bardziej rozbudowany kod jest w załączniku. Prosty przykład żeby nie komplikować a przekazać koncepcję
Rafał B. napisał/a: | MSXML stosowałem tylko do parsowania węzła, nie wiedziałem że można inaczej pomijając manipulacje stringami. |
Uważam że ładowanie do klas MSXML plików xml i zapisywanie, opóźni kod. A skoro wszystko jest dostępne przez klasy Office.CustomPart i inne Office.Custom
i bezpośrednio manipuluje zawartością pliku, to lepiej iść w tą stronę.
Poruszanie się po xml-u zawsze będzie też wydajniejsze przez zapytania-język XPath (XML Path Language).
Co do tego co pisałem 1:1, chodziło mi o to żeby za często nie tworzyć nowego obiektu CustomXMLPart - co za tym idzie nowego pliku w zipie .xlsm
Tylko Tworzyć jeden plik na 1 arkusz, stąd namespace=CodeName, żeby później łatwo odnaleźć plik (CustomXMLPart) i do niego coś dopisać, jeżeli dane dotyczą tego samego arkusza.
Interfejs niewidocznych metod.xlsm
|
Pobierz Plik ściągnięto 66 raz(y) 26.94 KB |
|
_________________
Podejmę współpracę (pracę)
Programowanie C#, Android, iOS, VB.NET, VBA, ASP.NET Core, WPF, Xamarin, Power Platforms, XAML, MVC, LINQ, Entity Framework. Bazy danych SQL Server, Oracle, MySQL, Firebird
Wrocław i okolice …lub zdalnie. |
|
 | ID posta:
432468
|
|
|
 |
|
|
Rafał B.
ExcelSpec


Wersja: Win Office 2021
Pomógł: 91 razy Posty: 524
|
Wysłany: 02-01-2024, 22:11
|
|
|
Z enumami to był skrót myślowy- można je stosować, ale jest powszechnie znany związany z nimi bug, który w najmniej spodziewanym momencie pojawi się u klienta, bo pojawia się losowo, czyli niesławne `Compile error: Constant expression required`, dlatego nie stosuję ich w klasach, bo lubię spać spokojnie.
Cytat: | intellisense jest też i dla modułu |
to oczywiste, że jest Intellisense, ale chodziło nie o brak Intellisense, tylko za dużo śmieciowych podpowiedzi w Intellisense, pisałem o tym wielokrotnie. Mam projekt na grube kilkadziesiąt tysięcy linii kodu, wiele klas, modułów. Każda nazwa publicznego makra/skryptu zaśmieca mi Intellisense. Wielu to nie przeszkadza, ale mnie pisze się szybciej, gdy mam mniej podpowiedzi do wyboru. Ale poza tym konstrukcja With jest ograniczona do obiektów (i chyba tylko typów), więc do modułu nie da rady bez kombinacji. A lubię ją stosować With, bo dobrze(!) wykorzystana zwiększa przejrzystość i szybkość pisania kodu (nawet czasem szybkośc działania, bo kod trzyma referencję do onbiektu, jak to często robimy z .Range) i brakuje mi jej w innych językach.
Cytat: | Uważam że ładowanie do klas MSXML plików xml i zapisywanie, opóźni kod. |
Przecież pisałem, że cenna wskazówka i nie wiedziałem o możliwości uniknięcia tego, więc nie trzeba mnie przekonywać do tej zmiany!
Natomiast co do przykładu z interfejsami: nie widzę żadnych zalet tworzenia dwóch klas PrivateClass i PublicClass, według mnie lepiej to zrobić w jednej klasie, czyli implementować wszystko w ramach PublicClass odnosząc się do nomenklatury Kolegi. Wszystkto zapewnia deklarowany scope-zasięg widoczności Private/Public metod i akcesorów.
W ogóle w OOP każde powoływanie instancji (w przypadku VBA "new JakaśKlasa") wewnątrz klasy jest podejrzane z gruntu zasad SOLID, bo powinno być wstrzykiwanie zależności. A w tym przykładzie robimy zależność PublicClass korzysta z PrivateClass.
EDYCJA
chyba po namyśle zrozumiałem ideę: ta PublicClass miała być jedna sądząc po eksponowaniu jej metod, a PrivateClass może być potencjalnie wiele (jako poszczególne implementacje) i po zmianie implementacji na np. PrivateClassV2 zmieniamy tylko konstruktor (i typ pola) klasy PublicClass. Czyli de facto jest to ominięcie wady jezyka, że interfejs nie może implementować innych interfejsów i PublicClass pełni tę rolę "superinterfejsu".
Jeśli tak, to ja wolę jednak utworzenie interfejsu odpowiadającego od razu wymaganiom (czyli dodatkowego łączącego metody InterfaceClass1 i InterfaceClass2). Dodatkowa praca, ale później same benefity. Bo wówczas mamy np. ITaxCalculator i tylko podstawiamy implementacje w jakimś makrze w Module1. Czyli:
Kod: | Dim obj As ITaxCalculator
Set obj = New DefaultTaxCalculator
'Set obj = New NowyLadTaxCalculator ' zmiana kryteriów obliczania podatku
'Set obj = New JeszczeNowszyLadTaxCalculator ' zmiana kryteriów obliczania podatku
tax = obj.GetTax(params)
| Wówczas moim zdaniem bardziej widoczny jest urok interfejsów, bo w ogóle nie grzebiemy w istniejącym kodzie poza zmianą "new XXXX", jedynie dodajemy implementację i zmieniamy jedną linijkę w głównym makrze. Każdy, kto utrzymywał dużą aplikację, szczególnie w VBA, wie jakim koszmarem jest zmiana wymagań biznesowych klienta. Tydzień temu umawialiśmy się na X jako niezmiennik, a dziś dzwoni i zmieniamy, co przy źle napisanej apce ciągnie lawinę zmian i potencjalnych problemów. Dobre wykorzystanie interfejsów pomaga zmniejszyć ten ból to minimum. A każda zmiana istniejącego kodu, to potencjalnie problem.
Ale co do implementacji, to już kwestie preferencji, nie zbaczajmy z wątku za bardzo :) |
_________________ W erze zaawansowanych narzędzi LLM takich jak GPT 50% problemów użytkowników forum może być efektywnie rozwiązanych przez nich samych za pomocą tych narzędzi. Jednak często bardziej cenią swój czas niż czas tych, którzy oferują tutaj pomoc. |
|
 | ID posta:
432472
|
|
|
 |
|
|
|
Nie możesz pisać nowych tematów Nie możesz odpowiadać w tematach Nie możesz zmieniać swoich postów Nie możesz usuwać swoich postów Nie możesz głosować w ankietach Nie możesz załączać plików na tym forum Możesz ściągać załączniki na tym forum
|
Dodaj temat do Ulubionych Wersja do druku
|
|
Strona uĹźywa plikĂłw cookies.
Kliknij tutaj, Ĺźeby dowiedzieÄ siÄ jaki jest cel uĹźywania cookies oraz jak zmieniÄ ustawienia cookie w przeglďż˝darce.
KorzystajÄ
c ze strony uĹźytkownik wyraĹźa zgodÄ na uĹźywanie plikĂłw cookies, zgodnie z bieĹźÄ
cymi ustawieniami przeglÄ
darki.
SprawdĹş, w jaki sposĂłb przetwarzamy dane osobowe
|