Office Forum
www.Office-Loesung.de
Access :: Excel :: Outlook :: PowerPoint :: Word :: Office :: Wieder Online ---> provisorisches Office Forum <-
Keine Angst vor Klassen - 1 - Übersicht
Gehe zu Seite 1, 2  Weiter
zurück: WORD SHORTCUTS weiter: Word ist keine Schreibmaschine Unbeantwortete Beiträge anzeigen
Neues Thema eröffnen   Neue Antwort erstellen     Status: Tutorial Facebook-Likes Diese Seite Freunden empfehlen
Zu Browser-Favoriten hinzufügen
Autor Nachricht
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:05
Rufname: Thomas
Wohnort: Celle

Keine Angst vor Klassen - 1 - Übersicht - Keine Angst vor Klassen - 1 - Übersicht

Nach oben
       Version: Office 2010

Nach einigen Anregungen und Verbesserungsvorschlägen folgt hier nun eine neue Version des Klassen-Tutorials XTables.
Viel Spaß, viel Erfolg und - nur Mut!
Wink


Keine Angst vor Klassen
Programmieren von Klassen, Dictionaries und Enumerationen für Einsteiger


Viele Hobby-Programmierer haben nach wie vor Berührungsängste, wenn es um Klassen geht, obwohl grade Klassen den Programmieraufwand wesentlich verringern. Und schließlich arbeiten wir bei VBA ständig mit Objekten, Eigenschaften und Methoden, so stellt uns z. B. ActiveDocument.Content für das Objekt ActiveDocument über die Eigenschaft Content den Inhalt des Dokumentes zur Verfügung und ActiveDocument.Close bietet mit der Methode Close die Möglichkeit, das Dokument zu schließen. Dies alles ermöglichen Klassen, die den Objekten zugewiesen werden.

Um diese Berührungsängste ein wenig abzubauen, habe ich mich entschlossen, anhand eines konkreten Beispiels, dessen Nutzen dabei keine Rolle spielt! Wink , die Programmierung von und Arbeit mit Klassen und Dictionaries zu demonstrieren. Ich möchte darauf hinweisen, dass es sich hier um eine exemplarische Vorgehensweise handelt. Mir ist bewusst, dass Word 2010 z. B. Table.Title oder Table.Descr eingeführt hat und sich viele hier dargestellter Programm-Codes auch damit realisieren lassen. Jedoch lassen sich dann z. B. die für Barrierefreiheit notwendigen Titel und programmtechnische Namen nicht voneinander trennen…

In diesem Tutorial soll die Möglichkeit geschaffen werden, Tabellen in einem Worddokument mit aussagekräftigen Namen zu versehen, um komfortabel darauf zugreifen zu können. Die Klasse heißt deshalb XTables.

Für Neugierige hier schon mal ein Überblick über die bereitgestellten Eigenschaften und Methoden:

Methoden:

[index =] AddXTable([Key][, [Typ][, [Rows][, [Columns][, [Range][, [InsideLineStyle][, [OutsideLineStyle]])
• Fügt eine neue Tabelle ins Dokument und mit dem Schlüsselwert in die Namens-Auflistung ein, wenn der Schlüsselwert noch nicht existiert.

[index =] SetXTable(Table[, [Key][, [Typ]])
• Fügt eine bestehende Tabelle mit dem angegebenen Schlüsselwert in die Namens-Auflistung ein, wenn der Schlüsselwert noch nicht existiert.

table = GetXTable(Key[, Typ])
• Gibt die Tabelle mit dem angegebenen Schlüsselwert zurück und in {Typ} den ggf. vergebenen Typ.

table = GetXTableByIndex(Index[, Typ])
• Gibt die Tabelle mit dem angegebenen Index zurück und in {Typ} den ggf. vergebenen Typ.

[integer =] Rename(Key, NewKey)
• Gibt einer bereits registrierten Tabelle einen neuen Namen.

[integer =] AddX(Key[, [What][, [Index][, Count]])
• Fügt einer Tabelle Zeilen oder Spalten hinzu

[integer =] DelX(Key, What[, [Index][, [Where][, Count]])
• Entfernt in einer Tabelle Zeilen oder Spalten

Properties (Eigenschaften):

[long =] XTableColWidth(Key, Unit[, Array() | Index]) [= long])
• Gibt für eine registrierte Tabelle die Spaltenbreite der angegebenen Spalten oder aller Spalten, wenn keine Spalten angegeben wurden, zurück oder legt sie fest.
• Lese-Schreib-Zugriff

long = Last(Key, What)
• Gibt für eine registrierte Tabelle den Index der letzten Spalte oder Zeile zurück

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:07
Rufname: Thomas
Wohnort: Celle


AW: Keine Angst vor Klassen - 2 - Die 1. Klasse - AW: Keine Angst vor Klassen - 2 - Die 1. Klasse

Nach oben
       Version: Office 2010

I. Was sind Klassen

Entgegen der sonst üblichen Erklärungen in der Literatur möchte ich eine Klasse vereinfacht wie folgt beschreiben: Eine Klasse ist im Prinzip eine Variablen-Deklaration, die dann für alle Variablen gilt, der wir diese Klasse zuweisen. Wenn einer Variablen eine Klasse zugewiesen wird, nennt man sie Objekt. Der Klasse (und somit der Variablen resp. dem Objekt, der diese Klasse zugewiesen wurde) kann eine weitreichende Funktionalität in Form von Eigenschaften (Properties) und Methoden (Functions und Subs) "verpasst" werden, die beim Benutzen des Objektes verwendet werden kann. Entscheidend ist, dass später jedes Objekt der gleichen Klasse die gleiche Funktionalität besitzt, jedoch immer individuelle Werte beinhaltet!

II. Unsere erste Klasse

Wie vielleicht in der oben aufgeführten Klassenbeschreibung schon erkannt wurde, müssen wir die Tabellen so in Variablen ablegen, dass wir über den gewünschten Namen immer wieder darauf zugreifen können, ohne uns Gedanken darüber zu machen, die wievielte Tabelle im Dokument es eigentlich ist. Denn VBA stellt uns grundsätzlich nur die Möglichkeit zur Verfügung, über Indexnummern auf Tabellen zuzugreifen. Weiterhin wollen wir in dieser Variablen gleichzeitig einen von uns festgelegten Typ speichern.

Damit sind wir schon bei unserer ersten Klasse, die wir hier XTables_Extensions nennen (wozu diese Klasse genau gebraucht wird, sehen wir später noch). Also fügen wir im VBA-Editor über das Menü Einfügen ein Klassenmodul hinzu und ändern im Eigenschaftenfenster den Namen in XTables_Extensions.

Da eine Klasse ziemlich "dumm" ist, müssen wir ihr genau sagen, welche Funktionalität sie haben soll. Für unsere Zwecke brauchen wir erst mal nur Eigenschaften, nämlich den jeweiligen Namen der Tabelle (Key), die Tabelle selbst (Table) und den gewünschten Typ (Typ). Diese Werte wollen wir in die Variable schreiben und auch wieder auslesen.

Eigenschaften werden mit der Anweisung Property festgelegt und die gewünschten Zugriffsarten mit Let (Zuweisen von Werten), Get (Auslesen von Werten oder Objekten) und der Sonderform Set (Zuweisen von Objekten, dazu kommen wir später).

Somit erhält unsere Klasse XTables_Extensions folgenden Code:
Code:
Private pKey As Variant
Private pTable As Table
Private pTyp As Variant
 
Property Let Key(XKey As Variant)
    pKey = XKey
End Property
Property Get Key() As Variant
    Key = pKey
End Property
 
Property Set Table(tbl As Table)
    Set pTable = tbl
End Property
Property Get Table() As Table
    Set Table = pTable
End Property

Property Let Typ(Typ As Variant)
    pTyp = Typ
End Property
Property Get Typ() As Variant
    Typ = pTyp
End Property

Schauen wir uns pKey und Key genauer an:

Mit Property Let Key und Property Get Key legen wir fest, dass wir die Eigenschaft Key (enthält später den Namen der Tabelle) in diesem Objekt speichern und auslesen möchten. Physikalisch gespeichert wird der Wert in der Variablen pKey. Dies ist eine Variable, die nur innerhalb dieser Klasse benötigt wird und deshalb Private deklariert wird. Sie kann deshalb Private sein, weil wir ja nicht von außen direkt darauf zugreifen, sondern sie später über die Eigenschaft .Key mittels Let beschreiben und mittels Get auslesen.

Für Table und Typ gilt dies analog.

Mit den Typdeklarationen Variant und Table legen wir zusätzlich fest, welche Werte später in diesen Eigenschaften gespeichert werden können. Key und Typ deklarieren wir deshalb Variant, damit wir bei der Vergabe der Namen und unserer Typkennzeichen später absolut flexibel sind und es somit egal ist, ob wir Zahlen oder Strings verwenden.

Wenn wir einer Variablen nun mit Dim variable As XTables_Extensions diese Klasse zuweisen, dann erhalten wir eine Objektvariable mit den Eigenschaften variable.Key, variable.Table und variable.Typ, der wir wie gewohnt Werte zuweisen und auch wieder auslesen können:
Code:
variable.Key = "Beispiel"
Set variable.Table = ActiveDocument.Tables(1)
variable.Typ = "Typ A"
MsgBox variable.Key
MsgBox variable.Table.Columns.Count
MsgBox variable.Typ

Eine weitere Variable, der die gleiche Klasse zugewiesen wird, hat die gleiche Funktionalität, speichert aber völlig andere Werte:
Code:
Dim variable2 As XTables_Extensions
variable2.Key = "was ganz anderes"
Set variable2.Table = ActiveDocument.Tables(2)
variable2.Typ = "Typ B"
MsgBox variable2.Key
MsgBox variable2.Table.Columns.Count
MsgBox variable2.Typ

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:08
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 3 - Zusätzliche Funktionen - AW: Keine Angst vor Klassen - 3 - Zusätzliche Funktionen

Nach oben
       Version: Office 2010

III. zusätzliche Funktionen

Im Verlauf dieses Projektes werden einige Funktionen benötigt, die uns leider von Word nicht bzw. nicht so, wie wir sie gerne hätten, zur Verfügung gestellt werden. Deshalb werde ich sie hier kurz vorstellen. Da sie nicht ursächlich mit diesem Projekt zusammenhängen und auch in anderen Projekten genutzt werden können, fügen wir dazu ein neues Modul (nicht Klassenmodul!) ein und nennen es XFunctions (Menü Einfügen -> Modul)

III.1 Umwandlung von Maßeinheiten in Points und zurück

Diese Funktionen dienen zum komfortablen Umwandeln von diversen Maßeinheiten in die für die Tabellen benötigten Points und zurück. Die Beschreibung der Enumerationen folgt weiter unten.
Code:
'
' Umwandlung von Wert {val} von Maßeinheit {MeasureUnit} in Points
'
Function cPoints(val As Variant, mu As MeasureUnit) As Single
    Select Case mu
    Case Xcm
        cPoints = CentimetersToPoints(val)
    Case Xmm
        cPoints = MillimetersToPoints(val)
    Case Xinch
        cPoints = InchesToPoints(val)
    Case Xpixel
        cPoints = PixelsToPoints(val)
    Case Xpica
        cPoints = PicasToPoints(val)
    Case Else
        cPoints = val
    End Select
End Function
'
' Umwandlung von Wert {val} von Points in Maßeinheit {MeasureUnit}
'
Function cPointsTo(mu As MeasureUnit, val As Variant) As Single
    Select Case mu
    Case Xcm
        cPointsTo = PointsToCentimeters(val)
    Case Xmm
        cPointsTo = PointsToMillimeters(val)
    Case Xinch
        cPointsTo = PointsToInches(val)
    Case Xpixel
        cPointsTo = PointsToPixels(val)
    Case Xpica
        cPointsTo = PointsToPicas(val)
    Case Else
        cPointsTo = val
    End Select
End Function


III.2 QuickSort und QuickSortReverse

Des Weiteren benötigen wir eine Sortierroutine für die Spalten-Arrays, die an manche Methoden übergeben werden. Ich habe mich hier für die einfache, aber schnelle Quicksort-Routine entschieden und sie auf einfache Art um eine Reverse-Funktion erweitert. Eine ausführliche Erläuterung des Codes würde zum einen den Rahmen des Tutorials sprengen, findet sich aber zum anderen in diversen Ausführungen im Internet. Der Interessierte möge sich dort mit der Routine vertraut machen.

Die hier verwendete Quicksort-Routine sortiert ein eindimensionales Array (oder einen Teil davon) aufsteigend; da diese Funktion rekursiv, also auch von sich selbst, aufgerufen wird, ist die Angabe des Start- und End-Indexes immer erforderlich. QuickSortReverse gibt einfach nur das absteigend sortierte Array zurück; da wir immer nur das gesamte Array sortieren und QuickSortReverse QuickSort aufruft, braucht an QuickSortReverse nur das Array ohne die Start- und End-Indizes übergeben werden.
Code:
'
' Array sortieren
'

Sub QuickSort(ByRef sArray As Variant, ByVal MinElem As Long, ByVal MaxElem As Long)
    Dim Mitte As Long
    Dim vDummy As Variant
    Dim vMitte As Variant
    Dim i As Long, j As Long
   
    If MinElem > MaxElem Then       ' Abbruchbedingung der Rekursion prüfen
        Exit Sub
    End If
    Mitte = (MinElem + MaxElem) \ 2 ' Mitte des Arrays
    vMitte = sArray(Mitte)
    i = MinElem                     ' Zähler links/rechts initialisieren
    j = MaxElem
    Do
        Do While sArray(i) < vMitte ' Von links bis zur Mitte
            i = i + 1
        Loop
        Do While sArray(j) > vMitte ' Von rechts bis zur Mitte
            j = j - 1
        Loop
           
        If i <= j Then
            vDummy = sArray(j)      ' Die beiden gefundenen,
            sArray(j) = sArray(i)   '   falsch einsortierten Elemente
            sArray(i) = vDummy      '   vertauschen
            i = i + 1               ' nächste Position prüfen
            j = j - 1
        End If
           
    Loop Until i > j
    QuickSort sArray, MinElem, j    ' Rekursiver Aufruf mit den Teil-Arrays
    QuickSort sArray, i, MaxElem
End Sub

Sub QuickSortReverse(ByRef sArray As Variant)
    Dim revArray As Variant, revIdx As Long
    QuickSort sArray, LBound(sArray), UBound(sArray)
    revIdx = UBound(sArray)
    ReDim revArray(revIdx)
    For Each idx In sArray
        revArray(revIdx) = idx
        revIdx = revIdx - 1
    Next
    sArray = revArray
End Sub

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:09
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 4 - Enumerationen - AW: Keine Angst vor Klassen - 4 - Enumerationen

Nach oben
       Version: Office 2010

IV. Die Enumerationen

Um Parameter aussagekräftig zu vergeben oder zurückzugeben, machen wir es uns einfach, in dem wir mögliche Parameter, soweit sie uns jetzt schon bekannt sind, vorab definieren und mit möglichst aussagekräftigen Namen versehen.

Zu diesem Zweck gibt es die Enumerationen – das sind Aufzählungen, die ähnlich wie Konstanten fungieren (sie erhalten Werte [benutzerdefiniert oder automatisch], die dann nicht mehr geändert werden können), die in Gruppen zusammengefasst sind. Sie habe jedoch wesentliche Unterscheide:
    • zugewiesene Werte dürfen nur Integer-Werte sein (-32768 bis 32767)
    • es müssen keine Werte zugewiesen werden; in diesem Fall erfolgt eine automatische Wertzuweisung
    • eine Variable kann mit einem Enum-Typen deklariert werden und erhält als Variablen-Wert dann einen Elementnamen aus der zugehörigen Enumeration

Und wenn wir z. B. eine Parameter-Variable als einen solchen Enum-Typen deklarieren, brauchen wir nur noch den Elementnamen angeben und brauchen uns um die eigentlichen Werte keine Gedanken mehr machen. Der große Vorteil einer solchen Deklaration ist, dass während des Programmierens diese definierten Enumerationselemente als Quicktipp angezeigt werden (so, wie es auch bei VBA-Befehlen geschieht) und "falsche" Wertzuweisungen vermieden werden.

Für unser Projekt wissen wir, das wir die Maßeinheiten für die Breite abfragen müssen, bestimmte Fehlermeldungen benötigen und das wir bestimmte Tabellentypen definieren wollen. Für die Methoden AddX und DelX benötigen wir zusätzlich die Angaben, was (ob Zeilen oder Spalten) hinzugefügt oder gelöscht werden soll, ob beim Löschen die indizierte Zeile bzw. Spalte ebenfalls gelöscht werden soll und, wenn sich das Hinzufügen oder Entfernen auf die Tabelle (Anfang oder Ende) bezieht, diese Angabe ebenfalls.

Um diese Parameter also zu definieren nutzen wir die Enum-Anweisung; der Code wird in die im nächsten Kapitel beschriebene Klasse eingefügt:
Code:
Enum MeasureUnit
    Xpoint
    Xcm
    Xmm
    Xinch
    Xpixel
    Xpica
End Enum

Enum XTableErr
    Aborted = -1
    Undefined = -1
    Faults = 0
    Successful = 1
End Enum

Enum XTableType
    Undefined = 0
    Unknown = 0
    ' vordefinierte Typen
    Typ_A = 1
    Typ_B = 2
    '
    ' hier können weitere folgen...
End Enum
Public Enum XWhat
    Column
    Row
End Enum

Public Enum XIndex
    Table
End Enum
Public Enum XWhere
    Included
    Excluded
End Enum

Es scheint im Moment verwunderlich, dass wir teilweise mehrere Einträge mit gleichen Werten definieren. Das tun wir nur, um den Code später besser lesbar zu haben, denn im Kontext ist es besser verständlich, z. B. mal XTableType.undefined und mal XTableType.unknown zu benutzen, wie wir noch sehen werden. Allerdings ist es für die Programmausführung letztlich egal, ob wir den Enum-Eintrag als Namen oder den zugehörigen Wert benutzen, Hauptsache, der richtige Wert wird irgendwie übergeben.

Info
Die Angabe von Werten ist für die Enum-Anweisung nicht zwingend erforderlich, denn nicht immer sind bestimmte Werte für die Programmausführung erforderlich, sondern es müssen z. B. nur Differenzierungsmöglichkeiten geschaffen werden. Daher werden die Werte, wenn sie in der Enum-Auflistung nicht angegeben werden, automatisch fortlaufend vergeben. Wenn Werte angegeben werden, müssen sie im Integer-Wertebereich liegen (-32.768 bis 32.767), dürfen also auch negativ sein. Bei der automatischen Wertzuweisung ergibt sich die fortlaufende Nummerierung aus dem unmittelbar vorher zugewiesenen Element +1. So ergibt z. B.
Code:
Enum Beispiel
   A
   B = -4
   C
   D
   E = 999
End Enum
den Wert 0 für Beispiel.A, -3 für Beispiel.C und -2 für Beispiel.D.

Einer mit einem Enum-Typen deklarierten Variable werden die Werte als Elementnamen zugewiesen.
Code:
X = Beispiel.C
oder
Code:
Dim X As Beispiel
X = C
In beiden Fällen hat X den Wert -3.
_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:11
Rufname: Thomas
Wohnort: Celle


AW: Keine Angst vor Klassen - 5 - Dictionary, 2. Klasse - AW: Keine Angst vor Klassen - 5 - Dictionary, 2. Klasse

Nach oben
       Version: Office 2010

V. Das Dictionary-Objekt und die zweite Klasse

Nun ist das noch nicht sehr hilfreich, denn wir müssen für jede Tabelle eine Variable definieren und wir haben der Tabelle zwar einen Namen gegeben, aber mit dem Namen können wir noch nicht effektiv arbeiten. Um das letztlich zu ermöglichen, benutzen wir das Dictionary-Objekt aus der Microsoft Scripting Runtime-Bibliothek. Diese fügen wir unserem Projekt hinzu, in dem wir über das Menü Extras, Eintrag Verweise… diese Bibliothek auswählen.

Ein Dictionary-Objekt ist eine Auflistung von Werten (Items), die über einen Schlüssel (Key) angesprochen werden. Hier kann jedem Key genau ein Item zugewiesen werden. Hier erkennen wir schon, warum wir für unsere Tabellenobjekte eine Klasse gebildet haben, denn wir brauchen ja für jede Tabelle mit dem „Schlüssel“ (= Namen) Key mehrere Items, in diesem Projekt Table und Typ. Und da wir einem Dictionary-Item nicht nur Werte, sondern auch Objekte zuweisen können, lässt sich das prima verbinden, wenn wir einfach unser oben gestaltetes Tabellen-Objekt mit den erforderlichen Werten als Dictionary-Item speichern.

In unserer ersten Klasse haben wir die Funktionalität der Tabellenobjekte festgelegt. Alles Weitere erledigen wir nun in der „eigentlichen“ Klasse XTables. Diese fügen wir wie bereits mit der ersten Klasse geschehen in unser Projekt über Einfügen -> Klassenmodul hinzu und ändern den Namen im Eigenschaften-Fenster in XTables.

Das Dictionary, welches unsere gesamten Tabellenobjekte erhalten soll, nennen wir ebenfalls XTables und deklarieren es entsprechend als erstes:
Code:
Public XTables As New Scripting.Dictionary
Hier nehmen wir die Public-Deklaration, weil wir später natürlich mit den darin enthaltenen Objekten arbeiten wollen.

Hier folgt dann der im vorherigen Kapitel beschriebene Code der Enumerationen.

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:12
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 6 - SetXTable - AW: Keine Angst vor Klassen - 6 - SetXTable

Nach oben
       Version: Office 2010

Und nun kommen wir schon zu Implementierung der benötigten Eigenschaften und Methoden unserer Klasse:

V.1 Methoden

V.1a Methode SetXTable

Zuerst wollen wir es ermöglichen, unserer Namens-Auflistung eine Tabelle mit einem Namen und dem gewünschten Typ hinzuzufügen. Dafür schreiben wir die Methode SetXTable. Weil vielleicht später auch die lfd. Nummer der Tabelle benötigt wird, wollen wir diese lfd. Nummer auch wieder zurückgeben, um einem Programm später die weitere Verwendung zu ermöglichen. Außerdem können wir einen Fehlerwert zurückgeben, falls das Hinzufügen der Tabelle nicht klappen sollte, weil der Name bereits einer anderen Tabelle zugewiesen wurde.

Methoden werden als Sub oder Function in eine Klasse implementiert, und da wir einen Wert zurückgeben wollen, nehmen wir eine Function:
Code:
Function SetXTable(Table As Table, Optional Key As Variant, Optional Typ As Variant) As Long

Nachdem wir vorhin mit der Klasse XTables_Extensions eine Objekt-Definition für unsere Tabellen festgelegt haben, müssen wir die Tabellen nun mit dieser Klasse in Verbindung bringen. Dies geschieht über die entsprechende Deklaration:
Code:
Dim pTable As New XTables_Extensions

Der Variablen pTable wurde die Klasse XTables_Extensions zugewiesen und nun stehen die Eigenschaften Key, Table und Typ im Objekt pTable zur Verfügung.

Die Tabelle bekommen wir als Parameter im Funktionsaufruf geliefert und können sie gleich speichern:
Code:
Set pTable.Table = Table

Da es sich bei einer Tabelle ebenfalls um ein Objekt handelt und nicht um einen Wert, müssen wir für die Zuweisung Set verwenden!

Anders ist es mit den Parametern Key und Typ. Sie wurden Optional deklariert, was bedeutet, dass sie beim Funktionsaufruf nicht zwingend erforderlich sind. Das müssen wir natürlich berücksichtigen. Und da es so sein soll, dass Default-Werte genommen werden, wenn kein Parameter übergeben wurde, müssen wir sie entsprechend zuweisen. Für den Parameter Key, der den Namen der Tabelle beinhalten soll, bedeutet das, das die lfd. Nummer verwendet wird, wenn kein Name angegeben wurde.
Code:
pTable.Key = IIf(IsMissing(Key), XTables.Count + 1, Key)

erfüllt genau diesen Zweck. Mit der Kontrollstruktur IIF(Bedingung, Wahr-Ausdruck, Falsch-Ausdruck) prüfen wir in der Bedingung mit IsMissing(Key), ob Key übergeben wurde. Wenn Key nicht angegeben wurde (IsMissing=True), dann wird im Wahr-Teil die Anzahl der bereits in der Auflistung XTables vorhandenen Tabellen mit der Dictionary-Eigenschaft Count ermittelt. Da Auflistungen Null-basiert sind, das heißt der Zähler beginnt bei 0, erhalten wir für die erste Tabelle den Zählerstand 0, den wir um 1 erhöhen. Somit ist 1 die lfd. Nummer = Index unserer ersten Tabelle und Count ist auch bei der nächsten Abfrage 1. Wurde Key aber angegeben (IsMissing=False), dann wird im Falsch-Teil eben dieser angegebene Key zurückgegeben.

Den Namen haben wir also nun festgelegt und haben ihn in pTable.Key abgelegt.

Für den Typ machen wir es ähnlich. Hier soll es so sein, dass, wenn kein Typ angegeben wurde, als Typ die Anzahl der Tabellen-Spalten (Table.Columns.Count) genommen wird. Nach der Prüfung legen wir Typ ebenfalls im pTable-Objekt (pTable.Key) ab:
Code:
pTable.Typ = IIf(IsMissing(Typ), Table.Columns.Count, Typ)


Wir haben also nun ein Tabellen-Objekt pTable mit folgenden Eigenschaften:
    pTable.Table enthält die übergebene Tabelle
    pTable.Key enthält den übergebenen Namen oder die lfd. Nummer
    pTable.Typ enthält den übergebenen Typ oder die Anzahl Spalten der Tabelle
Und dieses Objekt übertragen wir jetzt in unser Dictionary-Objekt XTables. Als Dictionary-Key übernehmen wir unseren Namen aus der Variablen Key und als Dictionary-Item, also als „Wert“, übergeben wir dieses soeben erstellte Tabellen-Objekt pTable.
Code:
XTables.Add Key, pTable

und schon haben wir eine Tabelle, die wir mit Namen aufrufen können.

Dictionary.Item(Key) oder kurz Dictionary(Key) gibt uns den Wert (Item) des Schlüssels (Key) zurück, sodass wir unsere Tabelle, die z.B. mit dem Namen „Tabelle1“ eingetragen wurde, so ansprechen können (zurzeit nur innerhalb der Klasse, aber dazu später mehr):
    MsgBox XTables("Tabelle1").Table.Column.Count ' Anzahl Spalten der Tabelle
    MsgBox XTables("Tabelle1").Typ ' Typ der Tabelle

Wir sollten aber auf jeden Fall noch eine Prüfung vornehmen, ob nicht bereits schon eine Tabelle mit dem gleichen Namen angelegt wurde. In diesem Fall wollen wir ja einen Fehlerwert zurückgeben, damit das aufrufende Programm diese Situation ggf. verarbeiten kann.

In einem Dictionary-Objekt kann die Existenz eines Schlüssels mit der .Exists-Methode geprüft werden. Also bauen wir den bisherigen Code in eine If-Abfrage ein und können dann auch gleich den Index der Tabelle oder aber den Fehlerwert -1, den wir aussagekräftig aus unserer XTableErr-Enumeration als XTableErr.Aborted nehmen, zurückgeben. In einer VBA-Funktion wird ein Wert zurückgegeben, indem dem Namen der Funktion ein Wert zugewiesen wird.

Um die nun nicht mehr benötigten Ressourcen wieder freizugeben, wird zum Schluss pTable noch gelöscht.

Der gesamte Code der Funktion sieht dann so aus:
Code:
'
' Tabelle mit Schlüsselwert registrieren
'
Function SetXTable(Table As Table, _
                   Optional Key As Variant, _
                   Optional Typ As Variant) As Long
'
' Table:    die zu registrierende Tabelle
' Key:      optional Tabellen-Schlüssel, Default=lfd. Nummer
' Typ:      optional Typkennzeichen, Default=Anzahl Spalten der Tabelle
'
' Rückgabe: Indexnr. der Tabelle in der Auflistung,
'           wenn Tabelle hinzugefügt werden konnte, sonst -1
'
    Dim pTable As New XTables_Extensions
    If Not XTables.Exists(Key) Then
        Set pTable.Table = Table
        pTable.Key = IIf(IsMissing(Key), XTables.Count + 1, Key)
        pTable.Typ = IIf(IsMissing(Typ), Table.Columns.Count, Typ)
        XTables.Add Key, pTable
        SetXTable = XTables.Count
    Else
        SetXTable = XTableErr.Aborted
    End If
    Set pTable = Nothing
End Function

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:13
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 7 - GetXTable - AW: Keine Angst vor Klassen - 7 - GetXTable

Nach oben
       Version: Office 2010

V.1b Methode GetXTable

Wir können zwar mit dem Dictionary XTables arbeiten, aber der häufigste Fall wird ja sein, eine Tabelle mit einem bestimmten Namen auslesen zu wollen, um damit irgendetwas zu tun. Also müssen wir eine Methode bereitstellen, die gewünschte Tabelle an ein aufrufendes Programm zurückzugeben. Dafür schreiben wir als Methode die Funktion GetXTable(). Als weitere Funktionalität wollen wir dabei auch bei Bedarf den Tabellentyp mit zurückgeben und dieses Typkennzeichen, wenn es denn beim Aufruf angegeben wird, gleichzeitig als Prüfmöglichkeit verwenden, ob eine Tabelle einem bestimmten Typ entspricht.

Die Funktion muss also lauten:
Code:
Function GetXTable(Key As Variant, Optional Typ As Variant) As Table
Der Key ist natürlich unbedingt erforderlich, weil er den Namen der gewünschten Tabelle enthält. Der Typ verdient hier besondere Beachtung. Da wir hierüber zum einen eine Testmöglichkeit schaffen wollen, ob eine bestimmte Tabelle einem bestimmten Typ entspricht, aber auch die Möglichkeit bereitstellen wollen, einen Typ mit der Tabelle zusammen auslesen zu können, setzen wir ihn per Default auf XTableType.Unknown. Was daraus wird, sehen wir gleich.

Zuerst prüfen wir, ob der Name überhaupt schon registriert wurde. Ist das nicht der Fall, wird NOTHING zurückgegeben und, falls eine Typvariable angegeben wurde, erhält sie aus der XTableErr-Enumeration den Wert -1. So kann das aufrufende Programm den Rückgabewert auf Misserfolg prüfen und Programmfehler abfangen.
Code:
If XTables.Exists(Key) Then
     '...
Else
     GetXTable = Nothing
     Typ = XTableErr.Undefined
End If
Doch nun zum interessanten Teil, der erfolgreichen If-Prüfung. Zuerst holen wir die Tabelle mit dem entsprechenden Namen aus unserer Auflistung und stellen ihn zur Rückgabe bereit.
Code:
Set GetXTable = XTables(Key).Table

Aber für den Typ gibt es mehrere Möglichkeiten. Entweder wurde er beim Aufruf überhaupt nicht angegeben oder es wurde ein Wert übergeben oder es wurde eine Variable übergeben. Was bedeutet das im Einzelnen?
  • Set tbl = GetXTable("TabelleA") Der Typ ist für das aufrufende Programm uninteressant und wird vernachlässigt.

  • Set tbl = GetXTable("TabelleA", "TypA") Es soll auf „TypA“ geprüft werden, aber es wird kein Typ zurückgegeben. Das aufrufende Programm weiß, wenn eine Tabelle zurückgegeben wird, stimmt der Typ, wird Nothing zurückgegeben, stimmte der Typ nicht.

  • Set tbl = GetXTable("TabelleA", varTyp) Kombiniert und erweitert beide vorangegangenen Versionen. Wenn varTyp den Wert XTableType.unknown (0) hat, soll die Tabelle zurückgegeben werden und varTyp den definierten Typ beinhalten. Hat aber varType einen Wert (z.B. „TypA“), dann erfolgt wie zuvor eine Prüfung und es wird die Tabelle oder Nothing zurückgegeben.

Aus diesem Grund setzen wir Typ per Default auf XTableType.Unknown, wenn er nicht mit angegeben wurde, denn dann entspricht der erste Aufruf dem dritten und wir ersparen uns im Code eine zusätzliche Abfrage. Denn NUR, wenn Typ NICHT XTableType.Undefined ist UND wenn Typ NICHT dem definierten Typ entspricht, DANN wird als Tabelle Nothing zurückgegeben.Eine ggf. übergebene Typ-Variable erhält aber auf jeden Fall den definierten Typ der gewünschten Tabelle, wenn sie existiert.
Der entsprechende Code dafür sieht so aus:
Code:
        Set GetXTable = XTables(Key).Table
        If Not Typ = XTableType.undefined And Typ <> XTables(Key).Typ Then _
            Set GetXTable = Nothing
        Typ = XTables(Key).Typ

Hier nochmal die Methode GetXTable im Zusammenhang:
Code:
'
' Tabelle mit Schlüsselwert zurückgeben
'
Function GetXTable(Key As Variant, _
                   Optional Typ As Variant = XTableType.Unknown) As Table
'
' Key:      Schlüsselwert der zurückzugebenen Tabelle
' Typ:      optional Typkennzeichen;
'           wenn angegeben (und <> 0), dann Prüfung, ob Tabellentyp übereinstimmt.
'           Stimmt Typ nicht überein, wird Tabelle=NOTHING
'           und der DEFINIERTE Typ der Tabelle zurückgegeben.
'           Ermöglicht Prüfung, ob Tabelle einem bestimmten Typ entspricht
'           Wird eine Typvariable angegeben und ist 0,
'           dann wird in ihr der Typ zurückgegeben.
'
' Rückgabe: - Tabelle
'           - wenn Variable für Typkennzeichen übergeben,
'             dann Typkennzeichen in der Variablen
'
    If XTables.Exists(Key) Then
        Set GetXTable = XTables(Key).Table
        If Not Typ = XTableType.Undefined And Typ <> XTables(Key).Typ Then _
            Set GetXTable = Nothing
        Typ = XTables(Key).Typ
    Else
        Set GetXTable = Nothing
        Typ = XTableErr.Undefined
    End If
End Function

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:13
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 8 - GetXTableByIndex - AW: Keine Angst vor Klassen - 8 - GetXTableByIndex

Nach oben
       Version: Office 2010

V.1c Methode GetXTableByIndex

Der Vollständigkeit halber wollen wir auch noch eine Methode bereitstellen, die es uns ermöglicht, über die Indexnummer aus der Auflistung eine Tabelle zu holen. Diese Methode unterscheidet sich nur in einem Punkt von der vorherigen. Aus einem Dictionary-Objekt kann zwar über die entsprechenden Keys ein Item geholt werden kann, allerdings wird keine Index-Funktionalität bereitgestellt. Also müssen wir anhand des übergebenen Indexes das Dictionary mit einer Vergleichsvariablen durchlaufen und wenn wir an der richtigen Stelle angekommen sind, holen wir das Tabellen-Objekt, lesen die Table-Eigenschaft aus, welche ja die Tabelle enthält und geben sie aus der Funktion zurück.
Code:
    Dim idxCnt As Long
    Dim itemCnt As Variant
    If Not Index < 1 And Not Index > XTables.Count Then
        For Each itemCnt [In XTables.Items
            idxCnt = idxCnt + 1
            If idxCnt = Index Then
                Set GetXTableByIndex = itemCnt.Table
                Exit For
            End If
        Next
    Else ...
Natürlich prüfen wir auch hier vorher, ob der übergebene Index in einem gültigen Bereich liegt.

Wichtig ist hierbei, dass der Index des Dictionary nicht mit dem Index der ActiveDocument.Tables übereinstimmen muss. Ersterer ergibt sich aus der Reihenfolge, wie die Tabellen in unserer Klasse registriert wurden, letzterer aus der Reihenfolge, wie sie im Dokument angelegt wurden.

Was den Typ und dessen Zuweisung betrifft, unterscheidet sich diese Methode nicht von der vorherigen, so dass ich hier gleich den vollständigen Code zeige:
Code:
'
' Tabelle mit Indexnr aus Auflistung zurückgeben
'
Function GetXTableByIndex(Index As Long, _
                          Optional Typ As Variant = XTableType.Unknown) As Table
'
' Index:    Auflistungs-Indexnr. der zurückzugebenen Tabelle
' Typ:      siehe GetXTable
'
' Rückgabe: siehe GetXTable
'
    Dim idxCnt As Long
    Dim itemCnt As Variant
    If Not Index < 1 And Not Index > XTables.Count Then
        For Each itemCnt In XTables.Items
            idxCnt = idxCnt + 1
            If idxCnt = Index Then
                Set GetXTableByIndex = itemCnt.Table
                Exit For
            End If
        Next
        If Not Typ = XTableType.Undefined And Typ <> itemCnt.Typ Then _
            Set GetXTableByIndex = Nothing
        Typ = itemCnt.Typ
    Else
        GetXTableByIndex = Nothing
        Typ = XTableErr.Undefined
    End If
    Set itemCnt = Nothing
End Function


Nun haben wir alle Methoden bereitgestellt, um bestehende Tabellen mit Namen zu versehen und sie entsprechend aufzurufen. Hiermit ist es nun ein Leichtes, auch neue Tabellen ins Dokument einzufügen und sie gleichzeitig in unsere Auflistung aufzunehmen.

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:15
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 9 - AddXTable - AW: Keine Angst vor Klassen - 9 - AddXTable

Nach oben
       Version: Office 2010

V.1d Methode AddXTable

Wir stellen nun eine Methode bereit, eine Standardtabelle im Dokument zu erzeugen und sie in unserer Auflistung zu registrieren. Gleichzeitig geben wir der Tabelle Rahmenlinien, die wohl meistens verwendet werden. Wir benötigen im besten Fall für eine Tabelle
    • den Namen für unsere Auflistung, sonst wird es auch nur eine lfd. Nummer
    • den Typ, falls wir ihn benutzen wollen
    • die Anzahl Zeilen und
    • die Anzahl Spalten, die aber auch per Default (beliebig, hier: jeweils 2) festgesetzt werden
    • die Art der inneren und äußeren Rahmenlinien
    • und den Platz im Dokument, der aber ebenfalls per Default auf die aktuelle Cursorposition oder, falls geschehen, den aktuell selektierten Bereich (der dann überschrieben wird) festgesetzt wird.
Code:

Function AddXTable(Optional Key As Variant, _
                   Optional Typ As Variant, _
                   Optional Rows As Long = 2, _
                   Optional Columns As Long = 2, _
                   Optional InsideLineStyle As WdLineStyle = wdLineStyleNone, _
                   Optional OutsideLineStyle As WdLineStyle = wdLineStyleNone, _
                   Optional Range As Range) As Long

Da uns die meiste Arbeit die bereits geschriebenen Methoden abnimmt, brauchen wir erst mal wieder nur zu prüfen, ob der Name, wenn er denn angegeben wurde, schon existiert, um dann statt des Index der Tabelle den Fehlerwert XTableErr.Aborted (-1) zurückzugeben. Dann prüfen wir noch, ob die Position im Dokument mit übergeben wurde und setzen sie sonst auf die aktuelle Cursorposition bzw. Auswahl.
Code:
        If Range Is Nothing Then Set Range = Selection.Range

Dann können wir die Tabelle mit unserer Methode SetXTable schon registrieren.

Die Tabelle muss dann natürlich erst erzeugt werden, was wir mit
Code:
        Set tbl = ActiveDocument.Tables.Add(Range, Rows, Columns)

erledigen. Dieser erzeugten Tabelle weisen wir dann die inneren und äußeren Rahmenlinien zu.
Code:
        With tbl.Borders
            .InsideLineStyle = InsideLineStyle
            .OutsideLineStyle = OutsideLineStyle
        End With

An dieser Stelle können natürlich noch beliebige weitere Tabellen-Formatierungen eingefügt werden. Aber für dieses Tutorial soll es erst einmal genügen.
Dann übergeben wir die Tabelle mit dem Namen (in Key) und dem Typ (soweit jeweils vorhanden) direkt weiter an SetXTable. Da wir SetXTable in der Klasse selbst verwenden und die Klasse deshalb natürlich keiner unserer Variablen zugewiesen wurde, müssen wir das Schlüsselwort Me verwenden, welches eine automatische Variable ist, die auf die sich selbst zugewiesene Klasse verweist.
Code:
        AddXTable = Me.SetXTable(tbl, Key, Typ)

Der Rückgabewert von SetXTable ist ja der lfd. Index der Tabelle, den wir aus der Funktion durch die Zuweisung an AddXTable auch wieder zurückgeben.
Zum Schluss geben wir natürlich wieder die Ressourcen frei.

Die gesamte Methode sieht dann so aus:
Code:
'
' neue Tabelle ins Dokument und in die Auflistung einfügen,
' wenn Schlüsselwert noch nicht existiert
'
Function AddXTable(Optional Key As Variant, _
                   Optional Typ As Variant, _
                   Optional Rows As Long = 2, _
                   Optional Columns As Long = 2, _
                   Optional InsideLineStyle As WdLineStyle = wdLineStyleNone, _
                   Optional OutsideLineStyle As WdLineStyle = wdLineStyleNone, _
                   Optional Range As Range) As Long
'
' Key:      optional Schlüsselwert, Default=lfd. Nummer als Indexnr.
' Typ:      optional Typkennzeichen, Default=Anzahl Spalten der Tabelle,
'                    wenn TableType.undefined
' Rows:     optional Anzahl Zeilen, Default=2
' Columns:  optional Anzahl Spalten, Default=2
' InsideLineStyle:  optional    innere Rahmenlinie aus der wdLineStyle-Enumeration
' OutsideLineStyle: optional    äußere Rahmenlinie aus der wdLineStyle-Enumeration
' Range:    optional Posiiton als Range im Dokument,
'                    Default=aktuelle Position bzw. Selection im Dokument
'
' Rückgabe:          Indexnr. der Tabelle in der Auflistung,
'                    wenn Tabelle hinzugefügt werden konnte, sonst -1
'
    Dim tbl As Table
    If Not XTables.Exists(Key) Then
        If Range Is Nothing Then Set Range = Selection.Range
        Set tbl = ActiveDocument.Tables.Add(Range, Rows, Columns)
        With tbl.Borders
            .InsideLineStyle = InsideLineStyle
            .OutsideLineStyle = OutsideLineStyle
        End With
        AddXTable = Me.SetXTable(tbl, Key, Typ)
    Else
        AddXTable = XTableErr.Aborted
    End If
    Set tbl = Nothing
End Function

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:15
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 10 - Rename - AW: Keine Angst vor Klassen - 10 - Rename

Nach oben
       Version: Office 2010

V.1e Methode Rename

Als weitere Methode wollen wir in diesem Tutorial noch die Möglichkeit schaffen, den Namen einer bereits registrierten Tabelle zu ändern. Dies ist sehr einfach möglich, denn wir brauchen nur den Key des Tabellenobjektes und den zugehörigen Key des Dictionaries ändern.

Natürlich prüfen wir auch hier vorher, ob die zu ändernde Tabelle überhaupt in unserer Auflistung existiert und geben den entsprechenden Fehlercode bzw. Erfolgs-„Fehler“ zurück. Hier sehen wir auch eine Verwendung unserer Enumerationen. Der Rückgabewert ist nämlich vom Typ der Enumeration XTableErr und kann im aufrufenden Programm auch genauso abgefragt werden.
Code:
'
' Einer registrierten Tabelle einen neuen Schlüsselwert geben
'
Function Rename(Key As Variant, _
                NewKey As Variant) As XTableErr
'
' Key:      bisheriger Schlüsselwert der Tabelle
' NewKey:   neuer Schlüsselwert der Tabelle
'           enthält nach erfolgreicher Änderung XTableErr.successful,
'           sonst XTableErr.aborted
'
    If XTables.Exists(Key) Then
        XTables(Key).Key = NewKey
        XTables.Key(Key) = NewKey
        Rename = Successful
    Else
        Rename = Aborted
    End If
End Function

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:16
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 11 - AddX - AW: Keine Angst vor Klassen - 11 - AddX

Nach oben
       Version: Office 2010

V.1f Methode AddX

Jetzt wollen wir noch eine Methode bereitstellen, um in unsere Tabellen neue Spalten und Zeilen einzufügen. Da aber das normale Einfügen für mehrere Spalten oder Zeilen, die vielleicht noch auseinander liegen, viel zu umständlich ist, wollen wir das komfortabel bereitstellen.

Zuerst müssen wir wissen, was denn eingefügt werden soll, nämlich Zeilen oder Spalten. Hier übernehmen wir also einen Elementnamen aus unserer XWhat-Enumeration: Column oder Row. Falls dieser Parameter nicht angegeben wird, soll automatisch von Spalten ausgegangen werden. Dann müssen wir wissen, wo und wie viele Zeilen bzw. Spalten eingefügt werden sollen. Hier stellen wir mehrere Möglichkeiten zur Verfügung, weshalb wir auch den Typ Variant benutzen müssen. Als Einfüge-Position übergeben wir entweder den Index einer bestimmten Zeile oder Spalte, oder aber ein Array mit allen Zeilen bzw. Spalten. Diese Auflistung soll dabei völlig unabhängig von der Reihenfolge sein. Soll bspw. neben der 2. und neben der 5. Spalte eine weitere Spalte eingefügt werden, dann kann das als Array(2, 5) oder auch als Array (5, 2) übergeben werden. Wenn die Zeilen bzw. Spalten aber am Tabellenanfang oder Tabellenende eingefügt werden sollen, dann braucht der Parameter Index überhaupt nicht angegeben werden, kann aber auch aus der XIndex-Enumeration als XIndex.Table übergeben werden. Und schließlich brauchen wir noch die Anzahl der Zeilen bzw. Spalten. Um zu wissen, ob die Zeilen über oder unter der im Index angegebenen Zeile bzw. die Spalten links oder rechts neben der im Index angegeben Spalte eingefügt werden sollen, kann dieser Parameter positiv oder negativ übergeben werden. Positive Zahlen bedeuten dann unter der Index-Zeile bzw. rechts neben der Index-Spalte, negative Zahlen entsprechend über der Index-Zeile bzw. links neben der Index-Spalte.

Du siehst, es wird eine ziemlich mächtige, dafür aber komfortable Methode.

Als Erfolg (oder Misserfolg) soll dem aufrufenden Programm dann mitgeteilt werden, ob alle Zeilen- bzw. Spalten-Indizes verarbeitet werden konnten oder, weil vielleicht einige Zeilen bzw. Spalten gar nicht existierten, nur einige verarbeitet wurden oder überhaupt keine verarbeitet wurden, weil die Tabelle vielleicht gar nicht existiert oder alle Indizes außerhalb des Tabellenbereiches lagen.

Der Aufruf sieht also wie folgt aus:
Code:
Function AddX(Key As Variant, _
              Optional What As XWhat = Column, _
              Optional Index As Variant = XIndex.Table, _
              Optional Count As Long = 1) As XTableErr

Da wir mehrere Möglichkeiten verarbeiten müssen und Word uns nur die Möglichkeit gibt, einzelne Zeilen oder Spalten zu verarbeiten, lagern wir die eigentliche Routine zum Einfügen in eine separate Funktion aus, zu der wir im Anschluss kommen.

In dieser Hauptfunktion prüfen wir zuerst, ob ein Array (mit mehreren Spalten oder Zeilen) oder ein einzelner Index übergeben wurde (die Prüfung, ob überhaupt ein Index übergeben wurde [wg. Tabelle], können wir uns sparen, denn dieser Wert wurde bereits als Default im Funktionsaufruf gesetzt).

Diese Prüfung ermöglicht uns die Funktion VarType(), die zum einen den Typ einer Variablen als Integerwert zurückgibt (z.B. 2 bzw. vbInteger für Integer-Variablen), aber auch, und das ist für uns interessant, diesen Integerwert + 8192, wenn es sich dabei um ein Array handelt. Dieser Wert ist als Konstante vbArray vorhanden, so dass wir also nur prüfen müssen, ob VarType der übergebenen Index-Variablen größer als 8192 sprich vbArray ist und wir wissen, dass es sich um ein Array handelt. Ist das nicht der Fall, es wurde also ein einzelner Wert übergeben, können wir diesen Index sofort an unsere Einfüge-Routine doAddX übergeben.

Handelt es sich aber um ein Array, wird es etwas komplizierter. Was passiert denn, wenn links neben der 2., 3. und der 5. Spalte jeweils 2 weitere Spalten eingefügt werden? Wenn die 2. Spalte verarbeitet wird, ist alles in Ordnung, aber wenn danach die 3. Spalte verarbeitet wird, ist es ja nicht mehr die 3., sondern mittlerweile die 5., denn es wurden ja bereits 2 Spalten eingefügt. Und wenn dann die 5. verarbeitet wird, ist es bereits die 9. usw. Also müssen wir für jeden Indexeintrag die bereits eingefügten Spalten dem nächsten Index zuaddieren. Was ist nun aber, wenn im Array als Reihenfolge 2, 5, 3 angegeben wurde? Für die 5. Spalte wäre es noch in Ordnung, die beiden eingefügten Spalten werden addiert, also ist es nun die 7. Aber die 3.? Wir haben bereits 4 Spalten eingefügt, aber die ursprünglich 3. Spalte ist ja nun nicht die 7., sondern die 5...

Des Rätsels Lösung ist einfach. Wir müssen nur dafür sorgen, dass die übergebenen Indizes aufsteigend sortiert werden und können sie dann nacheinander abarbeiten. Dafür haben wir oben unsere QuickSort-Routine implementiert, die wir nun mit dem Index-Array aufrufen.
Code:
        QuickSort Index, LBound(Index), UBound(Index)

Jetzt können wir Index für Index aus dem Array an die Einfüge-Routine übergeben. Wir lassen dabei eine Variable mitlaufen, die für jeden Durchgang den aktuellen Index um die bereits eingefügten Zeilen bzw. Spalten korrigiert.

Gleichzeitig ermitteln wir aus dem Rückgabewert der Einfüge-Routine die Anzahl der aufgetretenen Fehler und geben zum Schluss den entsprechenden Fehlerwert aus der XTableErr-Enumeration zurück (alles verarbeitet = Successful, teilweise verarbeitet = Faults, nichts verarbeitet = Aborted).
Code:
'
' Zeilen/Spalten hinzufügen
'
Function AddX(Key As Variant, _
              Optional What As XWhat = Column, _
              Optional Index As Variant = XIndex.Table, _
              Optional Count As Long = 1) As XTableErr

' Key:      Tabellen-Schlüssel
' What:     XWHAT-Konstante; Column: Zeile, Row: Spalte; Default: Column
' Index:    Spalten-/bzw. ZeilenIndex;
'               Default = Tabellenanfang/-ende (je nach Count +/-)
'           Wird Index als Array übergeben, werden vor/an alle
'      angegeben Spalten/Zeilen die in Count angegebene Anzahl
'      Spalten/Zeilen angefügt
' Count:    Anzahl zuzufügender Spalten/Zeilen;
'           > 0: rechts/unter Spalte/Zeile
'           < 0: links/über Spalte/Zeile
'           Default = 1
' Rückgabe: alle Spalten/Zeilen hinzugefügt = XTableErr.Successful
'           einige Spalten/Zeilen hinzugefügt = XTable.Faults
'           keine Spalten/Zeilen hinzugefügt = XTableErr.Aborted

    Dim idx
    Dim idxCnt, curIdx As Long
    Dim idxErr As Long

    If Not VarType(Index) > vbArray Then
        idxErr = doAddX(Key, What, Index, Count)
    Else
        QuickSort Index, LBound(Index), UBound(Index)
        For Each idx In Index
            curIdx = idx + idxCnt * Abs(Count)
            AddX = doAddX(Key, What, curIdx, Count)
            If Not AddX = Aborted Then _
                idxCnt = idxCnt + 1 Else idxErr = idxErr + 1
        Next
    End If
    AddX = IIf(idxErr > 0, IIf(idxErr = Abs(Count), Aborted, Faults), _
                           Successful)
End Function

Nun kommen wir zur eigentlichen Einfüge-Funktion doAddX. Wir übernehmen die gleichen Parameter, als Index jedoch nur noch einen einzelnen Wert. Zuerst holen wir die benötigte Tabelle, deren Name in Key übergeben wurde, wenn sie denn vorhanden ist. Dann prüfen wir, ob der Index in einem gültigen Bereich liegt. Ist das der Fall, müssen wir zwischen Tabelle und Zeile-/Spalten-Index unterscheiden. Bei einer Tabelle (Index = 0 resp. XIndex.Table) wird bei einer Anzahl (Count) < 0 am Tabellenanfang (ganz links bzw. ganz oben) und bei einer Anzahl > 0 am Tabellenende (ganz rechts bzw. ganz unten) angefügt. Bei Zeilen und Spalten ist es wieder etwas komplizierter, da die VBA-Add-Funktion nur vor der Zeile bzw. Spalte einfügt. Also müssen wir bei einer Anzahl > 0 den Index wieder entsprechend korrigieren.
Je nach Erfolg wird der entsprechende Fehlerwert zurückgegeben, da es sich aber nur um einen Index handelt allerdings nur, ob erfolgreich (Successful) oder nicht (Aborted). Schließlich wird die Ressource wieder freigegeben.
Code:
Private Function doAddX(Key As Variant, _
                        What As XWhat, _
                        ByVal Index As Long, _
                        Count As Long) As XTableErr

    Dim tbl As Table
    Dim idx As Long
   
    Set tbl = GetXTable(Key)
    If Not tbl Is Nothing Then
        With tbl
            If Not Index < 0 _
            And Not Index > IIf(What = Column, .Columns.Count, .Rows.Count) Then
                For idx = 1 To Abs(Count)
                    If Index = XIndex.Table Then
                        If What = Column _
                        Then .Columns.Add IIf(Count > 0, Nothing, .Columns(1)) _
                        Else .Rows.Add IIf(Count > 0, Nothing, .Rows(1))
                    Else
                        If What = Column _
                        Then .Columns.Add _
                             .Columns(Index + idx - IIf(Count < 0, 1, 0)) _
                        Else .Rows.Add .Rows(Index + idx - IIf(Count < 0, 1, 0))
                    End If
                Next
                doAddX = Successful
            Else
                doAddX = Aborted
            End If
        End With
    Else
        doAddX = Aborted
    End If
    Set tbl = Nothing
End Function

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:17
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 12 - DelX - AW: Keine Angst vor Klassen - 12 - DelX

Nach oben
       Version: Office 2010

V.1g Methode DelX

Das Gegenstück zum Einfügen ist natürlich das Entfernen, was nach dem gleichen Muster geschehen soll. Der Ablauf ist ziemlich identisch mit Addx, nur was die Problematik mit dem Korrigieren der Indizes angeht, ist es hier umgekehrt. Jetzt dürfen die Indizes nicht aufsteigend, sondern müssen absteigend verarbeitet werden. Also kommt hier die QuickSortReverse-Funktion zum Einsatz.

Hier die entsprechenden Funktionen:
Code:
'
' Zeilen/Spalten löschen
'
Function DelX(Key As Variant, _
              What As XWhat, _
              Optional Index As Variant = XIndex.Table, _
              Optional Where As XWhere = Included, _
              Optional Count As Long = 1) As XTableErr

' Key:      Tabellen-Schlüssel
' What:     XWHAT-Konstante; Column: Zeile, Row: Spalte
' Index:    Spalten-/bzw. Zeilenindex
'               Default = Tabellenanfang/-ende (je nach Count +/-)
'           Wird Index als Array übergeben, werden vor/an alle angegeben
'           Spalten/Zeilen die in Count angegebene Anzahl Spalten/Zeilen angefügt
' Where:    XWHERE-Konstante; Zeile/Spalte in Count und zum Löschen eingeschlossen;
'           Default: Included;
'           bei Index=Table (0) unberücksichtigt
' Count:    Anzahl zu entfernener Spalten/Zeilen;
'           > 0: rechts/unter Spalte/Zeile
'           < 0: links/über Spalte/Zeile
'           Default = 1
' Rückgabe: alle Spalten/Zeilen gelöscht = XTableErr.Successful
'           einige Spalten/Zeilen gelöscht = XTable.Faults
'           keine Spalten/Zeilen gelöscht = XTableErr.Aborted

    Dim idx
    Dim idxCnt, curIdx As Long
    Dim idxErr As Long

    If Not Count = 0 Then
        If Not VarType(Index) > vbArray Then
            DelX = doDelX(Key, What, Index, Where, Count)
        Else
            QuickSortReverse Index
            For Each idx In Index
                curIdx = idx - idxCnt * Abs(Count)
                DelX = doDelX(Key, What, curIdx, Where, Count)
                If DelX = Successful Then _
                    idxCnt = idxCnt + 1 Else idxErr = idxErr + 1
            Next
            DelX = IIf(idxErr > 0, IIf(idxErr = Abs(Count), Aborted, Faults), _
                                   Successful)
        End If
    End If
End Function

 
Private Function doDelX(Key As Variant, _
                 What As XWhat, _
                 ByVal Index As Long, _
                 Where As XWhere, _
                 Count As Long) As XTableErr
   
    Dim tbl As Table
    Dim idx As Long
    Dim idxErr As Long
   
    Set tbl = GetXTable(Key)
    If Not tbl Is Nothing Then
        With tbl
            If Index = XIndex.Table Then
                Index = IIf(Count > 0, IIf(What = Column, _
                                           .Columns.Count, .Rows.Count) - Count, _
                                           1 - Count)
                Where = Excluded
            End If
            For idx = IIf(Count > 0, _
                          Index + IIf(Where = Excluded, Count, Count - 1), _
                          Index - IIf(Where = Excluded, 1, 0)) _
            To IIf(Count > 0, _
                   IIf(Where = Excluded, Index + 1, Index), _
                   Index - Abs(Count) + IIf(Where = Excluded, 0, 1)) _
            Step -1
                If Not idx < -1 _
                And Not idx > IIf(What = Column, .Columns.Count, .Rows.Count) Then
                    If What = Column _
                    Then .Columns(idx).Delete _
                    Else .Rows(idx).Delete
                Else
                    idxErr = idxErr + 1
                End If
            Next
            doDelX = IIf(idx = 0, Successful, Faults)
        End With
    Else
        doDelX = Aborted
    End If
    Set tbl = Nothing
End Function

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:18
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 13 - XTableColWidth (Let) - AW: Keine Angst vor Klassen - 13 - XTableColWidth (Let)

Nach oben
       Version: Office 2010

V.2 Eigenschaften

V.2a Property XTableColWidth

Jetzt werden wir noch das Ändern von Eigenschaften für unsere Tabellen beschreiben. Wir beschränken uns in diesem Tutorial auf die Spaltenbreite, stellen aber wieder eine andere Funktionalität als die Standard-Eigenschaften von VBA zur Verfügung.

Das Ändern der Spaltenbreite wäre ja ohne weiteres mit den Standardeigenschaften möglich, denn wir können ja auf unsere Tabelle im Dictionary über den Namen zugreifen und wie gewohnt alle von VBA zur Verfügung gestellten Eigenschaften ändern:
Code:
XTables("Tabelle1").Table.Columns(1).Width = CentimetersToPoints(5)   'Spalte 2 auf 5 cm

Aber wir bräuchten keine umfangreiche Klasse zu schreiben, wenn wir uns damit zufrieden geben würden.

Wir wollen eine Funktionalität zur Verfügung stellen, die es uns ermöglicht, bestimmte Spalten oder auch alle Spalten gleichzeitig mit einer neuen Breite zu versehen.

Eigenschaften werden ähnlich wie Functions oder Subs geschrieben, beginnen aber stattdessen mit Property und je nachdem, ob eine lesbare oder beschreibbare Eigenschaft geschrieben wird, mit dem Zusatz Let bzw. Get, wobei bei Eigenschaften, die keine Werte sondern ein Objekt schreiben, Set verwendet werden muss. Um die gleiche Eigenschaft zu lesen, die auch geschrieben wird, müssen beide den identischen Namen haben. Eigenschaften, die nur mit Let bzw. Set definiert sind, sind nur beschreibbar, Eigenschaften, die nur mit Get definiert sind, sind nur lesbar. Üblicherweise werden Eigenschaften paarweise definiert, nämlich Let und Get bzw. Set und Let, weil meistens die Eigenschaften beschreibbar und lesbar sein sollen. Eigenschaften mit Let, Set und Get zu definieren macht nur für Variant-Typen Sinn, da nur diese Typen Werte oder Objekte beinhalten können.

Um also die Eigenschaften einer Tabelle aus unserem Dictionary zu ändern, schreiben wir eine Property Let, und weil wir die Breite ändern wollen, nennen wir sie XTableColWidth. Wir benötigen dafür als Parameter natürlich den Namen der Tabelle, die Maßeinheit und, sofern nur bestimmte Spalten geändert werden sollen, die Auflistung der Spalten:
Code:
Property Let XTableColWidth(Key As Variant, _
                            Unit As MeasureUnit, _
                            Optional Index As Variant, _
                            Width As Long)

Hier sehen wir einen deutlichen Unterschied zu Function- oder Sub-Deklarationen. Die letzte Parameter-Variable in der Argument-Liste beinhaltet immer den Wert, der beim Aufruf der Property rechts vom „=“-Zeichen steht! In diesem Beispiel lautet der Aufruf
Code:
XTableColWidth(Key, Unit [, Array(Indizes) | Index]) = Width

Die Property startet mit der obligatorischen Prüfung, ob es die angegebene Tabelle gibt. Da wir bei einer Zuweisung mit Property Let keinen Wert zurückgeben können, geben wir hier eine Fehlermeldung aus. Fortgeschrittene werden in solchen Fällen einen Error-Handler benutzen, aber das soll nicht Bestandteil dieses Turorials sein.

Existiert die Tabelle in unserer Namens-Auflistung, dann wird je nach Übergabe von Index jede übergebene (wenn es sie gibt) oder jede Spalte (wenn keine Werte für Index übergeben wurden bzw. XIndex.Table übergeben wurde) in der Tabelle geändert. Der Wert wird vorher gem. der übergebenen Maßeinheit von unserer Funktion cPoints in die benötigten Points umgewandelt.

Der gesamte Code dafür sieht wie folgt aus:
Code:

'
' Spaltenbreite einstellen
'
Property Let XTableColWidth(Key As Variant, _
                            Unit As MeasureUnit, _
                            Optional Index As Variant = XIndex.Table, _
                            Width As Single)
'
' Key:    Tabellen-Schlüssel;
' Unit:   Masseinheit (MEASUREUNIT-Konstante)
' Index:  optional Spaltennummer als Integer oder
'         Auflistung als Integer-Array;
'         Default=alle Spalten (wenn nicht angegeben)
' Width:  Breite gem. Unit
'
    Dim tbl As Table
    Dim colIdx As Variant
   
    Set tbl = GetXTable(Key)
    If Not tbl Is Nothing Then
        If VarType(Index) > vbArray Then
            For Each colIdx In Index
                If colIdx > 0 And colIdx <= tbl.Columns.Count Then _
                    tbl.Columns(colIdx).Width = cPoints(Width, Unit)
            Next
        Else
            tbl.Columns.Width = cPoints(Width, Unit)
        End If
    Else
        MsgBox "Tabelle " & Key & " ist nicht definiert.", _
               vbCritical, _
               "Breite festlegen"
    End If
    Set tbl = Nothing
    Set colIdx = Nothing
End Property

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:19
Rufname: Thomas
Wohnort: Celle

AW: Keine Angst vor Klassen - 14 - XTableColWidth (Get) - AW: Keine Angst vor Klassen - 14 - XTableColWidth (Get)

Nach oben
       Version: Office 2010

Um die Spaltenbreiten (fast) genauso wieder auslesen zu können, müssen wir eine zugehörige Property Get schreiben.
Code:
Property Get XTableColWidth(Key As Variant, _
                            Unit As MeasureUnit, _
                            Optional Index As Variant = XIndex.Table) As Single

Sie sieht in der Deklaration auch fast genauso aus, nur fehlt natürlich ggü. der Let-Property der letzte Parameter, da ja keine Zuweisung von einem Wert erfolgt und demzufolge auch kein Parameter RECHTS eines „=“ stehen kann. Stattdessen geben wir den Typ des Rückgabewertes (hier: Single) an. Dieser Typ muss mit dem Typ des in der Let.Property übernommenen Wertes (hier: Width) übereinstimmen! Der Aufruf hier würde lauten:
Code:
Width = XTableColWidth(Key, Unit [, Array(Indizes) | Index])

Width ist also links vom “=” und erhält, wie in einer Function, den Wert aus XTableColWidth vom Typ Single.

Das Ermitteln der Spaltenbreiten erfolgt analog zum Setzen der Spaltenbreiten, nur das wir hier keine individuellen Breiten von Spalten zurückgeben, sondern entweder die Summe der angegebenen Spalten oder aller Spalten. Um die Summe aller Spaltenbreiten zu ermitteln haben wir keine VBA-Funktionalität (wie es beim Setzen aller Spalten möglich ist) zur Verfügung, sondern müssen alle Spalten einzeln durchlaufen und summieren. Aber statt eine Fehlermeldung auszugeben (wenn es denn zu einem Fehler kommt) können wir nun wieder einen Wert aus unserer XTableErr-Enumeration zurückgeben, weil es ja einen Rückgabewert gibt.

Folgendes ist aber zu berücksichtigen: Da i.d.R. mm- oder cm-Werte ausgelesen werden, wird eine Rückwandlung der von VBA gelieferten Points erforderlich. Das erledigt unsere cPointsTo-Funktion. Allerdings kommt es hier zwangsläufig zu Rundungsdifferenzen. So ergibt z. B.
Code:
MyTables.XTableColWidth("TestTabelle", Xcm, Array(2, 3)) = 1.5   ' Spalte 2 und 3 je 1,5cm
MsgBox MyTables.XTableColWidth("TestTabelle", Xcm, Array(2, 3))  ' Summe Breiten Spalte 2+3

nicht wie erwartet 3 (cm), sondern 2,998611 !

Der Code sieht wie folgt aus:
Code:
'
' Spaltenbreite zurückgeben
'
Property Get XTableColWidth(Key As Variant, _
                            Unit As MeasureUnit, _
                            Optional Index As Variant = XIndex.Table) As Single
'
' Key:          Tabellen-Schlüssel;
' Unit:         Masseinheit (MEASUREUNIT-Konstante)
' Index:        optional Spaltennummer als Integer oder
'               Auflistung als Integer-Array;
'               Default=alle Spalten (wenn nicht angegeben)
' Rückgabe:     Breite gem. Unit
'
    Dim tbl As Table
    Dim col As Variant
    Dim colIdx As Variant
    Dim lngPoints As Double
   
    Set tbl = GetXTable(Key)
    If Not tbl Is Nothing Then
        If VarType(Index) > vbArray Then
            For Each colIdx In Index
                If colIdx > 0 And colIdx <= tbl.Columns.Count Then _
                    lngPoints = lngPoints + tbl.Columns(colIdx).Width
            Next
        Else
            For Each col In tbl.Columns
                lngPoints = lngPoints + col.Width
            Next
        End If
        XTableColWidth = cPointsTo(Unit, lngPoints)
    Else
        XTableColWidth = XTableErr.Undefined
    End If
    Set tbl = Nothing
    Set col = Nothing
    Set colIdx = Nothing
End Property

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
KeepCoolMan
VBA-NonExpert ;) Office 2010


Verfasst am:
17. Nov 2010, 21:20
Rufname: Thomas
Wohnort: Celle


AW: Keine Angst vor Klassen - 15 - Last - AW: Keine Angst vor Klassen - 15 - Last

Nach oben
       Version: Office 2010

V.2b Property Last

Als letzte Eigenschaft möchte ich exemplarisch noch die Möglichkeit aufzeigen, mittels XWhat-Enumeration den Index der letzten Zeile bzw. Spalte zurückzugeben.

Hier übernehmen wir natürlich wieder den Tabellennamen (Key) und ob wir den Index der letzten Spalte oder Zeile erwarten (What). Nach der Prüfung, ob die Tabelle existiert, wird entweder der Index oder aber ein Fehlerwert zurückgegeben.
Code:
'
' Index der letzten Zeile/Spalte zurückgeben
'
Property Get Last(Key As Variant, _
                  What As XWhat) As Long
   
    Dim tbl As Table
   
    Set tbl = GetXTable(Key)
    If Not tbl Is Nothing Then
        Last = IIf(What = Column, tbl.Columns.Count, tbl.Rows.Count)
    Else
        Last = XTableErr.Undefined
    End If

End Property 

_________________
Gruß Thomas

Ich freue mich über Feedback, Kritik und Verbesserungsvorschläge...
Wer will, findet Möglichkeiten; wer nicht will, findet Gründe! :: Unt wär Rächdshraibfela fint, daaf sie behaltn!
Neues Thema eröffnen   Neue Antwort erstellen Alle Zeiten sind
GMT + 1 Stunde

Gehe zu Seite 1, 2  Weiter
Diese Seite Freunden empfehlen

Seite 1 von 2
Gehe zu:  
Du kannst Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.
Du kannst Dateien in diesem Forum nicht posten
Du kannst Dateien in diesem Forum herunterladen

Verwandte Themen
Forum / Themen   Antworten   Autor   Aufrufe   Letzter Beitrag 
Keine neuen Beiträge Word Hilfe: Tagesordnungspunkte Übersicht mit Punkten verbinden 1 Lioness 310 10. Sep 2012, 09:48
Lioness Tagesordnungspunkte Übersicht mit Punkten verbinden
Keine neuen Beiträge Word VBA Programmierung (Makros): Events in Klassen - WindowSelectionChange geht nicht 1 Keen Tied 252 05. Jun 2012, 12:54
Keen Tied Events in Klassen - WindowSelectionChange geht nicht
Keine neuen Beiträge Word Hilfe: gesucht: Übersicht zu Aufzählung, Liste, Gliederung 4 Elmar77 912 27. März 2011, 19:13
Elmar77 gesucht: Übersicht zu Aufzählung, Liste, Gliederung
Keine neuen Beiträge [Umfrage] Word Tipps & Tricks: Keine Angst vor Klassen 1 KeepCoolMan 2235 03. Nov 2010, 16:56
KeepCoolMan Keine Angst vor Klassen
Keine neuen Beiträge Word Hilfe: [Word] Wie ist meine Übersicht darstellbar ? 3 manuhu 416 19. Mai 2010, 12:34
Franzisk@ [Word] Wie ist meine Übersicht darstellbar ?
Keine neuen Beiträge Word Hilfe: Ist es möglich eine Übersicht der Docvariablen einzusehen? 1 AndréN 2260 28. Apr 2010, 16:46
Hubert_R Ist es möglich eine Übersicht der Docvariablen einzusehen?
Keine neuen Beiträge Word Gestaltungselemente: Übersicht erstellen in Word ??? 3 Sunny84 1479 25. Jan 2009, 23:43
CHF Übersicht erstellen in Word ???
Keine neuen Beiträge Word VBA Programmierung (Makros): Übersicht (viele) Dokumentvorlagen 4 Matthias_ 498 19. Aug 2008, 19:44
Lisa Übersicht (viele) Dokumentvorlagen
Keine neuen Beiträge Word VBA Programmierung (Makros): Übersicht der gedruckten Dateien 6 *Knut* 4247 30. Jul 2008, 15:25
Bitmixer Übersicht der gedruckten Dateien
Keine neuen Beiträge Word Serienbriefe: Übersicht Serienbrief-Empfänger 1 elduchte 1282 06. Jun 2008, 16:02
Maulende Myrte Übersicht Serienbrief-Empfänger
Keine neuen Beiträge Word Formate: Tools Übersicht Erklärung 1 xenone 861 19. Mai 2008, 11:08
CaBe Tools Übersicht Erklärung
Keine neuen Beiträge Word Gestaltungselemente: Mit Hyperlink zur Übersicht zurück gehen und Datei schließen 14 ThomasH. 2266 27. Feb 2008, 14:17
ThomasH. Mit Hyperlink zur Übersicht zurück gehen und Datei schließen
 

----> Diese Seite Freunden empfehlen <------ Impressum - Besuchen Sie auch: Microsoft Project