Az „Excel” fórum célja, hogy keretet adjon az Excel felhasználók széles táborának tapasztalataik megosztására, és lehetőséget a segítséget kérőknek. Az alábbi összefoglaló azért készült, hogy segítse a helyes kérdésfeltevést.
– Írd le szabatosan a problémát. Úgy fogalmazz, hogy ne csak te magad, de a szakértő is megértse, mire szeretnél választ kapni.
– Írd le, hogy milyen verziójú Excellel dolgozol. (Vagy ha nem – ill. nem csak – Excel, akkor micsoda?)
– Írd le, hogy milyen úton indultál el, és hol akadtál el rajta.
– A kérdés megértése szempontjából sokat segíthet, ha feltölteszt egy képet, amin látszik, hogy mit szeretnél, vagy illusztrálja azt.
– Még jobb, ha feltöltesz egy minta munkafüzetet valahová (pl. data.hu). Feltöltés előtt távolítsd el belőle a nem publikus adatokat.
– Ha a feladat jellege olyan, célszerű az "előtte" és "utána" állapotokat bemutatni. (Miből kellene csinálni mit?)
– Ha VBA kódon kell javítani, másold be a releváns kódrészt. Rövid kód mehet hozzászólásba, hosszú kód inkább ide: http://pastebin.com/
– Ha valami nem úgy működik, ahogy kellene, add meg a rendellenes viselkedés jellemzőit, a hibaüzenetet, és a hibát okozó programsort.
Esetleg ezt is megpróbálhatod. Úgy mértem, durván 5x gyorsabb, mint a ciklusos megoldás.
Kb. 55000 sornál 51 sec volt a futási idő. (Lehet, hogy a kézi szűrés-másolás ennél is gyorsabb :))
Sub töröl() Dim Rng As Range, WS As Worksheet Application.Calculation = xlCalculationManual Set WS = Sheets("Country Orders Data") Set Rng = WS.Range("B2", WS.Range("B" & Rows.Count).End(xlUp)) WS.Range("B:B").Insert With Rng.Offset(, -1) .FormulaR1C1 = "=IF(RC[1]<>""Denmark"",1,"""")" .Copy .PasteSpecial xlPasteValues .SpecialCells(xlCellTypeConstants, xlNumbers).EntireRow.Delete End With WS.Range("B:B").Delete Application.Calculation = xlCalculationAutomatic End Sub
Sheets("Country Orders Data").Activate Last = Cells(Rows.Count, "B").End(xlUp).Row For i = Last To 2 Step -1 If (Cells(i, "B").Value) <> "Denmark" Then Cells(i, "A").EntireRow.Delete End If Next i
Mivel azonban a sheet, amin töröl kb 35ezer sor van, ez eléggé kikészíti a programot.
Mi lehetne a gyorsabb megoldás? Rászűrés Denmarkra és kicopizás?
Ezt a 4 elemű tömböt én is nézegettem, de nem tudtam kiokoskodni, milyen adatokat is tárol. Végül megtaláltam a megoldást:
Sub szamol() Dim Param1 As Integer, Param2 As String Dim MenuCommand As CommandBarControl Set MenuCommand = CommandBars.ActionControl If Not TypeName(MenuCommand) = "CommandBarButton" Then Exit Sub Param1 = MenuCommand.Tag Param2 = MenuCommand.DescriptionText ...
A menü több szintes, minden szinten CommandBarControl objektumok szerepelnek. Azokat a menüpontokat, amelyek valamilyen feladatot hajtanak végre, vagyis amelyek meghívják a szamol() makrót, azokat msoControlButton típussal hoztam létre:
AlMenu(k).OnAction = "szamol"
AlMenu(k).DescriptionText = "..." 'Param2
AlMenu(k).Tag = "..." 'Param1
Azok a menüpontok, amelyekből almenü nyílik, azok msoControlPopup típusúak, és nincs .OnAction tulajdonságuk (pontosabban, ezt a tulajdonságát nem irányítottam a szamol() makróra).
Mikor a menüből meghívódik a 'szamol' makró, akkor a CommandBars.ActionControl tartalmazza azt a referenciát, amire szükségem van. Felvettem a 'szamol' makróban egy CommandBarControl típusú objektumot, ennek adtam át a CommandBars.ActionControl referenciát, és innentől kiolvashattam a korábban minden menüpontra egyedileg jellemző .DescriptionText és .Tag tulajdonságokat. Így már át tudok adni paramétereket a meghívott makrónak. Sőt, akár három paraméter is átadható, mert a .Parameter tulajdonság is használható.
Hát én úgy nézem, hogy a variant egy négyelemű tömb, amelynek elemei a hívó menuitem pozícióját mutatják.
Sub teszt() Dim v As Variant v = Application.Caller End Sub
Ha a fenti makrót saját menügombbal indítod, akkor a v(1 To 4) vektor elemei a következők lesznek:
v(2) : hányadik menü a felső menüsorban
v(1) : az adott menün belül hányadik menüpont
v(3) : ??? (nem tudom mi ez)
v(4) : kísérleteim szerint, ha legalább 3. szinten van az aktivált menüpont, akkor v(4) értéke 1, egyéb esetben nulla. (Vagyis, ha az aktivált menüpont a főmenü->almenü->menüpont útvonalon érhető el, mint pl. az Eszközök->Védele->Lapvédelem, vagy még ennél is lejjebb van a hierarchiában.)
Maximum 2 szintes menürendszer esetén működik az alábbi tesztprogram. A 3. szintre már nem tudtam kiterjeszteni, mert fogalmam sincs, miből derül ki az, hogy az aktív menüpont a hierarchia hányadik szntjén tartózkodik.
Sub szamol() Dim v As Variant With Application v = .Caller MsgBox .ActiveMenuBar.Menus(v(2)).Caption & " -> " & .ActiveMenuBar.Menus(v(2)).MenuItems(v(1)).Caption End With End Sub
Lehet, hogy van egyszerűbb módja is, pl. direkt hivatkozás az aktív menüpontra valahol az Application objektum ezernyi tulajdonsága között. Nem tudom.
Ezt a módszert hogyan lehetne alkamazni menürendszernél?
Létrehoztam egy több szintű menüt, minden menüpont CommandBarControl típusú.
Const maxMenuCommand As Integer = 40
Dim FoMenu As CommandBarControl
Dim AlMenu(maxMenuCommand) As CommandBarControl
Set FoMenu = CommandBars(1).Controls.Add(msoControlPopup)
Azokat az almenüpontokat, amelyeknek további almenüi vannak, így hoztam létre:
Set AlMenu(k) = FoMenu.Controls.Add(msoControlPopup)
Míg azokat a menüpontokat, amelyeknek már nincs almenüje, így:
Set AlMenu(k) = FoMenu.Controls.Add(msoControlButton)
Minden almenü .OnAction tulajdonságát ugyanarra a makróra állítottam:
AlMenu(k).OnAction = "szamol"
és a későbbi azonosítás kedvéért még néhány menüpontra jellemző adatot is beállítottam:
AlMenu(k).DescriptionText = "..."
AlMenu(k).Tag = "..."
Már csak az van hátra, hogy mikor meghívódik a 'szamol' makró, akkor valahogyan azonosítania kellene, hogy melyik menüpont indította el. Ha tudnám, melyik menüpontról lett meghívva, onnantól tudnám a 'szamol' makró paramétereit is, mert minden menüpont .DescriptionText és .Tag tulajdonságait kitöltöttem. Viszont az Application.Caller ebben az esetben egy Variant() típust ad vissza. Hogyan lesz ebből CommandBarControl objektumra való hivatkozás?
Nem biztos, hogy jól értem a kérdést, de ha igen, akkor a következő megoldást javaslom.
Ne az OnAction értékbe adjad be a paramétert, hanem magát a makrót írd meg úgy, hogy különböző dolgokat csináljon attól függően, hogy melyik képről indult.
Tehát pl. minden kép OnAction értéke a meret nevű makró, ami pedig így épül fel:
Sub meret() If TypeName(Application.Caller) = "String" Then Select Case Application.Caller Case "Kép 1" MsgBox "A 'Kép 1' objektumra kattintottál." Case "Kép 2" MsgBox "A 'Kép 2' objektumra kattintottál." Case "Szövegdoboz 3" MsgBox "A 'Szövegdoboz 3' objektumra kattintottál." End Select End If End Sub
Itt tehát az Application.Caller tartalmazza annak az objektumnak a nevét, amelyről a kórvégrehajtás indult.
Az ID azonosítók mentés, kilépés, újra megnyitás után változnak (de természetesen továbbra is egyediek maradnak), úgyhogy másként kell azonosítani az eseményt kiváltó objektumot. Így aztán most már két kérdésben kérek tőletek segítséget!
1. Hogyan lehet (pl. Shape objektum) OnAction eseménykezelőjébe olyan makróhivatkozást megadni, ami paramétereket is át tud adni a meghívott makrónak?
2. Hogyan lehet azonosítani azt az elemet, ami kiváltott az OnAction hatására a makróhívást?
Illetve talán rosszul közelítek a kérdéshez. Hogyan oldható meg, hogy egy képre kattintva elinduljon egy makró, ami ezt a képet (csak ezt, a többit nem) módosítja?
Egy dologban szeretném a segítségeteket kérni! Makróval képeket szúrok be egy munkalapra, és azt szeretném, hogy a képre kattintva elinduljon egy makró.
Addig rendben, hogy a kép 'Shape' típusú objektum, és az 'OnAction' tulajdonságát beállítom a meghívandó makró nevére, de hogyan adok át paramétert a makrónak? Paraméterek nélkül valóban meghívódik a makró a képre kattintva, de át szeretném adni paraméterként, hogy melyik képre kattintva hívódott meg a makró! Arra gondolok, hogy a meghívandó makró várna egy Long típusú értéket, ami az egérkattintás eseményt kiváltó Shape objektum ID azonosítója lenne. Így próbáltam:
Dim abra as Shape
Set abra = Munlalap.Shapes.AddPicture(FileNev, msoTrue, msoTrue, Xpos, Ypos, pWidth, pHeight)
abra.OnAction = "meret(" & abra.ID & ")"
Az 'abra' objektum 'OnAction' tulajdonsága valóban felveszi pl. a "meret(12)" stringet, de rákattintva az ábrára hibaüzenetet kapok: "A(z) meret(12) makró nem futtatható. Lehetséges, hogy a makró nem érhető el a munkafüzetben, vagy le van tiltva az összes makró." Viszont a 'meret' makrót Excelből elindítva rendben lefut. Kipróbáltam:
Az Outlook a csatolmányokat belementi az email-be.
Amikor a mellékletet megnyitos, bemásolódik egy temporary mappába, és a továbbiakban az összes mentés, automentés itt rögzül. Aztán amikor be akarod zárni az emailt, az Outlook megkérdezi, hogy mentse-e a változásokat. És ha azt mondod, hogy igen, akkor a temp mappából az email obektumba beágyazódik a csatolt fájl utolsó verziója, és az egész együtt elmentődik vagy az exchange szerverre, vagy a lokális pst fájlba. Mivel elvileg ez (tehát az email + a beágyazott csatolt fájl) a végtermék, és minden egyéb csak munkaverzió, az ideiglenes fájlokra nincs szükség, tehát fölösleges őket az asztalra, vagy egyéb frekventált helyre másolni/menteni. Legalábbis valami ilyesmi lehet az ideológia mögötte.
Mindenesetre akár tetszik, akár nem, ehhez kell alkalmazkodni, ha Outlookot használ az ember. Fontos fájlok esetében érdemes rászokni arra, hogy mindenekelőtt lemented a mellékletet egy biztos helyre, és csak utána kezdesz dolgozni vele.
az 57 abból adódik, hogy a példában 18 ország volt, tehát az első 18 pontot kap, a második 17 stb. Ezt úgy számolom ki, hogy 18+1-helyezési sorszám. Mivel 3 csoportban értékelte őket:
n+1-ssz1+n+1-ssz2+n+1-ssz3=3*(n+1)-ssz1-ssz2-ssz3
a ssz-ot pedig a MATCH/HOL.VAN adja meg
Természetesen 57 helyett jobb megszámolni az elemszámot fv-nyel (3*(count(a:a)+1))
Sajnos egyik módszerrel sem sikerült elővarázsolni a file-t.
Emlékeztem, hogy valami T-vel kezdődő krix-krax-nevű könyvtárba "mentettem", de azt nem találtam meg (pedig volt vagy 40 könyvtár mindenféle kezdőbetűvel, és mindet végignéztem).
Viszont megtaláltam egy korábbi file-t, amivel szintén így jártam anno, úgyhogy segítségetek nem volt hiábavaló :)
Mégegyszer köszönöm!
(Pusztán csak elgondolkodás végett, vajon mire jók ezek a krixkrax nevű, elérhetetlen könyvtárak? Miért nem menti az Outlook valami normális helyre a file-okat alapból?... )
Amúgy azért használom gyakran, mert (nem muszáj kritizálni...) megterveztem a saját világom, országom, gazdasággal, domborzattal, valutákkal, stb. (köröbelül 2.-ikos kormoban kezdtem) és eleinte kézzel csináltam a dolgokat, de a micorosoft office sokszor nagyban megkönnyíti a dolgomat. A Publisherrel a 2havonta megjelenő újságokat gyártom (hogy később emlékezzek, hogy mi volt az országomban pl. 2010 Februárban, stb. Az excellel gyakran készítek hasonló táblázatokat, pl. az excelben vezetem a napi tőzsde-index grafikonomat is. A térképeket többnyire megcsinálom kézzel, de ma már (szerintem) elengedhetetlen a gépi formátum is, amit először paintal csinálok, majd a Publisherrel az átmásolt térképre ráírogatom az adatokat, neveket... Ezért van szükségem főleg erre a két office alkalmazásra :)