V této části vytvoříme první reálný doplněk pro MicroStation, který je praktický a užitečný. Slouží k odebrání čar z výběrové množiny, a to podle délky, kterou zadá uživatel. Je to užitečné například při použití takového druhu čar, který se u úseček s malou délkou zobrazí neúplně a výsledek je pak velmi nepřehledný.
Používání doplňku je popsané v tomto článku. My ho tady vytvoříme znovu, krok za krokem.
Založíme nový VBA projekt, pojmenovaný např. ReLiByLe (remove lines by length). Výchozí Module1 přejmenujeme na Main – bude hlavním modulem doplňku.
Pokud na prvním řádku modulu není příkaz Option Explicit, velmi doporučuji ho doplnit, případně nastavit jeho automatické vkládání, viz předchozí část.
Budeme pracovat s výběrovou množinou prvků, tj. s prvky, které uživatel vybere pomocí nástroje Vybrat prvek. Jak se k ní dostaneme ve VBA?
Aktivní model
V MicroStationu pracujeme s výkresy – soubory dgn. Každý výkres může obsahovat více modelů, minimálně pak jeden, který je výchozí (Default). V jednom okamžiku je vždy aktivní jenom jeden model a všechny prvky, které vytváříme nebo upravujeme, se ukládají do tohoto aktivního modelu.
Ve VBA je aktivní model reprezentován objektem ActiveModelReference, členem objektu Application, hlavního objektu knihovny MicroStationDGN.
Takže zpět k výběrové množině: Ve VBA k ní získáme přístup pomocí metody GetSelectedElements objektu ActiveModelReference.
Element je základní objekt reprezentující každý grafický prvek (úsečka, útvar, kružnice, text apod.).
Metoda GetSelectedElements vrací objekt ElementEnumerator.
S objekty jsme se seznámili v první části jako s „balíčky“ s různými vlastnostmi a metodami. A jak nyní vidíme, mohou být použité jako datový typ.
Deklarace objektové proměnné je stejná jako deklarace proměnných ostatních typů. Uvádí se v ní konkrétní typ objektu, se kterým chceme pracovat:
Dim elEm as ElementEnumerator
Pozn. 1: Konvence pojmenovávání proměnných
Přiřazení objektu k proměnné se provádí stejně jako u ostatních typů pomocí operátoru =, na rozdíl od nich je ale nutné použít klíčové slovo Set:
Set elEm = Application.ActiveModelReference.GetSelectedElements
Objekt Application je možné vynechat, je totiž automaticky předpokládán.
Element Enumerator
Objekt ElementEnumerator slouží pro přístup k prvkům nějaké množiny, v tomto případu množiny vybraných prvků. Jak napovídá název, umožňuje jejich výčet – postupné procházení, enumeraci.
Zpočátku, po přiřazení, se pozice enumerátoru nachází před prvním prvkem. Pomocí metody MoveNext se pokusí přesunout na první prvek. Pokud prvek existuje, vrátí tato metoda hodnotu True a přiřadí prvek do vlastnosti Current, Pokud prvek neexistuje, vrátí False a Current neukazuje na žádný prvek.
Pozn. 2: Datový typ Boolean
Řekněme, že výběrová množina obsahuje dva prvky. ElementEnumerator pak funguje takto:
Sub GetTypes()
Dim elEm As ElementEnumerator
Set elEm = ActiveModelReference.GetSelectedElements
If elEm.MoveNext Then Debug.Print elEm.Current.Type
If elEm.MoveNext Then Debug.Print elEm.Current.Type
If elEm.MoveNext Then Debug.Print elEm.Current.Type
End Sub
'Výsledek:
'3
'3
'
Získali jsme ElementEnumerator a přiřadili ho do proměnné elEm.
Použijeme If Then: když MoveNext najde prvek (vrací True), tak k němu máme přístup přes Current a vytiskneme jeho typ (pokud je to úsečka, uvidíme v ladícím okně číslo 3, viz dále).
Vybrali jsme dva prvky, takže přejdeme na další. Vytiskne se další číslo 3.
Zkusíme přejít ještě dál. Nic se nestane, protože třetí prvek neexistuje, MoveNext vrací False a podmínka If není splněna.
Tento příklad je pouze ukázkový, v praxi takto enumerator nikdy nepoužíváme. Nemůžeme totiž vědět, kolik prvků se v procházené množině nachází.
Potřebujeme opakovat příkaz (nebo blok příkazů), aniž bychom předem věděli, kolikrát.
Konstrukce Do While
Tato konstrukce slouží přesně k tomuto účelu. Umožňuje provádění příkazů v cyklu (smyčce) tak dlouho, dokud je splněna zadaná podmínka:
Do While podmínka
'příkazy
Loop
Klíčová slovaDo While a Loop tvoří začátek a konec cyklu. Když běh programu dojde k tomuto cyklu, vyhodnotí podmínku. Pokud není podmínka pravdivá, přeskočí cyklus a pokračuje za ním. Pokud je podmínka pravdivá, provede blok příkazů uvnitř cyklu a vrátí se na jeho začátek. To opakuje tak dlouho, dokud je podmínka pravdivá.
V našem případě bude podmínkou volání metody elEm.MoveNext. Pokud tato metoda vrátí hodnotu True (tzn. existuje další prvek), bude podmínka splněna a provede se příkaz uvnitř cyklu:
Do While elEm.MoveNext Debug.Print elEm.Current.TypeLoop
Můžeme vybrat libovolný počet prvků. Tento kód vždy spolehlivě vypíše jejich typ.
Objekt ElementEnumerator nemá žádnou vlastnost ani metodu, která by nám předem řekla, kolik prvků je v množině. Pokud bychom tuto informaci potřebovali, musíme ji sami spočítat:
Sub GetCountAndTypes()Dim elEm As ElementEnumeratorDim count As Long Set elEm = ActiveModelReference.GetSelectedElements Do While elEm.MoveNext count = count + 1 Loop Debug.Print "Počet vybraných prvků: " & count elEm.Reset Do While elEm.MoveNext Debug.Print elEm.Current.Type Loop End Sub
Deklarujeme proměnnou count datového typu Long, což je celé číslo. Jeho výchozí hodnota je nula.
Pozn. 3: Datový typ Long
Datový typ Long slouží k ukládání celých čísel v rozsahu -2 147 483 648 až 2 147 483 647. Vyžaduje 4 bajty paměti počítače.
Pokud jsme si jisti, že nebudeme pracovat s velkými čísly, můžeme použít i datový typ Integer. Má rozsah -32 768 až 32 767 a velikost 2 bajty.
Naopak pro čísla překračující rozsah typu Long slouží typ Decimal. V případě takové potřeby najdete podrobnosti v nápovědě VBA.
Použijeme enumerator a v každém kroku cyklu zvýšíme hodnotu proměnné Count.
Vypíšeme hodnotu count do ladícího okna. Všimněte si, že pro její sloučení s informativním textem jsme použily operátor &. Je to proto, že spojujeme řetězec typu String s proměnnou jiného typu. V tom případě nemůžeme použít operátor +.
Pozn. 4: Konverzní funkce
VBA obsahuje sadu funkcí pro převod mezi datovými typy. V tomto případě bychom mohli použít funkci Cstr, která převede číslo na text obsahující toto číslo. Operátor + pak opět funguje:
Debug.Print "Počet vybraných prvků: " + CStr(count)
Enumerator se nyní nachází za posledním prvkem množiny. Použijeme jeho metodu Reset, která ho vrátí před první prvek.
V dalším cyklu vypíšeme typy vybraných prvků.
Teď už víme, jak získat vybrané prvky a jak je procházet. V dalším kroku si ukážeme, jak zjistit, zda je vybraný prvek úsečka a pokud ano, jaká je její délka.
Objekt Element
Jak už víme, každý prvek ve výkresu (správněji v aktivním modelu) je ve VBA reprezentován objektem Element. Tento objekt obsahuje vlastnosti a metody, které jsou společné pro všechny typy prvků.
Vedle základního objektu Element existují objekty prvků různých typů: úsečka a lomená úsečka (SmartLine) jsou objektem LineElement, kružnice a elipsa jsou objektem EllipseElement apod. Tyto objekty mají vedle vlastností a metod objektu Element své vlastní specifické vlastnosti a metody. LineElement má vlastnost Length (délka), elipsa vlastnost PrimaryRadius a SecondaryRadius (primární a sekundární rádius) apod.
Zkusme vypsat délku vybraných úseček:
Sub GetLength1()Dim elEm As ElementEnumeratorDim liEl As LineElement Set elEm = ActiveModelReference.GetSelectedElements Do While elEm.MoveNext Set liEl = elEm.Current Debug.Print liEl.Length Loop End Sub
Vlastnost enumeratoru Current je typu Element, proto i v případě, kdy ukazuje na objekt LineElement, nemáme přístup k jeho vlastnosti Length. Musíme ho explicitně přiřadit do proměnné stejného typu. Deklarujeme tedy proměnnou liEl typu LineElement, v enumeračním cyklu do ní přiřazujeme aktuální objekt, na který odkazuje vlastnost enumeratoru Current a vypisujeme jeho vlastnost Length.
Funguje to, ale jen tehdy, když ve výkresu vybereme úsečky a lomové úsečky. Když je vybraná např. kružnice, dojde k chybě Type mismatch (neshoda typů):

Stiskem tlačítka Debug se přepneme do ladícího režimu a uvidíme řádek, kde došlo k chybě:

Přiřadit do objektové proměnné nějakého typu objekt jiného typu není možné.
Element Type
Víme, že každý objekt Element má vlastnost Type, svůj typ. Každý typ je určen předdefinovanou konstantou, např. pro úsečku je to konstanta msdElementTypeLine (hodnota 3), pro lomenou úsečku je to msdElementTypeLineString (hodnota 4) a pro kružnici msdElementTypeEllipse (hodnota 15).
Můžeme použít osvědčenou konstrkukci If Then: když enumerator ukazuje na objekt typu msdElementTypeLine, tak je to úsečka a vypíšeme její délku:
Sub GetLength2()Dim elEm As ElementEnumeratorDim liEl As LineElement Set elEm = ActiveModelReference.GetSelectedElements Do While elEm.MoveNext If elEm.Current.Type = msdElementTypeLine Then Set liEl = elEm.Current Debug.Print liEl.Length End If Loop End Sub
Tento postup funguje bez chyb, objekt Element ale nabízí i jiný, trochu jednodušší. Obsahuje vlastnost IsLineElement s návratovou hodnotou typu Boolean, kterou můžeme použít v podmínce místo porovnávání. Takovou Is[typ prvku] vlastnost má pro všechny typy prvků.
Podobné zjednodušení existuje i pro přetypování objektu Element na LineElement. Pokud už s objektem dále v kódu nepracujeme a nepotřebujeme ho mít uložený v proměnné, můžeme použít vlastnost AsLineElement, která vrací LineElement a bezprostředně nabízí všechny jeho členy, včetně vlastnosti Length.
Takový kód je rychlejší na zápis, ale i při běhu programu:
Sub GetLength3()Dim elEm As ElementEnumerator Set elEm = ActiveModelReference.GetSelectedElements Do While elEm.MoveNext If elEm.Current.IsLineElement Then Debug.Print elEm.Current.AsLineElement.Length End If Loop End Sub
Uživatelský vstup a ošetření chyb
Můžeme přistoupit k dalšímu kroku: dotázat se uživatele na délku rozhodnou pro odebrání úsečky. Použijeme vestavěnou funkci InputBox:
Dim length As Doublelength = InputBox("Zadejte délku:", TITLE)
Tuto funkci známe z minulé části. Víme, že vrací textový řetězec. Tady ji ale používáme pro přiřazení do proměnné typu Double. Pokud uživatel zadá celé nebo reálné číslo a stiskne tlačítko OK, je vše v pořádku. VBA provede automatický převod textu na číslo. Pokud stiskne tlačítko Cancel, dojde k chybě type mismatch. Funkce vrací prázdný řetězec a ten nelze automaticky převést na číslo.
Tuto situaci vyřešíme správným ošetřením chyby pomocí příkazu On Error GoTo. Příkaz GoTo převede běh programu na řádek, který je označený názvem (label). Může to být libovolný text, který začíná písmenem a končí dvojtečkou. Velikost písma je libovolná. Musí být na začátku řádku, o to se postará editor.
GoTo label
'kód zde bude přeskočen
label:
'tady bude pokračovat běh programu
Pozn. 5: Špagetový kód
Příkaz On Error GoTo se pokusí provést příkaz na dalším řádku. Pokud dojde k chybě, přeskočí na označený řádek:
Sub RemoveLinyByLength()Dim length As Double On Error GoTo errorHandler length = InputBox("Zadejte délku:", TITLE) 'TODO: implementace hlavního bloku příkazůerrorHandler:End Sub
Pokud uživatel zadá špatný vstup nebo stiskne tlačítko Cancel, běh programu přeskočí za řádek označený jako errorHandler. Tam je konec procedury, takže běh programu se ukončí.
Zbývá implementovat hlavní blok příkazů:
Sub RemoveLinesByLength()Dim elEm As ElementEnumeratorDim length As Double On Error GoTo errorHandler length = InputBox("Zadejte délku:", TITLE) Set elEm = ActiveModelReference.GetSelectedElements Do While elEm.MoveNext If elEm.Current.IsLineElement Then If elEm.Current.AsLineElement.length <= length Then ActiveModelReference.UnselectElement elEm.Current End If Else ActiveModelReference.UnselectElement elEm.Current End If Loop errorHandler:End Sub
Pomocí enumeratoru procházíme vybrané prvky. Pokud je typu ElementLine, použijeme další blok If Then a pokud je jeho délka menší nebo rovna (<=), odebereme ji z výběru metodou UnselectElement objektu ActiveModelReference. Totéž učiníme, pokud vybraný prvek není LineElement.
Doplníme poslední kousek kódu, ve kterém ověříme, zda je vybrán alespoň jeden prvek:
Sub RemoveLinesByLength()
Dim elEm As ElementEnumerator
Dim length As Double
If Not ActiveModelReference.AnyElementsSelected Then
MsgBox "Není vybrán žádný prvek.", vbOKOnly + vbExclamation, TITLE
Exit Sub
End If
On Error GoTo errorHandler
length = InputBox("Zadejte délku:", TITLE)
Set elEm = ActiveModelReference.GetSelectedElements
Do While elEm.MoveNext
If elEm.Current.IsLineElement Then
If elEm.Current.AsLineElement.length <= length Then
ActiveModelReference.UnselectElement elEm.Current
End If
Else
ActiveModelReference.UnselectElement elEm.Current
End If
Loop
errorHandler:
End Sub
Použijeme metody AnyElementsSelected objektu ActiveModelReference, který vrací True, je-li vybrán alespoň jeden prvek. Nás zajímá opačná možnost False, použijeme proto logický operátor Not. Pokud podmínka platí, upozorníme uživatele a ukončíme proceduru příkazem Exit Sub.
V dalším pokračování přidáme možnost volby, zda se budou odebírat úsečky kratší nebo delší než zadaná délka. InputBox nahradíme vlastním dialogovým oknem.
Kompletní kód příkladů v této části:
Další část: 4. Odebrat úsečky dle délky – vlastní dialog