# PowerShell Programmierung

Basics über das Programmieren in und mit Powershell

# Sprache

Die Sprache ist das Fundament von PowerShell. Variablen, Operatoren, Strings und andere Sprachelemente bestimmen, was dein Code tut und warum er manchmal etwas völlig anderes tut als erwartet. Wenn du verstehen willst, wie PowerShell denkt, beginnt die Reise hier.

# Escape-Sequenzen

Escape-Sequenzen sind spezielle Zeichenkombinationen, mit denen sich **Steuerzeichen** innerhalb von Strings darstellen lassen, z. B. Zeilenumbrüche oder Tabs.

In PowerShell wird dafür das **Backtick-Zeichen** verwendet:

```
`
```

---

## **Grundlagen**

Escape-Sequenzen funktionieren **nur in doppelt-quoted Strings**:

```powershell
"Text`nNeue Zeile"   # funktioniert
'Text`nNeue Zeile'   # kein Effekt
```

---

## Wichtige Escape-Sequenzen

<table id="bkmrk-sequenz-bedeutung-be"><thead><tr><th>Sequenz</th><th>Bedeutung</th><th>Beschreibung</th></tr></thead><tbody><tr><td>``n`</td><td>New Line</td><td>Zeilenumbruch</td></tr><tr><td>``r`</td><td>Carriage Return</td><td>Cursor an den Anfang der Zeile</td></tr><tr><td>``r`n`</td><td>Windows-Zeilenumbruch</td><td>Kombination aus CR + LF</td></tr><tr><td>``t`</td><td>Tab</td><td>Tabulator</td></tr><tr><td>``</td><td>Backtick</td><td>Gibt ein Backtick-Zeichen aus</td></tr><tr><td>``"`</td><td>Anführungszeichen</td><td>Doppelte Quotes innerhalb von Strings</td></tr></tbody></table>

---

## **Beispiele**

### Zeilenumbruch

```powershell
"Hallo`nWelt"
```

Ausgabe:

```
Hallo
Welt
```

---

### Tabulator

```powershell
"Name`tAlter"
```

Ausgabe:

```
Name    Alter
```

---

### Windows-Zeilenumbruch

```powershell
"Hallo`r`nWelt"
```

---

## Verhalten in Dateien

Beim Arbeiten mit Dateien (z. B. `Out-File`, `Set-Content`) wird häufig ``r`n` verwendet, da dies dem Windows-Standard entspricht.

---

## **Typische Stolperfallen**

### 1. Falsche Anführungszeichen

```powershell
'Hallo`nWelt'   # ❌ kein Umbruch
```

### 2. Backtick übersehen

Das Escape-Zeichen ist **kein Apostroph (`'`)**, sondern:

```
`
```

### 3. Unsichtbare Zeichen

Escape-Sequenzen sind nicht sichtbar im Code, wirken aber auf die Ausgabe. Das macht Debugging manchmal… sagen wir… charakterbildend.

---

## **Alternative Methoden**

### .Split() (kein Escape, aber oft verwandt)

```powershell
"text1,text2" -split ","
```

Oder:

```powershell
"text1,text2".Split(",")

```

`.Split()` ist eine **.NET String-Methode**, kein Escape-Mechanismus, wird aber oft im gleichen Kontext verwendet, wenn Strings verarbeitet werden.

---

## Fazit

Escape-Sequenzen sind ein einfacher Weg, um Strings zu formatieren, ohne sie über mehrere Zeilen schreiben zu müssen.

Das wichtigste:

- Backtick = Escape
- Nur in `" "` aktiv
- ``n` für schnellen Zeilenumbruch
- ``r`n` für Windows-Kompatibilität

# Enum-Werte

Enum-Werte bedeuten vordefinierte mögliche Werte

### `[System.Environment+SpecialFolder]`

<table border="1" id="bkmrk-wert-bedeutung-deskt" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 49.9404%;"></col></colgroup><tbody><tr><td>Wert</td><td>Bedeutung</td></tr><tr><td>`Desktop`</td><td>**Desktop** – Der Desktop vom aktuellen Benutzer</td></tr><tr><td>`MyComputer`</td><td>**Dieser PC** – beinhaltet Datenträger und Laufwerke</td></tr><tr><td>`Programs`</td><td>**Startmenü Programme** – Im Startmenü angezeigten Programme</td></tr><tr><td>  
</td><td>  
</td></tr></tbody></table>

MyDocuments  
Personal  
Favorites  
Startup  
Recent  
SendTo  
StartMenu  
MyMusic  
MyVideos  
DesktopDirectory  
NetworkShortcuts  
Fonts  
Templates  
CommonStartMenu  
CommonPrograms  
CommonStartup  
CommonDesktopDirectory  
ApplicationData  
PrinterShortcuts  
LocalApplicationData  
InternetCache  
Cookies  
History  
CommonApplicationData  
Windows  
System  
ProgramFiles  
MyPictures  
UserProfile  
SystemX86  
ProgramFilesX86  
CommonProgramFiles  
CommonProgramFilesX86  
CommonTemplates  
CommonDocuments  
CommonAdminTools  
AdminTools  
CommonMusic  
CommonPictures  
CommonVideos  
Resources  
LocalizedResources  
CommonOemLinks  
CDBurning

<div id="bkmrk-" style="color: #cccac2; background-color: #242936; font-family: Consolas, 'Courier New', monospace; font-weight: normal; font-size: 14px; line-height: 20px; white-space: pre;"></div><div id="bkmrk-x-%28x%29" style="color: rgb(0, 0, 0); font-size: 16px; line-height: 16px; background-color: rgb(247, 247, 247); border: 1px solid rgb(153, 153, 153); border-radius: 1px; top: 1px; left: 1px;"><div class="ir-ext-dimensions"><span class="ir-ext-rendered" title="Rendered image dimensions (after any scaling/resizing has been applied)"> x </span> <span class="ir-ext-natural" title="Natural image dimensions (without applying any scaling/resizing)"> (x) </span></div><div class="ir-ext-filesize"></div></div>

# PowerShell Variablen

Variablen die von PowerShell je nach Kontext, selbst deklariert werden.

<table border="1" id="bkmrk-variable-beschreibun" style="border-collapse: collapse; width: 100%; height: 300px;"><colgroup><col style="width: 19.1895%;"></col><col style="width: 80.8105%;"></col></colgroup><thead><tr style="height: 29.8px;"><td class="align-center" style="height: 29.8px;">**Variable**</td><td class="align-center" style="height: 29.8px; padding-left: 40px;">**Beschreibung**</td></tr></thead><tbody><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;"><span style="color: rgb(53, 152, 219);">`$PSCommandPath`</span></td><td style="height: 30.4667px;">Vollständiger Pfad zum aktuellen Skript</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;"><span style="color: rgb(35, 111, 161);">`<span style="color: rgb(53, 152, 219);">$PSModulePath</span>`</span></td><td style="height: 30.4667px;">Beinhaltet die Pfade zu den Modulen</td></tr><tr><td class="align-center" style="height: 30.4667px;">`<span style="color: rgb(53, 152, 219);">$PSScriptRoot</span>`</td><td style="height: 30.4667px;">Der Pfad zum Ordner worin sich das aktuelle Skript befindet</td></tr><tr style="height: 29.8px;"><td class="align-center" style="height: 29.8px;">`<span style="color: rgb(53, 152, 219);">$MyInvocation</span>`</td><td style="height: 29.8px;">Enthält Infos darüber, wie das Skript aufgerufen wurde</td></tr></tbody></table>

### <span style="color: rgb(53, 152, 219);">`$PSScriptRoot`</span>

- **Datentyp:** <span style="color: rgb(185, 106, 217);">`System.String`</span>

### <span style="color: rgb(53, 152, 219);">`$PSCommandPath`</span>

- **Datentyp:** <span style="color: rgb(185, 106, 217);">`System.String`</span>

### <span style="color: rgb(53, 152, 219);">`$PSScriptRoot`</span>

- **Datentyp:**

### <span style="color: rgb(35, 111, 161);">`<span style="color: rgb(53, 152, 219);">$MyInvocation</span>`</span>

- **Datentyp:** <span style="color: rgb(251, 238, 184);">`InvocationInfo`</span>

Enthält Infos darüber, wie das Skript aufgerufen wurde

- <span style="color: rgb(255, 255, 255);">`<span style="color: rgb(53, 152, 219);">$MyInvocation</span>.<span style="color: rgb(251, 238, 184);">MyCommand</span>` </span>– Informationen und die Idendität des aktuellen Kommando-Objekts
- - <span style="color: rgb(255, 255, 255);">`<span style="color: rgb(53, 152, 219);">$MyInvocation</span>.<span style="color: rgb(251, 238, 184);">MyCommand.CommandType</span>`</span>
    - <span style="color: rgb(255, 255, 255);">`<span style="color: rgb(53, 152, 219);">$MyInvocation</span>.<span style="color: rgb(251, 238, 184);">MyCommand.Path</span>` </span>– Enthält den Pfad zum aktuellen Skript

# Operatoren

Operatoren sind spezielle Sprachelemente in PowerShell, die Werte verarbeiten, vergleichen, verknüpfen oder verändern. Sie bilden einen zentralen Bestandteil der Sprache und werden in nahezu jedem Skript verwendet.

Je nach Typ übernehmen Operatoren unterschiedliche Aufgaben:

- Werte vergleichen (`-eq`, `-ne`, `-gt`, ...)
- Logische Bedingungen verknüpfen (`-and`, `-or`, `-not`)
- Inhalte zusammenführen oder aufteilen (`-join`, `-split`)
- Berechnungen durchführen (`+`, `-`, `*`, `/`)

## String- und Array-Operatoren

Diese Operatoren werden häufig verwendet, um Texte und Listen zu verarbeiten.

### Split

Der Operator `-split` zerlegt einen String anhand eines Trennzeichens in mehrere Elemente eines Arrays.

Beispiel:

```powershell
"Max;Mustermann;30" -split ";"

```

Ergebnis:

```powershell
Max
Mustermann
30

```

Weitere Informationen findest du auf der Seite **Split**.

---

### Join

Der Operator `-join` führt mehrere Elemente eines Arrays zu einem einzelnen String zusammen.

Beispiel:

```powershell
"Max","Mustermann",30 -join ";"

```

Ergebnis:

```powershell
Max;Mustermann;30

```

Weitere Informationen findest du auf der Seite **Join**.

---

## Merksatz

`-split` macht aus einem String ein Array.

`-join` macht aus einem Array einen String.

Beide Operatoren werden häufig gemeinsam verwendet, wenn Daten eingelesen, verarbeitet und anschließend wieder ausgegeben werden.

# Join

Der `-join` Operator in PowerShell wird verwendet, um mehrere Elemente (z. B. Strings in einem Array) zu einem einzigen String zusammenzufügen.

---

## 🧠 Syntax

```powershell
<array> -join <delimiter>

```

Oder ohne Trennzeichen:

```powershell
<array> -join

```

---

## 📌 Grundlagen

- Gibt immer einen **String (`System.String`)** zurück
- Verbindet alle Elemente eines Arrays **in der gegebenen Reihenfolge**
- Der Delimiter wird **wörtlich verwendet** (kein Regex!)
- Standard-Delimiter ist ein **leerer String** (`""`)

---

## 🔹 Beispiele

### Einfaches Zusammenfügen

```powershell
$array = "Apfel","Birne","Banane"
$result = $array -join ","

```

**Ergebnis:**

```powershell
Apfel,Birne,Banane

```

---

### Join ohne Trennzeichen

```powershell
$array = "Hallo","Welt"
$result = $array -join

```

**Ergebnis:**

```powershell
HalloWelt

```

---

### Join mit Leerzeichen

```powershell
$array = "Das","ist","ein","Test"
$result = $array -join " "

```

**Ergebnis:**

```powershell
Das ist ein Test

```

---

### Join mit Zeilenumbruch

```powershell
$array = "Zeile1","Zeile2","Zeile3"
$result = $array -join "`n"

```

**Ergebnis:**

```powershell
Zeile1
Zeile2
Zeile3

```

---

## ⚠️ Wichtige Hinweise

### 1. Kein Regex

**Im Gegensatz zu `<a href="https://doku.borinas.com/books/powershell-programmierung/page/split" title="Split">-split</a>`:**  
Der Delimiter wird **nicht** als [regulärer Ausdruck](https://doku.borinas.com/books/webseiten-entwicklung/page/regularer-ausdruck-regex "Regulärer Ausdruck (RegEx)") interpretiert.

Das bedeutet:

- Sonderzeichen haben **keine spezielle Bedeutung**
- `"."` ist einfach ein Punkt, kein Platzhalter

---

### 2. `$null`-Werte

```powershell
$array = "A",$null,"B"
$result = $array -join ","

```

**Ergebnis:**

```powershell
A,,B

```

**Erklärung:**

- `$null` wird als **leerer String** behandelt
- Der Delimiter wird trotzdem eingefügt

---

### 3. Nicht-String-Werte

```powershell
$array = 1,2,3
$result = $array -join "-"

```

**Ergebnis:**

```powershell
1-2-3

```

**Erklärung:**

- Alle Elemente werden **automatisch in Strings konvertiert**

---

## 🔄 Alternative Methoden

### `[string]::Join()`

```powershell
[string]::Join(",", $array)

```

**Unterschied zu `-join`:**

- Methodenaufruf statt Operator
- Gleiche Funktionalität, oft in .NET-Kontexten verwendet

---

## 🧩 Typische Anwendungsfälle

- Array in einen String umwandeln
- CSV-Zeilen erzeugen
- Textausgaben formatieren
- Mehrzeilige Strings erzeugen

---

## 🧪 Mini-Beispiel aus der Praxis

```powershell
$user = "Max","Mustermann",30
$csv = $user -join ";"

```

**Ergebnis:**

```powershell
Max;Mustermann;30

```

---

## 🧠 Merksatz

> `-join` klebt alles zusammen. Ohne Struktur, wenn du keinen Delimiter setzt.

# Split

Der `-split` Operator in PowerShell wird verwendet, um einen String anhand eines Trennzeichens (Delimiter) in ein Array von Teilstrings aufzuteilen.

---

## 🧠 Syntax

```powershell
<string> -split <delimiter>

```

Oder mit erweiterten Optionen:

```powershell
<string> -split <delimiter>, <max-substrings>, <options>

```

---

## 📌 Grundlagen

- Gibt immer ein **Array (`System.String[]`)** zurück
- Der **Delimiter ist ein [regulärer Ausdruck](https://doku.borinas.com/books/webseiten-entwicklung/page/regularer-ausdruck-regex "Regulärer Ausdruck (RegEx)") (RegEx!)**, kein einfacher Text
- Groß-/Kleinschreibung kann beeinflusst werden

---

## 🔹 Beispiele

### Einfaches Splitten

```powershell
$text = "Apfel,Birne,Banane"
$result = $text -split ","

```

**Ergebnis:**

```powershell
Apfel
Birne
Banane

```

---

### Split mit Leerzeichen

```powershell
$text = "Das ist ein Test"
$result = $text -split " "

```

---

### Mehrere Trennzeichen (Regex)

```powershell
$text = "Apfel;Birne,Banane"
$result = $text -split "[,;]"

```

**Erklärung:**

- `[ , ; ]` bedeutet: splitte bei Komma **oder** Semikolon

---

### 💡 Verhalten bei `max-substrings`

Wenn die maximale Anzahl an Elementen erreicht ist:

> **Das letzte Element im Ergebnis enthält den gesamten verbleibenden Rest des Strings und wird nicht weiter gesplittet.**

```powershell
$text = "A,B,C,D"
$result = $text -split ",", 2

```

**Ergebnis:**

```powershell
A
B,C,D

```

**Erklärung:**

- Es werden maximal **2 Elemente** erzeugt
- Das erste Element entsteht durch den ersten Split
- Das zweite Element enthält **alles, was danach noch übrig ist** (`B,C,D`)

---

### 🧩Split mit Optionen

```powershell
$text = "a,b,c"
$result = $text -split ",", 0, "IgnoreCase"

```

#### Mögliche Optionen:

- `IgnoreCase`  
    → Groß- und Kleinschreibung wird ignoriert  
    → `"A,B,C" -split "a"` würde trotzdem funktionieren
- `CaseSensitive`  
    → Groß- und Kleinschreibung wird beachtet  
    → `"A,B,C" -split "a"` liefert **kein Ergebnis**, da kein Match
- `SimpleMatch`  
    → Der Delimiter wird **als normaler Text behandelt, nicht als Regex**  
    → Sonderzeichen wie `.`, `*`, `+` verlieren ihre Regex-Bedeutung
    
    Beispiel:
    
    ```powershell
    "1.2.3" -split ".", 0, "SimpleMatch"
    
    ```
    
    → funktioniert ohne Escape (`\.`)

---

## ⚠️ Wichtige Hinweise

### 1. Regex-Falle

```powershell
$text = "1.2.3"
$result = $text -split "."

```

**Problem:**  
`.` bedeutet im Regex „beliebiges Zeichen“

➡️ Ergebnis: komplett zerlegt

**Lösung:**

```powershell
$result = $text -split "\."

```

---

### 🗑 2. Leere Elemente

```powershell
"text,,text" -split ","

```

**Ergebnis:**

```powershell
text

text

```

**Erklärung:**

- Jeder Teilstring entsteht **zwischen zwei Trennzeichen (Delimiter)**
- Wenn zwei Delimiter direkt aufeinander folgen (`,,`), liegt dazwischen **kein Inhalt**
- PowerShell erzeugt dafür trotzdem ein Element im Array

> Dieses Element ist ein **leerer String (`""`)**, kein `null`

---

## 🔄 Alternative Methoden

### `.Split()` (kein Regex!)

```powershell
$text = "A,B,C"
$result = $text.Split(",")

```

**Unterschied zu `-split`:**

- `.Split()` verwendet **kein Regex**
- oft schneller und einfacher
- weniger flexibel

---

### `[regex]::Split()`

```powershell
[regex]::Split("A,B,C", ",")

```

➡️ Alternative mit explizitem Regex-Handling

---

## 🧩 Typische Anwendungsfälle

- CSV-Daten zerlegen
- Log-Dateien parsen
- Benutzereingaben aufteilen
- Pfade oder Listen verarbeiten

---

## 🧪 Mini-Beispiel aus der Praxis

```powershell
$csv = "Max;Mustermann;30"
$name = $csv -split ";"

$vorname = $name[0]
$nachname = $name[1]
$alter = $name[2]

```

---

## 🧠 Merksatz

> `-split` denkt in Regex. Wenn du das vergisst, bekommst du Chaos statt Struktur.

# Befehle

# Start-Sleep

Das Cmdlet `Start-Sleep` pausiert die Ausführung eines Skripts oder einer Befehlssequenz für eine definierte Zeitspanne.

Es wird häufig verwendet, um:

- Wartezeiten einzubauen
- Prozesse zu synchronisieren
- Ressourcen zu schonen (z. B. bei Schleifen)

---

## 🔧 Syntax

```powershell
Start-Sleep [-Seconds] <Double> [<CommonParameters>]

Start-Sleep -Milliseconds <Int32> [<CommonParameters>]

```

---

## 📌 Parameter

### `-Seconds`

- **Typ:** `Double`
- **Pflicht:** Ja (wenn `-Milliseconds` nicht verwendet wird)
- Gibt die Wartezeit in Sekunden an
- Dezimalwerte sind erlaubt

```powershell
Start-Sleep -Seconds 2.5

```

---

### `-Milliseconds`

- **Typ:** `Int32`
- **Pflicht:** Ja (wenn `-Seconds` nicht verwendet wird)
- Gibt die Wartezeit in Millisekunden an

```powershell
Start-Sleep -Milliseconds 500

```

---

## ⚠️ Hinweise zur Verwendung

- `-Seconds` und `-Milliseconds` **dürfen nicht gleichzeitig verwendet werden**
- Während der Ausführung blockiert `Start-Sleep` den aktuellen Thread vollständig
- Es erfolgt **keine Hintergrundverarbeitung** während der Pause

---

## 📊 Verhalten

<table id="bkmrk-eigenschaft-beschrei"><thead><tr><th>Eigenschaft</th><th>Beschreibung</th></tr></thead><tbody><tr><td>Blockierend</td><td>Ja</td></tr><tr><td>Rückgabewert</td><td>Keiner</td></tr><tr><td>Thread-Verhalten</td><td>Aktueller Thread wird pausiert</td></tr><tr><td>Genauigkeit</td><td>Abhängig vom System-Timer</td></tr></tbody></table>

---

## 🧪 Beispiele

### Einfaches Warten

```powershell
Write-Host "Start"
Start-Sleep -Seconds 2
Write-Host "Ende"

```

---

### Verwendung in Schleifen

```powershell
for ($i = 1; $i -le 5; $i++) {
    Write-Host "Durchlauf $i"
    Start-Sleep -Seconds 1
}

```

---

### Kurze Pause in Millisekunden

```powershell
Start-Sleep -Milliseconds 200

```

---

### Dynamische Wartezeit

```powershell
$delay = 1.5
Start-Sleep -Seconds $delay

```

---

## ⚙️ Typische Anwendungsfälle

- Polling (z. B. auf Datei oder Prozess warten)
- Debugging (Abläufe verlangsamen)
- UI-Skripte (z. B. kurz warten, bis Controls geladen sind)
- API-Rate-Limiting

---

## ❗ Alternativen / Ergänzungen

### `[System.Threading.Thread]::Sleep()`

```powershell
[System.Threading.Thread]::Sleep(1000)

```

**Unterschiede:**

- Arbeitet nur mit Millisekunden
- Weniger PowerShell-idiomatisch
- Kein Support für `CommonParameters`

---

### `Start-Sleep` vs. `Start-Job`

<table id="bkmrk-szenario-empfehlung-"><thead><tr><th>Szenario</th><th>Empfehlung</th></tr></thead><tbody><tr><td>Einfaches Warten</td><td>`Start-Sleep`</td></tr><tr><td>Asynchrone Ausführung</td><td>`Start-Job`</td></tr><tr><td>UI-Responsiveness wichtig</td><td>Kein `Start-Sleep`</td></tr></tbody></table>

---

## 🚫 Typische Fehler

### 1. Beide Parameter gleichzeitig verwenden

```powershell
# ❌ Falsch
Start-Sleep -Seconds 1 -Milliseconds 500

```

---

### 2. UI einfrieren

```powershell
# ❌ Problematisch in WinForms/WPF
Start-Sleep -Seconds 5

```

➡️ Blockiert die UI komplett

---

### 3. Zu kurze Wartezeiten erwarten hohe Präzision

```powershell
Start-Sleep -Milliseconds 1

```

➡️ Systembedingt oft ungenau

---

## 🧠 Best Practices

- Für UI-Anwendungen besser Timer verwenden statt `Start-Sleep`
- In Schleifen immer bewusst einsetzen, um CPU-Last zu reduzieren
- Wartezeiten so kurz wie möglich halten
- Für präzise Zeitsteuerung ggf. andere Mechanismen nutzen

---

Wenn du das Ding in einer WinForms-App benutzt, dann frierst du dir halt elegant dein UI ein und wunderst dich danach, warum alles tot wirkt. Klassischer Anfänger-Move, aber immerhin ein lehrreicher.

# Join-Path

Das Cmdlet `Join-Path` kombiniert mehrere Pfadsegmente zu einem gültigen Dateisystempfad.

Es wird verwendet, um:

- Pfade plattformunabhängig zusammenzusetzen
- manuelles Hantieren mit Trennzeichen (`\` oder `/`) zu vermeiden
- Fehler durch doppelte oder fehlende Separatoren zu verhindern

---

## 🔧 Syntax

    ```powershell
Join-Path [-Path] <String> [-ChildPath] <String> [[-AdditionalChildPath] <String[]>] [<CommonParameters>]
```


#### `-Path`

- **Typ:** `String`
- **Pflicht:** Ja
- Basispfad (z. B. Verzeichnis)

#### `-ChildPath`

- **Typ:** `String`
- **Pflicht:** Ja
- Pfadsegment, das an `-Path` angehängt wird

    ```powershell
Join-Path -Path "C:\Temp" -ChildPath "Datei.txt"
```


#### `-AdditionalChildPath`

<p class="callout info">*Verfügbar ab: **PowerShell 6+***</p>

- **Typ:** `String[]`
- **Pflicht:** Nein
- Weitere Pfadsegmente, die nacheinander angehängt werden

    ```powershell
Join-Path -Path "C:\Temp" -ChildPath "Logs" -AdditionalChildPath "2026","April"
```


---

## ⚠️ Hinweise zur Verwendung

- Trennzeichen werden automatisch korrekt gesetzt (kein manuelles `\` nötig)
- Funktioniert providerübergreifend (z. B. Registry, Zertifikate)
- Mehrere Segmente werden sauber zusammengeführt
- Bestehende Trennzeichen im Input werden berücksichtigt (keine doppelten `\\`)

---

## 📊 Verhalten

<table id="bkmrk-eigenschaft-beschrei"><thead><tr><th>Eigenschaft</th><th>Beschreibung</th></tr></thead><tbody><tr><td>Plattformabhängigkeit</td><td>Berücksichtigt das jeweilige Dateisystem</td></tr><tr><td>Rückgabewert</td><td>`String` (zusammengesetzter Pfad)</td></tr><tr><td>Validierung</td><td>Keine Existenzprüfung des Pfades</td></tr><tr><td>Separator-Handling</td><td>Automatisch korrekt</td></tr></tbody></table>

---

## 🧪 Beispiele

### Einfaches Zusammenfügen

```powershell
Join-Path -Path "C:\Temp" -ChildPath "Datei.txt"
```

Ergebnis: `C:\Temp\Datei.txt`

---

### Mehrere Segmente

```powershell
Join-Path -Path "C:\Temp" -ChildPath "Logs" -AdditionalChildPath "2026","April"
```

Ergebnis: `C:\Temp\Logs\2026\April`

---

### Mit Variablen

```powershell
$base = "C:\Temp"
$file = "report.txt"

Join-Path -Path $base -ChildPath $file
```

Ergebnis: `C:\Temp\report.txt`

---

### Provider-unabhängig (z. B. Registry)

```powershell
Join-Path -Path "HKCU:\Software" -ChildPath "Microsoft"
```

---

## ⚙️ Typische Anwendungsfälle

- Dynamische Dateipfade erstellen
- Arbeiten mit temporären Verzeichnissen
- Plattformunabhängige Skripte schreiben
- Zusammenbau von Registry-Pfaden

---

## ❗ Alternativen / Ergänzungen

### `String`-Konkatenation

```powershell
# ❌ Fehleranfällig
"C:\Temp\" + "Datei.txt"
```

- Fehleranfällig bei fehlenden oder doppelten Trennzeichen
- Nicht plattformunabhängig

---

### `[System.IO.Path]::Combine()`

```powershell
[System.IO.Path]::Combine("C:\Temp", "Datei.txt")
```

**Unterschiede:**

- .NET-Methode, nicht PowerShell-spezifisch
- Keine Provider-Unterstützung (nur Dateisystem)
- Kein Support für `CommonParameters`

---

## 🧠 Best Practices

- Immer `Join-Path` statt String-Konkatenation verwenden
- Ab PowerShell 6 kann für mehrere Segmente `-AdditionalChildPath` verwendet werden.
- Kombination mit `Test-Path` für Existenzprüfung
- Variablen statt Hardcoding verwenden

---

Wenn du Pfade immer noch per String zusammenklebst, dann sabotierst du dich halt selbst mit Ansage. Funktioniert kurz, bricht später, und dann suchst du den Fehler wie ein Detektiv ohne Kaffee. Nimm einfach `Join-Path` und erspar dir das Drama.

# Set-Alias

Das Cmdlet `Set-Alias` erstellt oder verändert einen Alias für ein Cmdlet, eine Funktion oder einen Befehl.

Ein Alias ist dabei einfach ein alternativer Kurzname für einen bestehenden Command.

Es wird häufig verwendet, um:

- häufig genutzte Befehle schneller aufzurufen
- eigene Kurzbefehle zu definieren
- bestehende Aliase umzubiegen oder anzupassen
- Shell-Umgebungen individueller zu gestalten

---

## 🔧 Syntax

```powershell
Set-Alias [-Name] <String> [-Value] <String>
          [-Description <String>]
          [-Option <ScopedItemOptions>]
          [-PassThru]
          [-Scope <String>]
          [-Force]
          [-WhatIf]
          [-Confirm]
          [<CommonParameters>]

```

---

## 📌 Parameter

### `-Name`

- **Typ:** `String`
- **Pflicht:** Ja
- Name des Alias

```powershell
Set-Alias -Name ll -Value Get-ChildItem

```

---

### `-Value`

- **Typ:** `String`
- **Pflicht:** Ja
- Zielbefehl, auf den der Alias zeigen soll

```powershell
Set-Alias -Name edit -Value notepad

```

---

### `-Description`

- **Typ:** `String`
- **Pflicht:** Nein
- Fügt dem Alias eine Beschreibung hinzu

```powershell
Set-Alias -Name gs -Value Get-Service -Description "Listet Dienste auf"

```

---

### `-Option`

- **Typ:** `ScopedItemOptions`
- **Pflicht:** Nein
- Steuert das Verhalten des Alias

Mögliche Optionen:

<table id="bkmrk-option-bedeutung-non"><thead><tr><th>Option</th><th>Bedeutung</th></tr></thead><tbody><tr><td>`None`</td><td>Keine besondere Einschränkung</td></tr><tr><td>`ReadOnly`</td><td>Alias kann nur mit `-Force` geändert werden</td></tr><tr><td>`Constant`</td><td>Alias kann gar nicht mehr geändert werden</td></tr><tr><td>`Private`</td><td>Nur im aktuellen Scope sichtbar</td></tr></tbody></table>

```powershell
Set-Alias -Name test -Value Get-Date -Option ReadOnly

```

---

### `-PassThru`

- Gibt das erstellte Alias-Objekt zurück

```powershell
Set-Alias -Name now -Value Get-Date -PassThru

```

---

### `-Scope`

- Legt fest, in welchem Scope der Alias existiert

Beispiele:

<table id="bkmrk-scope-bedeutung-loca"><thead><tr><th>Scope</th><th>Bedeutung</th></tr></thead><tbody><tr><td>`Local`</td><td>Nur aktueller Scope</td></tr><tr><td>`Global`</td><td>Überall verfügbar</td></tr><tr><td>`Script`</td><td>Nur innerhalb des Skripts</td></tr></tbody></table>

```powershell
Set-Alias -Name ll -Value Get-ChildItem -Scope Global

```

---

### `-Force`

- Erzwingt das Überschreiben von `ReadOnly`-Aliases

```powershell
Set-Alias -Name ls -Value Get-Process -Force

```

---

## ⚠️ Hinweise zur Verwendung

- Aliase speichern **keine Parameter**
- Ein Alias ersetzt nur den Befehlsnamen
- Aliase gelten standardmäßig nur für die aktuelle Sitzung
- Für dauerhafte Aliase muss man sie ins Profil (`$PROFILE`) schreiben
- `Constant`-Aliases können nicht mehr entfernt werden

---

## 📊 Verhalten

<table id="bkmrk-eigenschaft-beschrei"><thead><tr><th>Eigenschaft</th><th>Beschreibung</th></tr></thead><tbody><tr><td>Rückgabewert</td><td>Standardmäßig keiner</td></tr><tr><td>Überschreibbar</td><td>Ja, außer `Constant`</td></tr><tr><td>Persistenz</td><td>Nur aktuelle Sitzung</td></tr><tr><td>Unterstützt Funktionen</td><td>Ja</td></tr><tr><td>Unterstützt EXE-Dateien</td><td>Ja</td></tr></tbody></table>

---

## 🧪 Beispiele

### Einfachen Alias erstellen

```powershell
Set-Alias -Name ll -Value Get-ChildItem

```

Jetzt funktioniert:

```powershell
ll

```

---

### Alias für Programme

```powershell
Set-Alias -Name np -Value notepad

```

---

### Bestehenden Alias überschreiben

```powershell
Set-Alias -Name ls -Value Get-Process -Force

```

Jetzt startet `ls` plötzlich Prozesse statt Dateien aufzulisten.  
Ein hervorragender Weg, sich selbst drei Stunden später maximal zu verwirren.

---

### Alias dauerhaft speichern

```powershell
Add-Content -Path $PROFILE -Value 'Set-Alias -Name ll -Value Get-ChildItem'

```

---

### Alias anzeigen

```powershell
Get-Alias ll

```

---

## ⚙️ Typische Anwendungsfälle

- Eigene Kurzbefehle erstellen
- Linux-ähnliche Befehle nachbauen
- Lange Befehlsnamen abkürzen
- Eigene Shell-Umgebungen konfigurieren
- Schnellzugriffe für Skripte

---

## ❗ Alternativen / Ergänzungen

### `New-Alias`

```powershell
New-Alias -Name ll -Value Get-ChildItem

```

**Unterschied zu `Set-Alias`:**

<table id="bkmrk-cmdlet-verhalten-new"><thead><tr><th>Cmdlet</th><th>Verhalten</th></tr></thead><tbody><tr><td>`New-Alias`</td><td>Erstellt nur neue Aliase</td></tr><tr><td>`Set-Alias`</td><td>Erstellt oder überschreibt</td></tr></tbody></table>

---

### Funktionen statt Alias

```powershell
function ll {
    Get-ChildItem -Force
}

```

**Vorteil:**

- Kann Parameter enthalten
- Deutlich flexibler
- Besser für komplexe Logik

---

## 🚫 Typische Fehler

### 1. Denken, dass Aliase Parameter speichern

```powershell
# ❌ Funktioniert NICHT wie erwartet
Set-Alias -Name ll -Value "Get-ChildItem -Force"

```

Ein Alias verweist nur auf einen Commandnamen.  
Nicht auf eine komplette Befehlszeile.

➡️ Dafür nutzt man Funktionen.

---

### 2. Alias nach Neustart weg

```powershell
Set-Alias -Name test -Value Get-Date

```

Nach neuer PowerShell-Sitzung verschwunden.

➡️ Alias ins `$PROFILE` schreiben.

---

### 3. Wichtige Standard-Aliase überschreiben

```powershell
Set-Alias -Name cd -Value Get-Date

```

Technisch möglich.  
Psychologisch fragwürdig.

---

## 🧠 Best Practices

- Nur sinnvolle Kurzformen verwenden
- Standard-Aliase nicht leichtfertig überschreiben
- Für komplexe Logik lieber Funktionen nutzen
- Dauerhafte Aliase ins `$PROFILE` auslagern
- In Skripten sparsam mit Aliases umgehen, damit der Code lesbar bleibt

# Module

Irgendwann wird jedes Skript groß genug, um unübersichtlich zu werden. Module helfen dabei, Funktionen, Variablen und andere Bestandteile sinnvoll zu organisieren und wiederzuverwenden. Kurz gesagt: weniger Chaos, mehr Struktur. Meistens jedenfalls.

***Modules.psm1*** – Erweiterungen für das Skript, die zusätzliche Variablen , Funktionen oder Objekte beinhalten. Es kann nicht allein ausgeführt werden und muss von einem **Skript** oder [**Manifest**](https://doku.borinas.com/books/powershell-programmierung/page/manifest "Manifest") [importiert](https://doku.borinas.com/books/powershell-programmierung/page/import-modul-laden "Import (Modul laden)") werden .

Es gibt auch → **modulqualifizierter Aufruf**

# Manifest

Ein **PowerShell-Manifest** (`.psd1`) ist im Grunde nur eine Hashtable mit vordefinierten Keys. Es enthält die **Metadaten und Konfiguration** zu deinem Modul – also alles, was PowerShell über dein Modul wissen muss, ohne den eigentlichen Code auszuführen. Deshalb liegt es **bewusst in einer eigenen Datei**, getrennt vom Modul selbst.

---

## 🧱 Grundlegende Metadaten

Das ist die Identität deines Moduls. Ohne das bist du einfach nur irgendein Skript mit Existenzkrise.

- `RootModule` – Hauptdatei (`.psm1` oder `.dll`)
- `ModuleVersion` – Version (z. B. `1.0.0`)
- `GUID` – Eindeutige ID
- `Author` – Ersteller/Entwickler des Moduls
- `CompanyName` – Unternehmen
- `Copyright`
- `Description` – Beschreibung des Moduls

---

## 🔗 Abhängigkeiten

Hier sagst du: „Ich funktioniere nicht alleine, ich brauche andere.“

- `RequiredModules`   
    → Externe Abhängigkeiten  
    → Angabe über **Modulnamen** (PowerShell sucht es selbst)  
    → Müssen im System vorhanden sein (installiert / auffindbar, z.B. über `$PSModulePath`)  
    → Gehören **nicht** zu deinem Modul
- `RequiredAssemblies` – DLLs
- `ScriptsToProcess` – Skripte, die beim Laden ausgeführt werden
- `ModuleList` – Nur die Meta-Information welche andere Module zugehörig sind
- `NestedModules`  
    → Angabe über **Dateien/Pfade** (du sagst konkret, was geladen wird)  
    → Interne Bausteine deines Moduls  
    → Werden zusammen mit deinem Modul geladen  
    → Gehören **zu deinem Modul dazu**

---

## 🚀 Export (was nach außen sichtbar ist)

Das ist der Teil, wo du entscheidest, was du der Welt zeigst. Oder versteckst.

- `FunctionsToExport`
- `CmdletsToExport`
- `VariablesToExport`
- `AliasesToExport`

👉 Klassiker:  
`'*'` = alles exportieren  
oder explizit = bessere Kontrolle

---

## 🧠 Kompatibilität &amp; Anforderungen

Hier wird’s picky.

- `PowerShellVersion`
- `PowerShellHostName`
- `PowerShellHostVersion`
- `DotNetFrameworkVersion`
- `CLRVersion`
- `ProcessorArchitecture`

---

## 📦 Private Daten (der geheime Keller)

Alles, was du selbst definierst.

- `PrivateData` – Hashtable für eigene Infos

Beispiel:

```powershell
PrivateData = @{
    PSData = @{
        Tags = @('UI','Forms')
        ProjectUri = 'https://...'
    }
}
```

---

## 🌐 PSData (wichtig für Gallery etc.)

Teil von `PrivateData`, aber so wichtig, dass es eigentlich sein eigenes Ding ist.

- `Tags`
- `LicenseUri`
- `ProjectUri`
- `IconUri`
- `ReleaseNotes`

---

## 🧩 Sonstiges (der „Warum gibt’s das?“ Bereich)

Selten gebraucht, aber existiert halt:

- `FileList`
- `TypesToProcess`
- `FormatsToProcess`
- `CompatiblePSEditions` (`Desktop`, `Core`)
- `HelpInfoURI`

---

## 🔧 Mini-Beispiel

Damit du nicht nur trockene Theorie hast:

```powershell
@{
    RootModule        = 'MeinModul.psm1'
    ModuleVersion     = '1.0.0'
    GUID              = '12345678-abcd-1234-abcd-1234567890ab'
    Author            = 'Jonny'
    Description       = 'Mein erstes Modul'

    FunctionsToExport = @('Get-Thing', 'Set-Thing')

    PowerShellVersion = '5.1'

    PrivateData = @{
        PSData = @{
            Tags = @('Example','Test')
        }
    }
}
```

---

## Der eigentliche Punkt (den viele übersehen)

Du brauchst vielleicht **30% davon wirklich**.

Der Rest ist:

- entweder für Publishing
- oder für Leute, die Kontrolle lieben (oder Angst haben)

Wenn du lokal entwickelst:  
→ `RootModule`, `ModuleVersion`, `FunctionsToExport`  
→ fertig, läuft.

# Import (Modul laden)

Hier passiert das Offensichtliche, das trotzdem erstaunlich oft falsch gemacht wird: Du lädst dein Modul in die aktuelle Session.

Ohne Import bist du einfach nur jemand, der Code hortet.

---

### 🔧 Grundbefehl

```powershell
Import-Module MeinModul
```

PowerShell sucht dein Modul automatisch in den bekannten Modulpfaden (`$env:PSModulePath`).

Wenn es da liegt: easy. Wenn nicht: dein Problem.

---

### 📁 Import über Pfad

Wenn dein Modul irgendwo rumliegt, wo PowerShell es nicht findet:

```powershell
Import-Module "C:\MeinProjekt\MeinModul.psm1"
```

Oder direkt das Verzeichnis:

```powershell
Import-Module "C:\MeinProjekt\MeinModul"
```

PowerShell nimmt dann automatisch das [Manifest](https://doku.borinas.com/books/powershell-programmierung/page/manifest "Manifest (Modul.psd1)") (`.psd1`) oder die Moduldatei (`.psm1`).

---

### 🔁 Erneutes Laden (Reload)

Du änderst Code, aber PowerShell hängt noch am alten Stand fest? Überraschung.

```powershell
Import-Module MeinModul -Force
```

`-Force` lädt das Modul neu, auch wenn es schon geladen ist.

Pflicht beim Entwickeln, außer du stehst auf Selbstverwirrung.

---

### 🎯 Selektiver Import

Du musst nicht alles laden. Du kannst gezielt nur bestimmte Dinge importieren:

```powershell
Import-Module MeinModul -Function Get-Thing
```

Oder mehrere:

```powershell
Import-Module MeinModul -Function Get-Thing, Set-Thing
```

Funktioniert natürlich nur, wenn dein Manifest das auch erlaubt (`FunctionsToExport`).

---

### 🧠 Automatischer Import

Seit neueren PowerShell-Versionen gilt:

Wenn du eine Funktion aus einem Modul aufrufst, wird es automatisch importiert.

```powershell
Get-Thing
```

→ PowerShell denkt sich: „Kenn ich nicht… ach warte, da gibt’s ein Modul“

Klingt smart. Ist es auch.  
Bis es nicht mehr funktioniert und du nicht weißt warum.

---

### 🔍 Geladene Module anzeigen

```powershell
Get-Module
```

Alle verfügbaren Module anzeigen:

```powershell
Get-Module -ListAvailable
```

Wenn dein Modul hier nicht auftaucht, kannst du lange importieren. Wird nix.

---

### 🧹 Modul entfernen

Falls du es wieder loswerden willst:

```powershell
Remove-Module MeinModul
```

Hilfreich beim Entwickeln, wenn du nicht mit `-Force` arbeiten willst.

---

### ⚠️ Bereits geladenes Modul

Wenn ein Modul bereits geladen ist und du erneut `Import-Module` aufrufst, passiert… nichts.

```powershell
Import-Module MeinModul
```

PowerShell sagt sich: „Kenn ich schon“ und ignoriert dich stillschweigend.

Das bedeutet:

- Änderungen im Code werden **nicht** übernommen
- Du arbeitest weiter mit der alten Version

Wenn du sicherstellen willst, dass dein Modul wirklich neu geladen wird:

```powershell
Import-Module MeinModul -Force
```

Alternativ kannst du es vorher entfernen:

```powershell
Remove-Module MeinModul Import-Module MeinModul
```

**Wichtig beim Entwickeln:**  
Wenn sich „irgendwas komisch anfühlt“, ist es erstaunlich oft genau das hier.

Du debugst dann nicht deinen Code.  
Du debugst deine Vergangenheit.

---

## Der eigentliche Punkt (den wieder alle ignorieren)

**Import ist kein einmaliger Akt, sondern Teil deines Workflows.**

- Entwicklung → ständig `-Force`
- Debugging → bewusst laden/entladen
- Produktion → einmal sauber laden und fertig

Wenn dein Modul sich beim Import komisch verhält, liegt es fast nie am Import.  
Es liegt daran, was dein Modul beim Laden tut.

Und genau da trennt sich „läuft irgendwie“ von „ich hab verstanden, was ich da gebaut habe“.

# System.Windows.Forms

**Namespace:** System.Windows.Forms  
  
Mit **System.Windows.Forms** können Forms erstellt werden, die wie ein Windows-Fenster agieren. Um es nutzen zu können, muss das entsprechende Assembly eingebunden werden:  
  
*Add-Type -AssemblyName System.Windows.Forms*  
[](https://doku.borinas.com/books/powershell-programmierung/page/form "Form")

# CheckBox

**Namespace:** `System.Windows.Forms`

<details id="bkmrk-eigenschaften-%2F-prop"><summary>Eigenschaften / Propertys</summary>

- ***Property* – Standardwert**  
    Beschreibung oder Erläuterung der Eigenschaft

---

- **Appearance** – *Normal*  
    Darstellung der CheckBox  
    `Normal` = klassische Checkbox  
    `Button` = verhält sich wie ein Toggle-Button
- **AutoCheck** – *$true*  
    Ob die CheckBox ihren Zustand automatisch selbst ändert
- **Checked** – *$false*  
    Ob die CheckBox aktiviert ist
- **CheckState** – *Unchecked*  
    Zustand der CheckBox  
    (`Unchecked`, `Checked`, `Indeterminate`)
- **ThreeState** – *$false*  
    Erlaubt dritten Zustand (`Indeterminate`)
- **Text** – *""*  
    Angezeigter Text neben der Checkbox
- **TextAlign** – *MiddleLeft*  
    Ausrichtung des Textes
- **CheckAlign** – *MiddleLeft*  
    Position des Häkchens
- **FlatStyle** – *Standard*  
    Darstellung der Checkbox  
    (`Standard`, `Flat`, `Popup`, `System`)
- **AutoSize** – *$false*  
    Passt Größe automatisch an Inhalt an
- **Enabled** – *$true*  
    Aktiviert oder deaktiviert die CheckBox
- **Visible** – *$true*  
    Sichtbarkeit der CheckBox
- **Font** – *Standard-Systemfont*  
    Schriftart des Textes
- **ForeColor** – *ControlText*  
    Textfarbe
- **BackColor** – *Transparent*  
    Hintergrundfarbe
- **Dock** – *None*  
    Docking innerhalb des Containers
- **Anchor** – *(Top, Left)*  
    Verhalten bei Größenänderung des Containers
- **Location** – *(0,0)*  
    Position innerhalb des Containers
- **Size** – *(104,24 ungefähr)*  
    Größe der CheckBox
- **TabIndex** – 0  
    Reihenfolge beim Durchtabben
- **TabStop** – *$true*  
    Ob die CheckBox per TAB erreichbar ist

</details>---

Die **CheckBox** gehört zu diesen Controls, die harmlos aussehen… bis man plötzlich merkt, dass daran halbe UI-Logik hängt.

Denn technisch gesehen ist sie nicht einfach nur „an oder aus“.  
Sie ist oft ein kleiner Schalter für:

- Einstellungen
- Features
- Berechtigungen
- Optionen
- Dynamisches UI-Verhalten

Und plötzlich hängt daran alles. Willkommen im Club menschlicher Selbstüberschätzung.

---

```powershell
# CheckBox erstellen
$checkBox = New-Object System.Windows.Forms.CheckBox
$checkBoxNew = [System.Windows.Forms.CheckBox]::new()

# Text
$checkBox.Text = "Dark Mode aktivieren"

# Position & Größe
$checkBox.Location = New-Object System.Drawing.Point(10,10)
$checkBox.AutoSize = $true

# Standardmäßig aktiv
$checkBox.Checked = $true

```

---

## 📥 Werte auslesen

```powershell
# Aktiviert?
$state = $checkBox.Checked

# Exakter Zustand
$checkState = $checkBox.CheckState

```

---

- ***Event* – Hinweistext**  
    Auslöser / Trigger dieses Events

---

- **CheckedChanged**  
    Wird ausgelöst, sobald sich `Checked` ändert
- **CheckStateChanged**  
    Feuert bei Änderung von `CheckState`

---

- **Click**  
    Wird bei jedem Klick ausgelöst
- **DoubleClick**  
    Feuert beim Doppelklick
- **MouseDown**  
    Feuert vor `Click`
- **MouseUp**  
    Feuert nach `Click`

---

- **KeyDown**  
    Taste wird gedrückt
- **KeyUp**  
    Taste wird losgelassen

---

# Events - *CheckBox*

## ✅ CheckedChanged

Das wichtigste Event der ganzen CheckBox.

Wird ausgelöst, sobald sich der Zustand ändert.

```powershell
$checkBox.Add_CheckedChanged({
    Write-Host "CheckBox Zustand:" $checkBox.Checked
})

```

👉 Das ist normalerweise das Event, das du wirklich willst.

Nicht `Click`.

Nicht `MouseDown`.

Nicht irgendwelche kreativen Konstruktionen aus emotionalem Kontrollverlust.

---

## 🔁 CheckStateChanged

Ähnlich wie `CheckedChanged`, aber für `CheckState`.

Relevant bei `ThreeState`.

```powershell
$checkBox.ThreeState = $true

$checkBox.Add_CheckStateChanged({
    Write-Host "State:" $checkBox.CheckState
})

```

👉 Ohne `ThreeState` bringt dir das meistens exakt gar nichts.

---

## 🖱️ Click

Feuert bei jedem Klick auf die CheckBox.

```powershell
$checkBox.Add_Click({
    Write-Host "CheckBox wurde geklickt"
})

```

Das Problem:

`Click` bedeutet nicht automatisch, dass sich der Zustand geändert hat.

Das vergessen Leute ständig und bauen dadurch doppelte Logik.

---

## ⌨️ KeyDown

Für Tastatursteuerung.

```powershell
$checkBox.Add_KeyDown({
    Write-Host $_.KeyCode
})

```

👉 Viele vergessen komplett, dass Benutzer auch Tastaturen besitzen. Faszinierende gesellschaftliche Entwicklung eigentlich.

---

---

# 🧩 ThreeState

Normalerweise kennt eine CheckBox nur:

```powershell
Checked
Unchecked

```

Mit `ThreeState` kommt hinzu:

```powershell
Indeterminate

```

Beispiel:

```powershell
$checkBox.ThreeState = $true
$checkBox.CheckState = "Indeterminate"

```

Das nutzt man oft für:

- Teilweise ausgewählt
- Gemischte Zustände
- „Nicht eindeutig“

Klassisches Beispiel:

> Ordner-Auswahl mit Unterelementen  
> Einige aktiviert → graues Kästchen

---

# 🎨 Appearance = Button

Das hier kennen überraschend viele nicht:

```powershell
$checkBox.Appearance = "Button"

```

Dann wird aus der CheckBox ein Toggle-Button.

```powershell
$checkBox.Text = "Musik aktivieren"
$checkBox.Appearance = "Button"

```

👉 Sehr praktisch für moderne UI-Schalter.

Und ja, technisch bleibt es trotzdem einfach nur eine CheckBox im Kostüm. Menschen machen das übrigens auch ständig.

---

# ⚠️ Typische Stolperfallen

- `CheckedChanged` feuert auch bei Änderungen per Code

```powershell
$checkBox.Checked = $true

```

→ Event wird trotzdem ausgelöst

---

- `Click` und `CheckedChanged` gleichzeitig nutzen  
    → doppelte Ausführung

---

- `ThreeState` aktiviert, aber nur `Checked` geprüft

```powershell
if ($checkBox.Checked)

```

→ ignoriert `Indeterminate`

---

- `AutoCheck = $false`

Dann ändert die CheckBox ihren Zustand **nicht selbst**.

```powershell
$checkBox.AutoCheck = $false

```

👉 Ab da bist *du* verantwortlich.

Glückwunsch. Du bist offiziell der Zustand-Manager deines kleinen Universums.

---

# 🧩 Best Practice

- Für einfache Optionen → `CheckBox`
- Für Ein/Aus-Schalter → `Appearance = Button`
- Für mehrere zusammenhängende Optionen → `GroupBox`
- Für „eine von vielen“ → eher `RadioButton`

---

Ich greif einen Punkt raus, den viele komplett unterschätzen:

**Eine CheckBox ist kein Datenspeicher. Sie ist nur UI.**

Das hier:

```powershell
if ($checkBox.Checked)

```

…ist kein „Systemzustand“.

Das ist nur die aktuelle Anzeige im Interface.

Wenn deine komplette Logik davon abhängt, ob irgendein Kästchen gerade angehakt ist, endet dein Projekt irgendwann wie ein Keller voller Verlängerungskabel. Funktioniert irgendwie. Bis jemand dagegen tritt.

# CheckedListBox

**Namespace:** `<a href="https://doku.borinas.com/books/powershell-programmierung/chapter/systemwindowsforms" title="System.Windows.Forms">System.Windows.Forms</a>`

<details id="bkmrk-properties-%2F-eigensc"><summary>Properties / Eigenschaften</summary>

- *Property – Standardwert*  
    Beschreibung oder Erläuterung der Eigenschaft

---

### **Verhalten** (Behavior)

- **AllowDrop** – *$false*  
    Drag &amp; Drop erlauben
- **CheckOnClick** – *$false*  
    Checkbox wird direkt beim Klick geändert (ohne zweiten Klick)
- **Enabled** – *$true*  
    Aktiv / deaktiviert
- **FormattingEnabled** – *$true*  
    Für komplexe Objekte
- **SelectionMode** – *One*  
    Auswahlmodus (meist irrelevant hier)
- **Sorted** – *$false*  
    Automatisch sortieren
- **ThreeDCheckBoxes** – *$false*  
    3D-Darstellung der Checkboxen
- **ScrollAlwaysVisible** – *$false*  
    Scrollbar immer anzeigen
- **HorizontalScrollbar** – *$false*  
    Horizontale Scrollbar
- **TabStop** – *$true*  
    Fokus per Tab
- 

---

### **Daten / Inhalt** (Data)

- **Items** – *(leer)*  
    Alle Einträge
- **CheckedItems** – *(leer)*  
    Alle **aktiv angehakten** Items
- **CheckedIndices** – *(leer)*  
    Indizes der angehakten Items
- **SelectedItem** – *$null*  
    Aktuell markiertes Item (nicht gleich checked!)
- **TabIndex** – 0  
    Tab-Reihenfolge

---

### **Layout &amp; Position**

- **Location** – *(0,0)*  
    Position
- **Size** – *(Width=120, Height=96)*
- **Width** – *(abhängig vom Layout)*
- **Height** – *(abhängig vom Layout)*
- **Anchor** – *(Top, Left)*  
    Verhalten bei Größenänderung
- **Dock** – *None*  
    Layout im Container
- **ItemHeight** – *(abhängig von Font)*

---

### **Aussehen (Appearance)**

- **BackColor** – *SystemColors.Window*  
    Hintergrundfarbe
- **ForeColor** – *SystemColors.WindowText*  
    Textfarbe
- **Font** – *Standard-Systemfont*  
    Schriftart
- **BorderStyle** – *Fixed3D*  
    Rahmenstil (`None`, `FixedSingle`, `Fixed3D`)
- **Visible** – *$true*  
    Sichtbarkeit

---

### **Meta / System**

- **Name** – *""*  
    Interner Name
- **TopIndex** – 0  
    Oberstes sichtbares Item


---

### **Größe**

</details>Die **CheckedListBox** ist im Grunde eine [ListBox](https://doku.borinas.com/books/powershell-programmierung/page/listbox "ListBox") mit eingebauten Checkboxen.

#### **CheckedListBox erstellen**

```powershell
$clb = New-Object System.Windows.Forms.CheckedListBox
$clbNew = [System.Windows.Forms.CheckedListBox]::new()
```

#### **Items hinzufügen**

```powershell
$clb.Items.Add("Apfel")
$clb.Items.Add("Banane")
$clb.Items.AddRange(@("Orange","Mango","Traube"))
```

#### **📥 Werte auslesen**

```Powershell
# Alle angehakten Items
$checked = $clb.CheckedItems

# Indizes
$indices = $clb.CheckedIndices

# Einzelnes Item (nur Fokus, nicht Check!)
$selected = $clb.SelectedItem

```

👉 **Wichtiger Unterschied:**  
`SelectedItem` ≠ `CheckedItems`

- `SelectedItem` betrifft nur das Item, welches Ausgewählt ist.
- `CheckedItems` beinhaltet alle Items welches auch einen Haken haben

---

## <span role="text">**Events -** *CheckedListBox*</span>

<details id="bkmrk-events-event-%24sender"><summary>Events</summary>

- ***Event***  
    Wird ausgelöst, ...

---

- **TextChanged**  
    ... wenn sich die Text-Eigenschaft des Controls ändert

##### **Interaktion**

- **Click**  
    ... wenn ein Eintrag der Liste angeklickt wird
- **DoubleClick**  
    ... wenn ein Eintrag doppelt angeklickt wird
- **ItemCheck**  
    ... wenn sich der Checked-Zustand eines Items ändert (**bevor** die Änderung übernommen wird)
- **SelectedIndexChanged**  
    ... wenn sich die Auswahl (markiertes Item) ändert

##### **Maus**

- **MouseEnter**  
    ... wenn der Mauszeiger die CheckedListBox betritt
- **MouseLeave**  
    ... wenn der Mauszeiger die CheckedListBox verlässt
- **MouseMove**  
    ... wenn der Mauszeiger innerhalb der CheckedListBox bewegt wird
- **MouseDown**  
    ... wenn eine Maustaste auf der CheckedListBox gedrückt wird
- **MouseUp**  
    ... wenn eine Maustaste auf der CheckedListBox losgelassen wird
- **MouseHover**  
    ... wenn der Mauszeiger für kurze Zeit auf der CheckedListBox verweilt

##### **Tastatur**

- **KeyDown**  
    ... wenn eine Taste gedrückt wird, während die CheckedListBox den Fokus hat
- **KeyUp**   
    ... wenn eine gedrückte Taste wieder losgelassen wird
- **KeyPress**   
    ... wenn eine Taste gedrückt wird, die ein Zeichen erzeugt (z. B. Buchstaben, Zahlen)

##### **Fokus**

- **Enter**  
    ... wenn das Control Fokus erhält
- **Leave**   
    ... wenn das Control Fokus verliert

##### **Darstellung / Layout**

- **Paint**  
    ... wenn die CheckedLisBox neu gezeichnet wird
- **LocationChanged**  
    ... wenn sich die Position der CheckedListBox ändert
- **SizeChanged**  
    ... wenn sich die Größe der CheckedListBox ändert

##### **Zustand**

- **EnabledChanged**  
    ... wenn sich der Enabled-Status der CheckedListBox ändert
- **VisibleChanged**  
    ... wenn sich die Sichtbarkeit der CheckedListBox ändert

##### **Aussehen**

- **FontChanged**  
    ... wenn sich die Schriftart der CheckedListBox ändert
- **ForeColorChanged**  
    ... wenn sich die Textfarbe der CheckedListBox ändert
- **BackColorChanged**  
    ... wenn sich die Hintergrundfarbe der CheckedListBox ändert

##### **Datenbindung**

- **DataSourceChanged**  
    ... wenn sich die Datenquelle ändert
- **DisplayMemberChanged**  
    ... wenn sich die Anzeigebindung ändert
- **ValueMemberChanged**  
    ... wenn sich die Wertbindung ändert

</details>##### `$sender` 

→ Das Objekt, das das Event ausgelöst hat. Meistens die `CheckedListBox` selbst

##### `$e`

→ Zusatzinfos zum Event → **was genau ist passiert**

```PowerShell
$CheckedListBox.Add_Click({
  param($sender, $e)

  $sender.BackColor = "Red"
})

```

#### <span role="text">**ItemCheck**</span>

```Powershell
$CheckedListBox.Add_ItemCheck({
  param($sender, $e)

  $e.Index
  $e.NewValue
  $e.CurrentValue
})

```

- `$e.Index` → welches Item
- `$e.NewValue` → neuer Zustand
- `$e.CurrentValue` → alter Zustand

---

## <span role="text">**Tipps &amp; Tricks** *- CheckedListBox*</span>

#### **Check direkt beim Klick**

```Powershell
$checkListBox.CheckOnClick = $true
```

---

#### **Alle gesetzten Werte holen**

```Powershell
$clb.CheckedItems | ForEach-Object {
$_
}

```

---

#### **🧩 Check programmatisch setzen**

```Powershell
$clb.SetItemChecked(0, $true)

```

---

#### **⚠️ Typische Stolperfallen**

- `SelectedItem` benutzt statt `CheckedItems`
- `ItemCheck` falsch verstanden (Timing!)
- Doppel-Events durch Click + ItemCheck
- `CheckOnClick = $false` → wirkt wie Bug

#### **🧩 Mentales Modell**

Die CheckedListBox hat **zwei Zustände gleichzeitig**:

1. Fokus (SelectedItem)
2. Check-Zustand (CheckedItems)

Wenn du das vermischst → Chaos.

#### **🧩 Best Practice**

- Für mehrere unabhängige Optionen → perfekt
- Für „eine Auswahl“ → falsches Control
- Für komplexe Daten → lieber eigene UI bauen

# DialogResult

### Überblick

`System.Windows.Forms.DialogResult` ist ein **Enum**, das den **Rückgabewert eines Dialogfensters** beschreibt. Es gibt an, **wie** ein Dialog geschlossen wurde, zum Beispiel durch Klick auf **OK**, **Cancel**, **Yes** oder **No**.

Der Typ wird hauptsächlich in Verbindung mit Windows-Forms-Dialogen wie `MessageBox` oder `OpenFileDialog` verwendet.

#### Typ

Enum (feste, benannte Werte; intern numerisch)

### Werte (Auszug)

<table border="1" id="bkmrk-wert-bedeutung-none-" style="border-collapse: collapse; width: 100%; height: 273.534px;"><colgroup><col style="width: 17.0441%;"></col><col style="width: 82.9559%;"></col></colgroup><tbody><tr style="height: 29.8px;"><td style="height: 29.8px;">Wert</td><td style="height: 29.8px;">Bedeutung</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`None`</td><td style="height: 30.4667px;">Kein Ergebnis oder Dialog noch nicht geschlossen</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`OK`</td><td style="height: 30.4667px;">OK-Schaltfläche gedrückt</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`Cancel`</td><td style="height: 30.4667px;">Abbrechen gedrückt oder Fenster geschlossen</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`Yes`</td><td style="height: 30.4667px;">Ja gedrückt</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`No`</td><td style="height: 30.4667px;">Nein gedrückt</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`Abort`</td><td style="height: 30.4667px;">Abbruch</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`Retry`</td><td style="height: 30.4667px;">Wiederholen</td></tr><tr style="height: 30.4667px;"><td class="align-center" style="height: 30.4667px;">`Ignore`</td><td style="height: 30.4667px;">Ignorieren</td></tr></tbody></table>

Nicht jeder Dialog liefert jeden Wert zurück.  
Die verfügbaren Rückgabewerte hängen von den verwendeten Buttons ab.

### Verwendung:

- Vergleich von Dialog-Ergebnissen, z.B. MessageBox.Show()
- Typsichere Alternative zu String-Vergleichen

<div id="bkmrk-x-%28x%29" style="color: rgb(0, 0, 0); font-size: 16px; line-height: 16px; background-color: rgb(247, 247, 247); border: 1px solid rgb(153, 153, 153); border-radius: 1px; top: 1px; left: 1px;"><div class="ir-ext-dimensions"><span class="ir-ext-rendered" title="Rendered image dimensions (after any scaling/resizing has been applied)"> x </span> <span class="ir-ext-natural" title="Natural image dimensions (without applying any scaling/resizing)"> (x) </span></div><div class="ir-ext-filesize">  
</div></div><div id="bkmrk-x-%28x%29-1" style="color: rgb(0, 0, 0); font-size: 16px; line-height: 16px; background-color: rgb(247, 247, 247); border: 1px solid rgb(153, 153, 153); border-radius: 1px; top: 1px; left: 1px;"><div class="ir-ext-dimensions"><span class="ir-ext-rendered" title="Rendered image dimensions (after any scaling/resizing has been applied)"> x </span> <span class="ir-ext-natural" title="Natural image dimensions (without applying any scaling/resizing)"> (x) </span></div><div class="ir-ext-filesize">  
</div></div>

# FolderBrowserDialog

Öffnet einen Dialog zur Auswahl eines Ordners.  
Typischer Windows-"Ordner auswählen"-Dialog. Weil Menschen offenbar selbst beim Auswählen eines Verzeichnisses noch eine GUI brauchen und nicht einfach `"C:\Irgendwas"` eintippen können.

---

## Konstruktor

```powershell
$dialog = [System.Windows.Forms.FolderBrowserDialog]::new()

```

---

# Eigenschaften

## `Description`

Text oberhalb des Verzeichnisbaums.

```powershell
$dialog.Description = "Wähle einen Zielordner aus"

```

---

## `SelectedPath`

Der aktuell ausgewählte Ordnerpfad.

Kann:

- vor dem Öffnen gesetzt werden → Startordner
- nach dem Schließen ausgelesen werden → ausgewählter Ordner

```powershell
$dialog.SelectedPath = "C:\Temp"

```

```powershell
$dialog.SelectedPath

```

---

## `RootFolder`

Legt fest, ab welchem Systemordner der Benutzer navigieren darf.

Verwendet Werte aus:

```powershell
[System.Environment+SpecialFolder]

```

Beispiele:

- `Desktop`
- `MyComputer`
- `MyDocuments`
- `ProgramFiles`

```powershell
$dialog.RootFolder = [System.Environment+SpecialFolder]::Desktop

```

---

## `ShowNewFolderButton`

Bestimmt, ob die Schaltfläche **"Neuen Ordner erstellen"** angezeigt wird.

```powershell
$dialog.ShowNewFolderButton = $true

```

---

# Methoden

## `ShowDialog()`

Öffnet den Dialog.

Rückgabewert:

```powershell
[System.Windows.Forms.DialogResult]

```

Meist:

- `OK`
- `Cancel`

```powershell
$result = $dialog.ShowDialog()

```

---

## `Dispose()`

Gibt verwendete Ressourcen frei.

Technisch nicht immer zwingend nötig, aber sauberer. Besonders wenn man viele Dialoge erzeugt. Windows Forms sammelt sonst gerne kleinen Müll an wie ein Messie mit Kabelschublade.

```powershell
$dialog.Dispose()

```

---

# Einfaches Beispiel

```powershell
Add-Type -AssemblyName System.Windows.Forms

$dialog = [System.Windows.Forms.FolderBrowserDialog]::new()

$dialog.Description = "Wähle einen Ordner"
$dialog.SelectedPath = "$env:USERPROFILE\Desktop"
$dialog.ShowNewFolderButton = $true

if ($dialog.ShowDialog() -eq "OK") {
    $dialog.SelectedPath
}

$dialog.Dispose()

```

---

# Typischer Ablauf

```powershell
Dialog erstellen
    ↓
Eigenschaften setzen
    ↓
ShowDialog()
    ↓
DialogResult prüfen
    ↓
SelectedPath verwenden
    ↓
Dispose()

```

---

# Wichtige Hinweise

## `SelectedPath` setzt auch den Startordner

Viele denken:

> "`RootFolder` bestimmt den Startordner"

Nein.  
`RootFolder` begrenzt nur den sichtbaren Bereich.

Der tatsächliche Startordner kommt meistens von:

```powershell
SelectedPath

```

---

## Benutzer kann abbrechen

Darum niemals direkt:

```powershell
$dialog.SelectedPath

```

verwenden ohne vorher:

```powershell
ShowDialog()

```

zu prüfen.

Sonst arbeitest du eventuell mit leerem Inhalt weiter. Und plötzlich löscht ein Skript rekursiv `"\"`. Kleine Eskalation. Riesige Wirkung.

---

# Unterschied zu `OpenFileDialog`

<table id="bkmrk-dialog-zweck-folderb"><thead><tr><th>Dialog</th><th>Zweck</th></tr></thead><tbody><tr><td>`FolderBrowserDialog`</td><td>Ordner auswählen</td></tr><tr><td>`OpenFileDialog`</td><td>Datei auswählen</td></tr><tr><td>`SaveFileDialog`</td><td>Speicherort + Dateiname auswählen</td></tr></tbody></table>

---

# Typische Verwendung

- Installationspfad wählen
- Backup-Ordner wählen
- Exportpfad wählen
- Scan-Verzeichnis wählen
- Musik-/Bildordner auswählen
- Benutzerfreundliche Pfadauswahl in GUIs

---

# Minimalbeispiel

```powershell
if ($dialog.ShowDialog() -eq "OK") {
    $path = $dialog.SelectedPath
}

```

Das ist im Grunde der Kern von allem hier. Der Rest ist Komfort, Einschränkung oder kosmetische Kontrolle über das Windows-Chaos von 2003.

# Form

**Namespace:** `<a href="https://doku.borinas.com/books/powershell-programmierung/chapter/systemwindowsforms" title="System.Windows.Forms">System.Windows.Forms</a>`

<details id="bkmrk-properties-%2F-eigensc"><summary>Properties / Eigenschaften</summary>

- *Property – Standardwert*  
    Beschreibung oder Erläuterung der Eigenschaft

---

- **AutoScaleMode** – *Font*  
    Skalierung basierend auf Schriftgröße
- **AutoSize** – *$false*  
    Passt die Form automatisch an den Inhalt an
- **BackColor** – *Control*  
    Hintergrundfarbe
- **ClientSize** – *(300,300)*  
    Innenbereich der Form (ohne Rahmen)
- **ControlBox** – *$true*  
    Zeigt Schließen / Minimieren / Maximieren
- **ForeColor** – *ControlText*  
    Standard-Textfarbe
- **FormBorderStyle** – *Sizable*  
    Fensterrahmen (`None`, `FixedSingle`, `Sizable`, …)
- **Icon** – *$null*  
    Fenster-Icon (`[Icon]::new("App.ico")`)
- **KeyPreview** – *$false*  
    Form bekommt Key-Events vor Controls
- **MaximizeBox** – *$true*  
    Maximieren erlauben
- **MaximumSize** – *(0,0)*  
    Maximalgröße (0 = unbegrenzt)
- **MinimumSize** – *(0,0)*  
    Minimale Größe
- **Opacity** – *1.0*  
    Transparenz (0.0 – 1.0)
- **Padding** – *(0)*  
    Innenabstand
- **StartPosition** – *WindowsDefaultLocation*  
    Startposition des Fensters
- **ShowIcon** – *$true*  
    Icon anzeigen
- **ShowInTaskbar** – *$true*  
    In Taskleiste sichtbar
- **Size** – *(300,300)*  
    Fenstergröße
- **Text** – *""*  
    Fenstertitel
- **TopMost** – *$false*  
    Immer im Vordergrund
- **WindowState** – *Normal*  
    (`Normal`, `Minimized`, `Maximized`)

---

#### *Eigenschaften, die sich gegenseitig beeinflussen*

- `AutoSize = $true` → ignoriert **Size**
- `Dock = "Fill"` → ignoriert **AutoSize**
- `Dock = "Top"` / `"Bottom"` → **Width** wird ignoriert
- `Dock = "Left"` / `"Right"` → **Height** wird ignoriert
- `FormBorderStyle = "None"` → keine ControlBox, kein **Icon** sichtbar

</details>Eine `Form` ist das **Hauptfenster deiner Anwendung**.  
Sie ist der Container für alle anderen Controls.

---

### Grundidee

Die `Form` ist **die Bühne**.

- enthält alle Controls
- verwaltet Layout und Lebenszyklus
- steuert Anzeige (`Show` / `ShowDialog`)

---

### Typischer Ablauf

1. Properties setzen
2. Controls hinzufügen
3. Events definieren
4. Form anzeigen (`Show()` / `ShowDialog()`)

---

### Form erstellen

```powershell
# Klassisch
$form = New-Object System.Windows.Forms.Form

# .NET-Style
$form = [System.Windows.Forms.Form]::new()

```

---

### Form anzeigen

```powershell
# Nicht blockierend
$form.Show()

# Modal (blockierend)
$form.ShowDialog()

```

---

### Controls hinzufügen

```powershell
$form.Controls.Add($button)
$form.Controls.AddRange(@($label, $textbox))

```

---

### Layout &amp; Verhalten

```powershell
$form.Size = [System.Drawing.Size]::new(400, 300)
$form.StartPosition = "CenterScreen"
$form.TopMost = $true
$form.FormBorderStyle = "FixedDialog"

```

---

## **Events** - *Form*

```powershell
$form.Add_*
  param($sender, $e)

```

- `$sender` → die Form selbst
- `$e` → EventArgs

---

#### **Load**

Wird beim Initialisieren der Form ausgelöst

```powershell
$form.Add_Load({
    Write-Host "Form lädt"
})

```

---

#### **Shown**

Wird nach dem Anzeigen ausgelöst

```powershell
$form.Add_Shown({
    Write-Host "Form sichtbar"
})

```

---

#### **FormClosing**

Vor dem Schließen (kann verhindert werden)

```powershell
$form.Add_FormClosing({
    param($sender, $e)

    $e.Cancel = $true  # verhindert Schließen
})

```

---

#### **FormClosed**

Nach dem Schließen

```powershell
$form.Add_FormClosed({
    Write-Host "Form geschlossen"
})

```

---

#### **Resize**

Bei Größenänderung

```powershell
$form.Add_Resize({
    Write-Host "Neue Größe: $($this.Size)"
})

```

---

## **Tipps &amp; Tricks** - *Form*

---

### Typische Stolperfallen

- **Form schließt sofort**  
    → kein `ShowDialog()` verwendet
- **Layout bricht auseinander**  
    → `Dock` / `Anchor` falsch gesetzt
- **Größe ignoriert**  
    → `AutoSize` aktiv
- **Fenster reagiert nicht auf Keys**  
    → `KeyPreview = $true` fehlt

---

## Mentales Modell

Die `Form` ist der **Lebenszyklus-Controller deiner UI**.

Sie bestimmt:

- wann etwas sichtbar ist
- wann etwas endet
- wie alles organisiert ist

---

## Wann sinnvoll?

- Immer (jede WinForms-App braucht mindestens eine Form)

---

## Wann problematisch?

- Zu viel Logik direkt in der Form
- Vermischung von UI und Business-Logik

# Label

Ein `Label` ist ein **reines Anzeige-Control** für Text.  
Es dient zur Beschreibung, Anzeige oder Strukturierung von UI-Inhalten.

Ein `Label` ist **nicht für direkte Benutzerinteraktion vorgesehen**, kann jedoch auf Ereignisse wie Mausklicks reagieren.

### Label erstellen

```powershell
# Klassisch
$label = New-Object System.Windows.Forms.Label

# .NET-Style
$label = [System.Windows.Forms.Label]::new()
```

---

## **Eigenschaften** - *Label*

<details id="bkmrk-properties-%2F-eigensc"><summary>Eigenschaften</summary>

- **Property** *– Standardwert*  
    Beschreibung oder Erläuterung der Eigenschaft

---

- **Text** – *""*  
    Angezeigter Text
- **AutoSize** – *$false*  
    Passt Größe automatisch an den Inhalt an
- **TextAlign** – *TopLeft*  
    Ausrichtung des Textes innerhalb des Labels
- **BorderStyle** – *None*  
    Rahmenstil des Labels
- **Dock** – None  
    Layout innerhalb des Parent-Containers
- **Anchor** – *(Top, Left)*  
    Verhalten bei Größenänderung des Containers
- **Padding** – *(0, 0, 0, 0)*  
    Innenabstand
- **Margin** – *(3, 3, 3, 3)*  
    Außenabstand
- **UseMnemonic** – *$true*  
    Aktiviert Zugriff über Alt+Key (z.B. &amp;Datei)

##### **geerbt von** `<strong>Control</strong>`

- **Width** – *100*  
    Breite des Labels
- **Height** – *23*  
    Höhe des Labels
- **Size** – *(Width=100, Height=23)*  
    Größe des Labels
- **Location** – *(0, 0)*  
    Position innerhalb des Containers
- **ForeColor** – *SystemColors.ControlText*  
    Textfarbe (systemabhängig)
- **BackColor** – *Transparent*  
    Hintergrundfarbe (übernimmt Parent-Hintergrund)
- **Font** – *Microsoft Sans Serif, 8.25pt*  
    Standard-Systemschrift
- **Enabled** – *$true*  
    Aktiviert das Label
- **Visible** – *$true*  
    Sichtbarkeit des Labels
- **TabIndex** – 0  
    Reihenfolge beim Durchtabben
- **TabStop** – *$false*  
    Label ist standardmäßig nicht per Tab erreichbar
- **Name** – *""*  
    Interner Name des Controls

</details>### `Text`

**Typ:** `system.String`

Die Eigenschaft **Text** legt den angezeigten Inhalt eines Labels fest.

```powershell
$label.Text = "Hallo Welt"

# Mehrzeilig
$label.Text = "Zeile 1`nZeile 2"
```

PowerShell macht implizite Konvertierung:

```powershell
$Label.Text = 123
```

funktioniert, weil es intern zu `"123"` wird.

---

### `AutoSize`

**Typ:** `System.Boolean`

Die Eigenschaft **AutoSize** bestimmt, ob sich das Label automatisch an die Größe seines Inhalts anpasst.

```powershell
# Automatische Größe
$label.AutoSize = $true
```

---

### Size

**Typ:** `System.Drawing.Size`

```powershell
# Feste Größe
$label.Size = [System.Drawing.Size]::new(200, 30)
```

---

### TextAlign

**Typ:** `System.Drawing.ContentAlignment`

```powershell
# Text zentrieren
$label.TextAlign = "MiddleCenter"
```

---

## **Events** - *Label*

<details id="bkmrk-events-event-%24sender"><summary>Events</summary>

- ***Even***   
    Wird ausgelöst, ...

---

##### **Interaktion** *(User Input)*

- **Click**  
    ... wenn das Label mit der Maus angeklickt wird
- **DoubleClick**  
    ... wenn das Label doppelt angeklickt wird
- **MouseEnter**  
    ... wenn der Mauszeiger das Label betritt
- **MouseLeave**  
    ... wenn der Mauszeiger das Label verlässt
- **MouseMove**  
    ... wenn der Mauszeiger innerhalb des Labels bewegt wird
- **MouseDown**  
    ... wenn eine Maustaste auf dem Label gedrückt wird
- **MouseUp**  
    ... wenn eine Maustaste auf dem Label losgelassen wird
- **MouseHover**  
    ... wenn der Mauszeiger für kurze Zeit auf dem Label verweilt

##### **Zustand / Inhalt**

- **TextChanged**  
    ... wenn sich der Text des Labels ändert
- **EnabledChanged**  
    ... wenn sich der Enabled-Status des Labels ändert
- **VisibleChanged**  
    ... wenn sich die Sichtbarkeit des Labels ändert

##### **Layout**

- **LocationChanged**  
    ... wenn sich die Position des Labels ändert
- **SizeChanged**  
    ... wenn sich die Größe des Labels ändert

##### **Darstellung** *(Rendering)*

- **Paint**  
    ... wenn das Label neu gezeichnet wird (z. B. bei Aktualisierung oder Überdeckung)

##### **Aussehen**

- **FontChanged**  
    ... wenn sich die Schriftart des Labels ändert
- **ForeColorChanged**  
    ... wenn sich die Textfarbe des Labels ändert
- **BackColorChanged**  
    ... wenn sich die Hintergrundfarbe des Labels ändert

</details>```powershell
$label.Add_*
  param($sender, $e)
```

- `$sender` → das Label selbst (= `$this`)
- `$e` → EventArgs (je nach Event unterschiedlich)

---

### **Interaktionen** *(User Input)*

##### **Click / DoubleClick / MouseDown**

```powershell
# Click
$label.Add_Click({
    param($sender, $e)

    Write-Host "Auf das Label wurde geklickt"
})

# DoubleClick
$label.Add_DoubleClick({
  param($sender, $e)

  Write-Host "Auf das Label wurde doppelt geklickt"
])

# MouseDown
$label.Add_MouseDown({
  param($sender, $e)

  Write-Host "Auf das Label wurde entweder mit links oder rechts geklickt"
})
```

##### **MouseEnter / MouseLeave**

Mit MouseEnter und MouseLeave sind Hover-Effekte möglich

```powershell
$label.Add_MouseEnter({
    $this.ForeColor = "Red"
})

$label.Add_MouseLeave({
    $this.ForeColor = "Black"
})
```

##### **MouseMove**

Mausbewegung innerhalb vom Label

```powershell
$label.Add_MouseMove({
    param($sender, $e)
})
```

##### **TextChanged**

Wird ausgelöst, wenn sich der Text ändert

```powershell
$label.Add_TextChanged({
    param($sender, $e)

    Write-Host "Neuer Text: $($sender.Text)"
})


```

---

## **Tipps &amp; Tricks** - *Label*

---

### Typische Stolperfallen

- **Text wird abgeschnitten**  
    → `AutoSize` vergessen oder `Size` zu klein
- **Mehrzeiliger Text funktioniert nicht**  
    → kein ``n` verwendet oder Größe zu klein
- **Label wirkt "unsichtbar"**  
    → `ForeColor` = `BackColor`
- **Klick funktioniert nicht wie erwartet**  
    → Label ist nicht für Interaktion gedacht

---

## Mentales Modell

Ein `Label` ist **UI-Deko mit Bedeutung**.

Es erklärt dem Benutzer, was andere Controls tun oder zeigt Status an.

---

## Wann sinnvoll?

- Beschriftung von Eingabefeldern
- Statusanzeigen
- Überschriften / Struktur

---

## Wann vermeiden?

- Wenn Interaktion erwartet wird → Button nutzen
- Wenn komplexe Darstellung nötig ist → anderes Control prüfen
- 

# ListBox

**Namespace:** `<a href="https://doku.borinas.com/books/powershell-programmierung/chapter/systemwindowsforms" title="System.Windows.Forms">System.Windows.Forms</a>`

<details id="bkmrk-properties-hidden-ta"><summary>Properties / Eigenschaften</summary>

<div>- ***Property** – Standardwert*  
    Beschreibung oder Erläuterung der Eigenschaft

---

</div><div>- **AllowDrop** – *$false*  
    Erlaubt Drag &amp; Drop auf die ListBox
- **Anchor** – *(Top, Left)*  
    Bestimmt, wie sich die ListBox bei Größenänderung des Containers verhält
- **BackColor** – *SystemColors.Window*  
    Hintergrundfarbe der ListBox
- **BorderStyle** – *Fixed3D*  
    Rahmenstil (`None`, `FixedSingle`, `Fixed3D`)
- **Dock** – *None*  
    Layout innerhalb des Parent-Containers (z.B. `Fill`)
- **DrawMode** – Normal  
    Zeichenmodus (`Normal`, `OwnerDrawFixed`, `OwnerDrawVariable`)
- **Enabled** – $true  
    Aktiviert oder deaktiviert die ListBox
- **Font** – *Standard-Systemfont*  
    Schriftart der Einträge
- **ForeColor** – *SystemColors.WindowText*  
    Textfarbe der Einträge
- **FormattingEnabled** – *$true*  
    Aktiviert Formatierung für komplexe Objekte
- **HorizontalScrollbar** – *$false*  
    Zeigt horizontale Scrollbar an
- **Location** – *(0,0)*  
    Position innerhalb des Containers
- **Name** – *""*  
    Interner Name der ListBox
- **ScrollAlwaysVisible** – *$false*  
    Scrollbar immer anzeigen, auch wenn nicht nötig
- **TabIndex** – 0  
    Reihenfolge beim Durchtabben
- **TabStop** – *$true*  
    Ob die ListBox per Tab erreichbar ist
- **TopIndex** – 0  
    Index des obersten sichtbaren Elements
- **Visible** – *$true*  
    Sichtbarkeit der ListBox

</div>##### **Items**

- **Items** – *(leer)*  
    Sammlung aller Listeneinträge
- **MultiColumn** – *$false*  
    Mehrspaltige Darstellung aktivieren
- **SelectedIndex** – *-1*  
    Index des aktuell ausgewählten Elements (`-1` = nichts)
- **SelectedItem** – *$null*  
    Aktuell ausgewähltes Element
- **SelectedItems** – *(leer)*  
    Collection aller ausgewählten Elemente (bei `Multi-Select`)
- **SelectionMode** – *One*  
    Auswahlmodus   
    
    - `One` → Nur ein Eintrag
    - `MultiSimple` → Mehrere ohne STRG
    - `MultiExtended` → Mehrere mit STRG/SHIFT
- **Sorted** – *$false*  
    Sortiert Einträge automatisch alphabetisch

##### **Größe**

- **ColumnWidth** – 0  
    Breite der Spalten bei MultiColumn (`0` = automatisch)
- **Height** – *(abhängig vom Layout)*  
    Höhe der ListBox
- **HorizontalExtent** – 0  
    Virtuelle Breite für horizontales Scrollen
- **IntegralHeight** – *$true*  
    Passt Höhe automatisch an volle Einträge an (kein halbes Item unten)
- **ItemHeight** – *(abhängig von Font)*  
    Höhe eines einzelnen Eintrags
- **Size** – *(Width=120, Height=96)*  
    Größe der ListBox
- **Width** – *(abhängig vom Layout)*  
    Breite der ListBox

</details> ![Image](https://media.nngroup.com/media/editor/2020/03/05/2-multiselect-dual-listbox-final) ![Image](https://think360studio-media.s3.ap-south-1.amazonaws.com/photo/plugin/article/2022/List-Box-17102022.jpg)

Die **ListBox** ist eines dieser Controls, die simpel wirken, aber erstaunlich schnell chaotisch werden, wenn man sie nicht im Griff hat. Im Kern zeigt sie eine Liste von Einträgen an, aus denen der Benutzer auswählen kann.

---

```powershell
# ListBox erstellen
$listBox = New-Object System.Windows.Forms.ListBox
$listBoxNew = [System.Windows.Forms.ListBox]::new()

# Größe & Position
$listBox.Size = New-Object System.Drawing.Size(200,150)
$listBox.Location = New-Object System.Drawing.Point(10,10)

# Items hinzufügen
$listBox.Items.Add("Apfel")
$listBox.Items.Add("Banane")
$listBox.Items.Add("Kirsche")

# Mehrere auf einmal
$listBox.Items.AddRange(@("Orange","Mango","Traube"))

```

---

#### 📥 Werte auslesen

```powershell
# Einzelne Auswahl
$selected = $listBox.SelectedItem

# Index
$index = $listBox.SelectedIndex

# Mehrere auswählen
$selectedItems = $listBox.SelectedItems

```

<details id="bkmrk-events-event-%E2%80%93-hinwe"><summary>Events</summary>

<div>- ***Event** – Hinweistext*  
    Auslöser / Trigger dieses Events

---

</div><div>- **SelectedIndexChanged**  
    Wird ausgelöst, sobald sich die Auswahl ändert.
- **SelectedValueChanged**  
    Fast wie `SelectedIndexChanged`, aber subtil anders.  
    Feuert, wenn sich der **Value** ändert (relevant bei `ValueMember`)
- ---
    
    **Click**  
    Löst bei jedem Klick auf das Control aus
- **DoubleClick** Feuert, beim Doppelklick auf das Control / oder ein Item
- **MouseDown** Feuert **vor** dem Click
- **MouseUp** Feuert **nach** dem Click
- ---
    
    **KeyDown** Wird ausgelöst, wenn eine Taste gedrückt wird
- **KeyUp** Sowie `KeyDown`, aber erst wenn losgelassen wird

</div></details>---

## **Events *-*** *ListBox*

#### **SelectedIndexChanged**

Wird ausgelöst, sobald sich die Auswahl ändert.

```powershell
$listBox.Add_SelectedIndexChanged({
    Write-Host "Ausgewählt:" $listBox.SelectedItem
})

```

---

#### **SelectedValueChanged**

Fast wie `SelectedIndexChanged`… aber subtil anders.  
Feuert, wenn sich der **Value** ändert (relevant bei `ValueMember`).

```powershell
$listBox.Add_SelectedValueChanged({
    Write-Host "Value geändert:" $listBox.SelectedItem
})

```

👉 Unterschied merkst du erst, wenn du mit Objekten arbeitest.

---

#### **Click**

Wird bei jedem Klick ausgelöst.  
Ja, auch wenn sich **nichts ändert**. Klassiker für doppelte Logik.

```powershell
$listBox.Add_Click({
    Write-Host "ListBox wurde geklickt"
})

```

---

#### **DoubleClick**

Wenn der User doppelt klickt. Perfekt für „öffnen“, „starten“, etc.

```powershell
$listBox.Add_DoubleClick({
    Write-Host "Doppelklick auf:" $listBox.SelectedItem
})

```

👉 UX-technisch oft sinnvoller als Button daneben.

---

#### **KeyDown**

Für Tastatursteuerung. Wird ausgelöst, wenn eine Taste gedrückt wird.

```powershell
$listBox.Add_KeyDown({
    if ($_.KeyCode -eq "Enter") {
        Write-Host "Enter auf:" $listBox.SelectedItem
    }
})

```

👉 Das ist der Moment, wo dein UI sich plötzlich „professionell“ anfühlt.

---

#### **KeyUp**

Wie KeyDown, nur nachdem losgelassen wurde.

```powershell
$listBox.Add_KeyUp({
    Write-Host "Taste losgelassen:" $_.KeyCode
})

```

---

#### **MouseDown**

Feuert **vor** Click. Gut für spezielle Logik.

```powershell
$listBox.Add_MouseDown({
    Write-Host "MouseDown erkannt"
})

```

---

#### **MouseUp**

Nach dem Klick.

```powershell
$listBox.Add_MouseUp({
    Write-Host "MouseUp erkannt"
})

```

---

## **Tipps &amp; Tricks** *- TabControl*

Ich sag’s dir direkt, weil ich genau weiß, wie das läuft:

Du kombinierst sowas:

```powershell
Click
SelectedIndexChanged
DoubleClick

```

…und wunderst dich, warum dein Code **mehrfach läuft**.

👉 Beispiel:

- Klick → `MouseDown`
- Klick → `Click`
- Auswahl ändert sich → `SelectedIndexChanged`

→ Boom, drei Events für einen simplen Klick.

---

##### 🧩 Mini-Leitfaden (der dir später Nerven spart)

- Auswahl reagieren → `SelectedIndexChanged`
- Aktion starten → `DoubleClick` oder `Enter`
- Nur Klick erkennen → `Click`
- Präzise Kontrolle → `MouseDown`

---

## ➕ Items verwalten

```powershell
# Entfernen
$listBox.Items.Remove("Apfel")

# Alles löschen
$listBox.Items.Clear()

# Einfügen an Position
$listBox.Items.Insert(0, "Neu")

```

---

## 🎨 Nützliche Tricks

### Automatisch sortieren

```powershell
$listBox.Sorted = $true

```

### Mehrspaltig anzeigen

```powershell
$listBox.MultiColumn = $true

```

### Scrollbar erzwingen

```powershell
$listBox.HorizontalScrollbar = $true

```

---

## ⚠️ Typische Stolperfallen

- `SelectedItem` ist `$null`, wenn nichts gewählt ist → obvious, aber wird ständig vergessen
- `SelectedItems` ist **kein Array**, sondern Collection → verhält sich leicht anders
- Bei `MultiExtended`: Benutzer müssen STRG drücken → sonst denkt jeder, dein UI ist kaputt
- `Items.AddRange()` erwartet ein Array → kein wild zusammengebauter String-Müll

---

## 🧩 Best Practice

- Für einfache Auswahl → `ListBox`
- Für strukturierte Daten → **ListView** (sonst wird’s hässlich)
- Für kleine Auswahl → lieber **ComboBox**

---

Ich greif einen Punkt raus, den du wahrscheinlich unterschätzt:

**Was speicherst du eigentlich in der ListBox? Strings oder Objekte?**

Wenn du nur Strings reinwirfst, verbaust du dir später jede sinnvolle Logik.  
Pack lieber direkt Objekte rein:

```powershell
$listBox.Items.Add([PSCustomObject]@{
    Name = "Chrome"
    Version = "123"
})

```

Und dann:

```powershell
$listBox.DisplayMember = "Name"

```

Das ist der Unterschied zwischen „funktioniert irgendwie“ und „ich hab Kontrolle über meinen Code“.

Das ist so ein klassischer Punkt, wo Leute sich später selbst hassen, weil sie am Anfang “einfach schnell Strings genommen haben”.

# RichTextBox

**Namespace:** `System.Windows.Forms`

<details id="bkmrk-properties-%2F-eigensc"><summary>Properties / Eigenschaften</summary>

- *Property – Standardwert*  
    Beschreibung oder Erläuterung der Eigenschaft

---

- **BackColor** – *SystemColors.Window*  
    Hintergrundfarbe der RichTextBox
- **BorderStyle** – *Fixed3D*  
    Rahmenstil (`None`, `FixedSingle`, `Fixed3D`)
- **Font** – *Standard-Systemfont*  
    Schriftart und -größe
- **ForeColor** – *SystemColors.WindowText*  
    Textfarbe
- **HideSelection** – *$true*  
    Steuert, ob der Text im nicht markierten Zustand verborgen wird
- **Text** – *""*  
    Der gesamte Text in der RichTextBox
- **WordWrap** – *$true*  
    Zeilenumbruch aktivieren/deaktivieren
- **Rtf** – *""*  
    Ruft den RTF-Inhalt der RichTextBox ab oder setzt ihn
- **Multiline** – *$true*  
    Zeigt Text auf mehreren Zeilen
- **SelectionAlignment** – *Left*  
    Ausrichtung des Textes in der aktuellen Auswahl (`Left`, `Center`, `Right`)
- **SelectionColor** – *SystemColors.HighlightText*  
    Farbe der ausgewählten Textstellen
- **SelectionFont** – *Standard-Schrift*  
    Schriftart der Auswahl
- **SelectionBackColor** – *SystemColors.Highlight*  
    Hintergrundfarbe der Auswahl
- **SelectionLength** – 0  
    Länge der aktuellen Auswahl
- **SelectionStart** – 0  
    Der Startindex der aktuellen Auswahl
- **TextChanged** – *$false*  
    Event wird ausgelöst, wenn sich der Text ändert

</details>Die **RichTextBox** ist eine erweiterte Textbox, die es ermöglicht, formatierte Textinhalte darzustellen und zu bearbeiten. Sie unterstützt RTF (Rich Text Format) sowie einfache Textformate.

---

#### **Erstellen**

```powershell
# Erstellen
$richTextBox = New-Object System.Windows.Forms.RichTextBox
$richTextBoxNew = [System.Windows.Forms.RichTextBox]::new()

# Größe & Position
$richTextBox.Size = New-Object System.Drawing.Size(400, 200)
$richTextBox.Location = New-Object System.Drawing.Point(10,10)

# Text setzen
$richTextBox.Text = "Hallo, dies ist ein Testtext in der RichTextBox!"

# Formatierter Text (RTF)
$richTextBox.Rtf = "{\rtf1\ansi\ansicpg1252\uc1\pard\lang1031\f0\fs20 Hallo, \b dies ist ein \i Test \b0\i0 Text.\par}"

```

#### **📥 Werte auslesen**

```powershell
# Einfacher Text
$plainText = $richTextBox.Text

# RTF-Text
$rtfText = $richTextBox.Rtf

# Auswahl
$selectedText = $richTextBox.SelectedText
```

👉 **Unterschied:**  
`Text` gibt nur den normalen Text zurück, während `Rtf` den gesamten formatierten Text (inklusive Formatierung) liefert.

---

## <span role="text">**Events -** *RichTextBox*</span>

#### <span role="text">**TextChanged**</span>

Wird ausgelöst, wenn sich der Text in der RichTextBox ändert.

```Powershell
$richTextBox.Add_TextChanged({
Write-Host "Text hat sich geändert!"
})
```

---

#### <span role="text">**SelectionChanged**</span>

Wenn der Benutzer die Auswahl ändert, wird dieses Event ausgelöst.

```Powershell
$richTextBox.Add_SelectionChanged({
Write-Host "Neue Auswahl: $($richTextBox.SelectedText)"
})
```

---

#### <span role="text">**LinkClicked**</span>

Wenn der Benutzer auf einen Link klickt, wird dieses Event ausgelöst.

```powershell
$richTextBox.Add_LinkClicked({
param($sender, $e)
Write-Host "Link angeklickt: $($e.Link)"
})
```

---

## <span role="text">**Tipps &amp; Tricks**</span>

#### **Formatierter Text**

```powershell
$richTextBox.SelectionColor = "Red"
$richTextBox.SelectionFont = New-Object System.Drawing.Font("Arial", 12, [System.Drawing.FontStyle]::Bold)

$richTextBox.AppendText(" Dies ist ein Text mit roter Schrift und fettem Arial.")
```

---

#### **RTF-Inhalt speichern**

```powershell
# RTF in Datei speichern
$richTextBox.SaveFile("C:\Pfad\zur\Datei.rtf", [System.Windows.Forms.RichTextBoxStreamType]::RichText)
```

---

#### **Hyperlinks hinzufügen**

```powershell
$richTextBox.AppendText("Hier klicken: ")
$richTextBox.InsertLink("https://www.example.com")
```

---

## ⚠️ Typische Stolperfallen

- **Text wird nicht formatiert**, aber du hast vergessen, den richtigen Stream (RTF vs. Text) zu verwenden.
- **Events feuern zu oft**: Achte darauf, dass du nicht zu viele Events auslöst. Besonders `TextChanged` ist gefährlich, weil es oft auch bei jeder kleinen Änderung feuert.

---

## 🧩 Best Practice

- Für einfache Textfelder immer die normale `TextBox` verwenden.
- Nutze `RichTextBox`, wenn du Formatierung und erweiterte Textoptionen brauchst.
- **RTF ist dein Freund**, wenn du komplexe Formatierungen brauchst – ansonsten geht auch normaler Text.

# TabControl

Ein `<a href="https://doku.borinas.com/books/powershell-programmierung/page/tabcontrol" title="TabControl">TabControl</a>` ist ein Container, der mehrere [`TabPage`](https://doku.borinas.com/books/powershell-programmierung/page/tabpage "TabPage")-Instanzen verwaltet und zwischen ihnen umschaltet.   
Es stellt die Tabs (Reiter) dar und bestimmt, welche `TabPage` aktuell sichtbar ist.

---

### **Grundlagen**

Das `TabControl` ist **die Steuerung**, nicht der Inhalt.

- `TabControl` → verwaltet Tabs
- `TabPage` → enthält den eigentlichen Inhalt

#### **TabControl erstellen**

```powershell
# Klassisch
$tabControl = New-Object System.Windows.Forms.TabControl

# .NET-Style
$tabControl = [System.Windows.Forms.TabControl]::new()
```

#### **TabPage hinzufügen**


Ein `TabPage` wird nicht direkt zum `TabControl` hinzugefügt, sondern zur enthaltenen Sammlung `$tabControl.TabPages`.  
Die Sammlung `TabPages` stellt mehrere Methoden zum Hinzufügen von `TabPage`-Instanzen bereit:

- `TabPages.Add($tabPage1)` – Fügt das `TabPage` `$tabPage1` zur Sammlung hinzu
- `TabPages.AddRange(@($tabPage2, $tabPage3))` – Fügt mehrere `TabPage`-Instanzen gleichzeitig als Array hinzu
- `TabPages.Insert(0, $tabPage4)` – Fügt das `TabPage` an der gewünschten Position innerhalb der Sammlung ein

```powershell
$tabControl.TabPages.Add($tabPage1)

$tabControl.TabPages.AddRange(@(
    $tabPage2,
    $tabPage3
))

$tabControl.TabPages.Insert(0, $tabPage4)

# Technisch möglich, aber nicht empfohlen
$tabControl.Controls.Add($tabPage4)
$tabControl.Controls.AddRange(@(
    $tabPage5,
    $tabPage6
))
```

Über `Add()` kann zusätzlich direkt ein neues `TabPage` erstellt werden:

```powershell
# Erstellt ein neues TabPage
$tabControl.TabPages.Add("TabText")

# Erstellt ein neues TabPage mit Name + Text
$tabControl.TabPages.Add("TabName", "TabText")
```

#### **TabPage entfernen**

Ein `TabPage` kann aus dem `TabControl` mit der Referenz zum TabPage und `Remove()` oder über den Index mit `RemoveAt()` entfernt werden.

```powershell
$tabControl.TabPages.Remove($tabPage1) # mit Referenz
$tabControl.TabPages.RemoveAt(0) # mit Index
```

Mit `Clear()` werden alle `TabPage`-Instanzen entfernt.

```powershell
# Alle entfernen
$tabControl.TabPages.Clear()
```

#### **TabPage Auswahl/Zugriff**

Mit dem jeweiligen Index vom TabPage, kann in TabPages direkt auf das TabPage zugegriffen werden.

```powershell
# Zugriff auf einzelnes TabPage
$tabControl.TabPages[0]

# Aktiven Tab setzen
$tabControl.SelectedIndex = 0
$tabControl.SelectedTab = $tabPage1
```

---

## **Eigenschaften**

<details id="bkmrk-eigenschaften"><summary>Eigenschaften</summary>

- **Alignment** – Position der Tabs (`Top`, `Bottom`, `Left`, `Right`)
- **Anchor** – Verankerung an den Rändern des Parent-Containers
- **Appearance** – Darstellung der Tabs (`Normal`, `Buttons`, `FlatButtons`)
- **Dock** – Automatische Ausrichtung im Parent-Container
- **DrawMode** – Zeichenmodus der Tabs
- **HotTrack** – Hover-Effekt über Tabs
- **ImageList** – Sammlung der für Tabs verfügbaren Bilder
- **ItemSize** – Größe einzelner Tabs (nur relevant bei `SizeMode = Fixed`)
- **Multiline** – Mehrere Reihen von Tabs erlauben
- **Padding** – Innenabstand des Tab-Headers
- **RowCount** – Anzahl der Tab-Reihen (relevant bei `Multiline`)
- **SelectedImageIndex** – Icon für den ausgewählten Tab
- **SelectedIndex** – Index des aktuell aktiven Tabs
- **SelectedTab** – Referenz auf die aktuell aktive `TabPage`
- **ShowToolTips** – Tooltips für Tabs aktivieren
- **SizeMode** – Größe der Tabs (`Normal`, `Fixed`)
- **TabPages** – Sammlung aller enthaltenen `TabPage`-Instanzen

</details>#### **Alignment** \[Systems.Windows.Forms.TabAlignment\]

Der Wert von `Alignment` legt fest, an welcher Seite des `TabControl` die Tabs dargestellt werden. Standardmäßig ist diese Eigenschaft auf `Top` gesetzt, wodurch sich die Tabs oberhalb des Inhaltsbereichs befinden. Alternativ können die Tabs auch am unteren (`Bottom`), linken (`Left`) oder rechten (`Right`) Rand angezeigt werden. Die Position der Tabs beeinflusst lediglich deren Darstellung und hat keinen Einfluss auf die enthaltenen `TabPage`-Instanzen oder deren Funktionalität.

#### **Anchor** \[Systems.Windows.Forms.AnchorStyles\]

Der Wert von `Anchor` legt fest, an welchen Rändern seines Parent-Containers ein Control verankert ist. Standardmäßig ist diese Eigenschaft auf `Top, Left` gesetzt, wodurch das Control seinen Abstand zum oberen und linken Rand beibehält. Wird die Größe des Parent-Containers verändert, passt das Control seine Position oder Größe entsprechend den festgelegten Verankerungen an.

Mehrere Verankerungen können kombiniert werden. Ist ein Control beispielsweise an `Left` und `Right` verankert, wird seine Breite automatisch angepasst, um den Abstand zu beiden Rändern beizubehalten. Durch die Kombination verschiedener Werte lässt sich das Verhalten eines Controls bei Größenänderungen flexibel steuern.

#### **Appearance** \[System.Windows.Forms.TabAppearance\]

Der Wert von `Appearance` legt fest, wie die Tabs eines `TabControl` dargestellt werden. Standardmäßig ist diese Eigenschaft auf `Normal` gesetzt, wodurch die Tabs im klassischen Registerkarten-Stil angezeigt werden. Alternativ können die Tabs als Schaltflächen (`Buttons`) oder als flache Schaltflächen (`FlatButtons`) dargestellt werden.

- **Normal** → klassische Registerkarten
- **Buttons** → Tabs werden wie normale Schaltflächen dargestellt
- **FlatButtons** → Tabs werden wie flache Schaltflächen dargestellt

Die Eigenschaft beeinflusst ausschließlich das Erscheinungsbild der Tabs und hat keinen Einfluss auf die Funktionalität des `TabControl` oder der enthaltenen `TabPage`-Instanzen. Unabhängig von der gewählten Darstellung können Tabs weiterhin ausgewählt und gewechselt werden.

#### **Dock** \[Systems.Windows.Forms.DockStyle\]

Der Wert von `Dock` legt fest, an welcher Seite seines Parent-Containers ein Control angedockt wird. Standardmäßig ist diese Eigenschaft auf `None` gesetzt, wodurch die Position und Größe des Controls ausschließlich durch dessen `Location`- und `Size`-Eigenschaften bestimmt werden. Alternativ kann das Control an den oberen (`Top`), unteren (`Bottom`), linken (`Left`) oder rechten (`Right`) Rand angedockt oder mit `Fill` auf die gesamte verfügbare Fläche des Parent-Containers ausgedehnt werden.

Im Gegensatz zu `Anchor` bestimmt `Dock` nicht die Abstände zu den Rändern, sondern übernimmt die automatische Positionierung und Größenanpassung des Controls. Wird beispielsweise `Fill` verwendet, füllt das Control den gesamten verfügbaren Bereich seines Parent-Containers aus.

#### **DrawMode** \[Systems.Windows.Forms.TabDrawMode\]

Der Wert von `DrawMode` legt fest, wie die Tabs des `TabControl` gezeichnet werden. Standardmäßig ist diese Eigenschaft auf `Normal` gesetzt, wodurch das Betriebssystem die Darstellung der Tabs vollständig übernimmt. Wird `DrawMode` auf `OwnerDrawFixed` gesetzt, ist der Entwickler für das Zeichnen der Tabs verantwortlich und kann deren Aussehen individuell gestalten.

Die Einstellung `OwnerDrawFixed` wird häufig verwendet, um eigene Farben, Schriftarten oder Symbole für Tabs darzustellen. Da die Tabs dabei selbst gezeichnet werden müssen, wird zusätzlich das `DrawItem`-Event benötigt, in dem die eigentliche Darstellung implementiert wird.

#### **HotTrack** \[System.Boolean\]

Der Wert von `HotTrack` legt fest, ob Tabs auf Mausbewegungen reagieren sollen. Standardmäßig ist diese Eigenschaft auf `False` gesetzt, wodurch Tabs ihr Aussehen beim Überfahren mit dem Mauszeiger nicht verändern. Wird `HotTrack` auf `True` gesetzt, hebt das `TabControl` den Tab unter dem Mauszeiger visuell hervor, um die Interaktion für den Benutzer deutlicher zu machen.

#### **ImageList** \[Systems.Windows.Forms.ImageList\]

Der Wert von `ImageList` legt die Bildersammlung fest, aus der die Tabs ihre Symbole beziehen. Standardmäßig ist diese Eigenschaft auf `$null` gesetzt, wodurch keine Symbole angezeigt werden. Die Eigenschaft dient lediglich als Quelle der verfügbaren Bilder. Welche Bilder tatsächlich in den Tab-Headern angezeigt werden, wird über die Eigenschaften `ImageIndex` oder `ImageKey` der jeweiligen `TabPage` festgelegt.

#### **ItemSize** \[System.Drawing.Size\]

Der Wert von `ItemSize` legt die Größe der einzelnen Tabs fest. Standardmäßig besitzt diese Eigenschaft den Wert `(Width=0, Height=0)`, wodurch die Größe der Tabs automatisch durch das `TabControl` bestimmt wird. Die Eigenschaft wird erst relevant, wenn `SizeMode` auf `Fixed` gesetzt ist. In diesem Fall verwendet das `TabControl` die in `ItemSize` festgelegte Breite und Höhe für alle Tabs.

#### **Multiline** \[System.Boolean\]

Der Wert von `Multiline` legt fest, ob die Tabs auf mehrere Reihen verteilt werden dürfen. Standardmäßig ist diese Eigenschaft auf `False` gesetzt, wodurch alle Tabs in einer einzelnen Reihe dargestellt werden. Wird `Multiline` auf `True` gesetzt, erstellt das `TabControl` bei Platzmangel automatisch zusätzliche Reihen, sodass alle Tabs sichtbar bleiben können.

#### **Padding** \[System.Windows.Forms.Padding\]

Der Wert von `Padding` legt den Innenabstand innerhalb der Tab-Header fest. Standardmäßig ist diese Eigenschaft auf `(6, 3)` gesetzt. Dadurch wird zwischen dem Rand eines Tabs und dessen Inhalt, beispielsweise dem Text oder einem Icon, ein zusätzlicher Abstand eingefügt.

Die Eigenschaft beeinflusst nicht den Inhalt der enthaltenen `TabPage`-Instanzen, sondern ausschließlich die Darstellung der Tabs selbst. Durch größere Werte kann mehr Platz zwischen dem Rand eines Tabs und dessen Inhalt geschaffen werden, während kleinere Werte zu einer kompakteren Darstellung führen.

#### **RowCount** \[System.Int32\]

Der Wert von `RowCount` gibt an, aus wie vielen Reihen die Tabs aktuell bestehen. Standardmäßig beträgt der Wert `0`, solange sich keine `TabPage` im `TabControl` befindet. Die Eigenschaft wird vom `TabControl` automatisch ermittelt und kann nicht direkt festgelegt werden. Besonders relevant ist `RowCount`, wenn `Multiline` auf `True` gesetzt ist, da die Tabs dann auf mehrere Reihen verteilt werden können.

#### **SelectedImageIndex** \[System.Int32\]

Der Wert von `SelectedImageIndex` legt den Index des Bildes fest, das für den aktuell ausgewählten Tab verwendet werden soll. Standardmäßig ist diese Eigenschaft auf `-1` gesetzt, wodurch kein spezielles Bild für den aktiven Tab definiert ist. Die Bilder werden dabei aus der dem `TabControl` zugewiesenen `ImageList` bezogen.

Ist ein gültiger Bildindex angegeben, kann für den ausgewählten Tab ein anderes Symbol als für die übrigen Tabs dargestellt werden. Die Eigenschaft wird hauptsächlich in Verbindung mit einer `ImageList` verwendet und hat ohne zugewiesene Bilder keine sichtbare Auswirkung.

#### **SelectedIndex** \[System.Int32\]

Der Wert von `SelectedIndex` entspricht dem Index des aktuell aktiven `TabPage`. Die `TabPages`-Collection ist 0-basiert, weshalb das erste `TabPage` den Index `0` besitzt. Befindet sich mindestens ein `TabPage` im `TabControl`, ist standardmäßig das erste `TabPage` aktiv. Ist die `TabPages`-Collection leer, beträgt der Wert von `SelectedIndex` `-1`.

#### **SelectedTab** \[System.Windows.Forms.TabPage\]

Der Wert von `SelectedTab` enthält eine Referenz auf die aktuell aktive `TabPage` des `TabControl`. Standardmäßig ist diese Eigenschaft auf `$null` gesetzt, solange sich keine `TabPage` in der `TabPages`-Collection befindet. Sobald mindestens ein `TabPage` vorhanden ist, verweist `SelectedTab` auf das aktuell ausgewählte `TabPage`. Über diese Eigenschaft kann sowohl das aktive `TabPage` ausgelesen als auch ein anderes `TabPage` direkt ausgewählt werden.

#### **ShowToolTips** \[System.Boolean\]

Der Wert von `ShowToolTips` legt fest, ob für die Tabs eines `TabControl` Tooltips angezeigt werden dürfen. Standardmäßig ist diese Eigenschaft auf `$false` gesetzt, wodurch keine Tooltips dargestellt werden. Wird `ShowToolTips` auf `$true` gesetzt, können einzelnen `TabPage`-Instanzen Tooltip-Texte zugewiesen werden, die beim Überfahren des jeweiligen Tabs mit dem Mauszeiger angezeigt werden.

#### **SizeMode** \[Systems.Windows.Forms.TabSizeMode\]

Der Wert von `SizeMode` legt fest, wie die Größe der einzelnen Tabs bestimmt wird. Standardmäßig ist diese Eigenschaft auf `Normal` gesetzt, wodurch die Breite jedes Tabs automatisch anhand seines Inhalts berechnet wird. Wird `SizeMode` auf `Fixed` gesetzt, erhalten alle Tabs dieselbe Größe, die über die Eigenschaft `ItemSize` festgelegt werden kann.

#### **TabPages** \[Systems.Windows.Forms.TabControl.TabPageCollection\]

Der Wert von `TabPages` enthält die Sammlung aller `TabPage`-Instanzen, die dem `TabControl` hinzugefügt wurden. Standardmäßig ist diese Sammlung leer. Über `TabPages` können `TabPage`-Instanzen hinzugefügt, entfernt oder anhand ihres Indexes bzw. ihrer Referenz abgerufen werden. Die Reihenfolge der Elemente innerhalb der Sammlung entspricht dabei der Reihenfolge der Tabs im `TabControl`.

---

## **Methoden**

<details id="bkmrk-methoden-beispiel-me"><summary>Methoden</summary>

##### *TabControl*

- **GetTabRect** – Gibt die Position und Größe vom TabPage-Reiter zurück

##### *TabControl.TabPages*

- **Add** – Fügt eine `TabPage`-Instanz hinzu
- **AddRange** – Fügt mehrere `TabPage`-Instanzen hinzu
- **Clear** – Entfernt alle `TabPage`-Instanzen
- **Insert** – Fügt eine `TabPage`-Instanz an einer gewünschten Stelle hinzu
- **Remove** – Entfernt eine `TabPage`-Instanz
- **RemoveAt** – Entfernt mit dem Index eine `TabPage`-Instanz

</details>### *TabControl*

#### **GetTabRect()**

**Datentyp:** \[System.Drawing.Rectangle\] **Rückgabewert:** Position und Größe des Tab-Headers

```PowerShell
GetTabRect( Index )
```

Die Methode `GetTabRect()` gibt die Position und Größe eines Tabs innerhalb des `TabControl` zurück.   
Über den angegebenen Index wird festgelegt, für welchen Tab die Informationen ermittelt werden sollen. Der Rückgabewert ist ein `Rectangle`, das die Position sowie die Breite und Höhe des entsprechenden Tab-Headers enthält. Die zurückgegebenen Koordinaten beziehen sich auf das TabControl selbst.

Die Methode wird häufig verwendet, um Mausklicks auf einzelne Tabs zu erkennen oder um eigene Zeichnungslogik mit `OwnerDrawFixed` umzusetzen.

### *TabControl.TabPages*

#### **Add()**

```PowerShell
$_.TabPages.Add( $tabPage )
# → vorhandenes TabPage hinzufügen
```

Die Methode `Add()` fügt eine `TabPage` zur `TabPages`-Collection hinzu. Das `TabPage` wird dabei am Ende der Sammlung eingefügt und erscheint als neuer Tab im `TabControl`.

Nach dem Hinzufügen kann das `TabPage` über die `TabPages`-Collection, `SelectedIndex` oder `SelectedTab` verwendet werden.

```powershell
$_.TabPages.Add( "Text" )
# → Neues TabPage mit Text erstellen
```

Der übergebene Text wird dabei als Beschriftung des Tabs verwendet.

```PowerShell
$_.TabPages.Add( "Name", "Text" )
# → Neues TabPage mit Name und Text erstellen
```

Hierbei wird sowohl der interne `Name` als auch die sichtbare Beschriftung (`Text`) festgelegt.

Diese Varianten eignen sich für einfache Tabs, bei denen keine weiteren Eigenschaften unmittelbar gesetzt werden müssen.

#### **AddRange()**

```PowerShell
$_.TabPages.AddRange( $tabPages )
```

Die Methode `AddRange()` fügt mehrere `TabPage`-Instanzen gleichzeitig zur `TabPages`-Collection hinzu.

Die Reihenfolge der Elemente im Array entspricht anschließend der Reihenfolge der Tabs im `TabControl`. Die Tabs werden dabei am Ende der vorhandenen Sammlung eingefügt.

#### **Clear()**

```PowerShell
$_.TabPages.Clear()
```

Die Methode `Clear()` entfernt alle `TabPage`-Instanzen aus der `TabPages`-Collection.

Nach dem Aufruf enthält das `TabControl` keine Tabs mehr. Existiert kein Tab mehr, besitzen Eigenschaften wie `SelectedIndex` den Wert `-1` und `SelectedTab` den Wert `$null`.

#### **Insert()**

```PowerShell
$_.TabPages.Insert( Index, $tabPage )
```

Die Methode `Insert()` fügt eine `TabPage` an einer bestimmten Position innerhalb der `TabPages`-Collection ein.

Bereits vorhandene Einträge werden ab dieser Position um eine Stelle nach hinten verschoben. Dadurch kann die Reihenfolge der Tabs gezielt beeinflusst werden.

#### **Remove()**

```PowerShell
$_.TabPages.Remove( $tabPage )
```

Die Methode `Remove()` entfernt eine bestimmte `TabPage` aus der `TabPages`-Collection.

Das `TabPage` selbst wird dabei nicht gelöscht, sondern lediglich aus dem `TabControl` entfernt. Es kann später erneut einer `TabPages`-Collection hinzugefügt werden.

#### **RemoveAt()**

```PowerShell
$_.TabPages.RemoveAt( Index )
```

Die Methode `RemoveAt()` entfernt die `TabPage` an der angegebenen Position aus der `TabPages`-Collection.

Die verbleibenden Einträge rücken anschließend entsprechend nach vorne auf. Dadurch können sich die Indizes nachfolgender TabPages ändern.

---

## **Events**

<details id="bkmrk-events-beispiel-even"><summary>Events</summary>

- **Selecting** – Vor dem Wechsel zum TabPage
- **Selected** – Nach dem Wechsel zum TabPage
- **Deselecting** – Vor dem Verlassen vom TabPage
- **Deselected** – Nach dem Verlassen vom TabPage
- **SelectedIndexChanged** – Ausgewählter TabPage hat sich geändert
- **Click** – Mausklick auf das TabControl
- **DoubleClick** – Doppelklick auf das TabControl
- **MouseDown** – Maustaste wurde auf dem TabControl gedrückt
- **MouseMove** – Maus wurde über dem TabControl bewegt
- **MouseUp** – Gedrückte Maustaste wurde auf dem TabControl losgelassen
- **MouseEnter** – Mauszeiger betritt den Bereich des TabControl
- **MouseLeave** – Mauszeiger verlässt den Bereich des TabControl
- **DragDrop** – Element wurde per Drag&amp;Drop auf dem TabControl abgelegt
- **KeyDown** – Taste wurde gedrückt während das TabControl den Fokus hat
- **ControlAdded** – Dem TabControl wurde ein TabPage hinzugefügt
- **ControlRemoved** – Vom TabControl wurde ein TabPage entfernt

- **Resize** – Größe des TabControl hat sich geändert
- **Paint** – TabControl wird neu gezeichnet

</details>```powershell
$tabControl.Add_*
  param($sender, $e)
```

- `$sender` → Das TabControl selbst (=`$this`)
- `$e` *(EventArgs) →* Argumente Tab (TabPage) entsprechend

---

### **Tabwechsel**

#### **Selecting**

Vor dem Wechsel zu einem Tab (TabPage) → kann noch abgebrochen werden

- `$e.TabPage` → der Tab, zu dem gewechselt werden soll
- `$e.TabPageIndex` → Index davon
- `$e.Cancel` → kannst du auf `$true` setzen → Wechsel wird verhindert

#### **Selected**

Nach dem Wechsel zu einem Tab (TabPage)

- `$e.TabPage` → der Tab, zu dem gewechselt wurde
- `$e.TabPageIndex` → Index davon

```powershell
$tabControl.Add_Selecting({
    param($sender, $e)

    # Ziel-Tab
    $nextTab = $e.TabPage

    if ($nextTab.Name -eq "PackageTab") {
        Write-Host "Wechsel verhindert!"

        # 🚨 DAS ist der Trick:
        $e.Cancel = $true
    }
})

```

---

#### **SelectedIndexChanged**

Wird ausgelöst, **nachdem sich der ausgewählte Tab geändert hat**

- ❌ Kein `$e.TabPage`
- ✅ `$sender.SelectedTab` → aktuell aktiver Tab
- ✅ `$sender.SelectedIndex` → Index des aktiven Tabs

<details id="bkmrk-%24sender%2C-%24e-%24sender-"><summary>$sender, $e</summary>

#### `<strong>$sender</strong>`

- `$sender.SelectedTab` → aktuell aktiver Tab (`TabPage`)
- `$sender.SelectedIndex` → Index davon
- `$sender.TabPages` → alle Tabs (Collection)
- `$sender.TabCount` → Anzahl Tabs
- `$sender.Name` → Name vom Control
- `$sender.Enabled` → ob aktiv
- `$sender.Visible` → sichtbar oder nicht

#### `<strong>$e</strong>`

- einfach nur ein Standard-EventArgs-Objekt
- ohne nützliche Zusatzinfos

</details>```powershell
$tabControl.Add_SelectedIndexChanged({
    param($sender, $e)

    # Aktueller Tab
    $currentTab = $sender.SelectedTab

    Write-Host "Aktiver Tab: $($currentTab.Name)"
})
```

#### **Deselecting**

Vor dem Verlassen eines Tabs (TabPage) → kann noch abgebrochen werden

```powershell
$tabControl.Add_Deselecting({
    param($sender, $e)

    # Aktueller Tab (der verlassen wird)
    $currentTab = $e.TabPage

    # Beispiel: Verhindere Verlassen wenn noch Auswahl vorhanden
    if ($currentTab.Name -eq "PackageTab" -and $checkedListBox.CheckedItems.Count -gt 0) {
        Write-Host "Du hast noch Auswahl!"

        $e.Cancel = $true
    }
})

```

`$e` *(EventArgs)  
•* `$e.TabPage` → der Tab, der verlassen wird  
• `$e.Cancel` → **`$true` = Wechsel wird verhindert**

#### **Deselected**

Nach dem Verlassen eines Tabs → ideal zum Zurücksetzen von UI

```powershell
$tabControl.Add_Deselected({
    param($sender, $e)

    # Verlassener Tab
    $oldTab = $e.TabPage

    if ($oldTab.Name -eq "PackageTab") {

        # CheckedListBox zurücksetzen
        for ($i = 0; $i -lt $checkedListBox.Items.Count; $i++) {
            $checkedListBox.SetItemChecked($i, $false)
        }

        # ListBox zurücksetzen
        $listBox.ClearSelected()
    }
})

```

`$e` *(EventArgs)  
•* `$e.TabPage` → der Tab, der verlassen wurde

##### <span role="text">🔍 **Konkreter Ablauf beim Tabwechsel**</span>

Wenn du von **Tab A → Tab B** wechselst:

1. **`Deselecting`** *(TabControl)*  
    → bevor Tab A verlassen wird  
    → **kann abgebrochen werden** (`$_.Cancel = $true`)
2. **`Selecting`** *(TabControl)*  
    → bevor Tab B aktiviert wird  
    → **kann ebenfalls abgebrochen werden**

<p class="callout info">👉 Wenn hier keiner abbricht, geht’s weiter:</p>

3. **`Deselected`** *(TabControl)*  
    → Tab A wurde gerade deaktiviert
4. **`SelectedIndexChanged`** *(TabControl)*  
    → der Index hat sich geändert
5. **`Selected`** *(TabControl)*  
    → Tab B ist jetzt aktiv
6. **`Leave`** *(TabPage A)*  
    → Fokus verlässt alten Tab
7. **`Enter`** *(TabPage B)*  
    → Fokus betritt neuen Tab

---

#### **Click**

Klick auf das Control (selten relevant)

#### **MouseDown**

Klick einer beliebigen Maustaste auf dem TabControl

- `$e.Button` – gedrückte Maustaste → `[MouseButtons]::Left`
- `$e.Location` – Position des Mausklicks

---

#### **ControlAdded / ControlRemoved**

Wenn TabPages hinzugefügt oder entfernt werden. Wird ausgelöst, wenn ein TabPage zur TabPages-Collection  
hinzugefügt oder daraus entfernt wird.

---

## **Tipps &amp; Tricks** *- TabControl*

---

#### Typische Stolperfallen

- **Tab wird nicht angezeigt**  
    → nicht zur `TabPages`-Collection hinzugefügt
- **Events greifen nicht**  
    → falsches Event verwendet (`Selecting` vs. `SelectedIndexChanged`)
- **Layout wirkt falsch**  
    → `Dock` / `Anchor` nicht sauber gesetzt
- **Icons fehlen**  
    → `ImageList` nicht gesetzt oder falscher Index

---

### Mentales Modell

Das `TabControl` ist ein **Container mit Umschalter-Logik**.

Es zeigt genau eine `TabPage` gleichzeitig  
und verwaltet nur, welche sichtbar ist.

---

### Wann sinnvoll?

- Strukturierung komplexer Inhalte
- Einstellungen / Optionen
- Platz sparen

---

### Wann vermeiden?

- Häufiges Hin- und Herspringen notwendig
- Linearer Workflow
- Stark voneinander abhängige Inhalte

---

# TableLayoutPanel

**<span class="BZ_Pyq_fadeIn">1. </span><span class="BZ_Pyq_fadeIn">Grid-</span><span class="BZ_Pyq_fadeIn">Struktur</span>**  
<span class="BZ_Pyq_fadeIn">Ein </span>`TableLayoutPanel` <span class="BZ_Pyq_fadeIn">ist </span><span class="BZ_Pyq_fadeIn">im </span><span class="BZ_Pyq_fadeIn">Kern </span><span class="BZ_Pyq_fadeIn">ein </span>**<span class="BZ_Pyq_fadeIn">dynamisches </span><span class="BZ_Pyq_fadeIn">Raster</span>** <span class="BZ_Pyq_fadeIn">aus:</span>

- `RowCount`
- `ColumnCount`
- `RowStyles`
- `ColumnStyles`

<span class="BZ_Pyq_fadeIn">Die </span><span class="BZ_Pyq_fadeIn">Styles </span><span class="BZ_Pyq_fadeIn">bestimmen </span>**<span class="BZ_Pyq_fadeIn">wie </span><span class="BZ_Pyq_fadeIn">der </span><span class="BZ_Pyq_fadeIn">Platz </span><span class="BZ_Pyq_fadeIn">verteilt </span><span class="BZ_Pyq_fadeIn">wird</span>**<span class="BZ_Pyq_fadeIn">, </span><span class="BZ_Pyq_fadeIn">nicht </span><span class="BZ_Pyq_fadeIn">nur </span><span class="BZ_Pyq_fadeIn">wie </span><span class="BZ_Pyq_fadeIn">viele </span><span class="BZ_Pyq_fadeIn">Zellen </span><span class="BZ_Pyq_fadeIn">existieren.</span>

# TabPage

Eine `TabPage` ist im Grunde eine einzelne Seite innerhalb eines [`TabControl`](https://doku.borinas.com/books/powershell-programmierung/page/tabcontrol "TabControl").  
Sie stellt den Inhalt dar, der angezeigt wird, wenn ein bestimmter Tab ausgewählt ist.

<details id="bkmrk-eigenschaften-beispi"><summary>Eigenschaften</summary>

<span style="text-decoration: underline;">***Beispiel Eigenschaft***</span>

- ***Eigenschaft**– Standardwert*  
    Beschreibung oder Erläuterung der Eigenschaft

---

- **Text** –   
    Der Titel des Tabs (sichtbar im Reiter)
- **Name** –   
    Interner Name zur Referenzierung im Code
- **Controls** –   
    Sammlung aller enthaltenen Controls
- **Dock** –   
    Bestimmt, wie sich die TabPage innerhalb des `TabControl` verhält  
    (meist automatisch `Fill`, alles andere ist selten sinnvoll)
- **Enabled** –   
    Legt fest, ob der Tab auswählbar ist
- **Visible** –   
    Bestimmt, ob der Tab angezeigt wird
- **ForeColor** –   
    Farben der Seite (abhängig vom Theme)
- **BackColor** –   
    Farben der Seite (abhängig vom Theme)
- **Padding** –   
    Innenabstand zum Rand

</details>---

### Grundidee

Ein `TabControl` ist der Container für die jeweiligen `TabPage`-Instanzen.  
`TabPage` beinhaltet den sichtbaren Inhalt und `TabControl` ist das möglichmachende Gerüst.

---

### Beispiel (PowerShell)

```powershell
$tabControl = [System.Windows.Forms.TabControl]::New()

# Erstellen
$tabPage1 = New-Object System.Windows.Forms.TabPage
$tabPage2 = [System.Windows.Forms.TabPage]::New()

# Hinzufügen
$tabControl.TabPages.Add($tabPage2) # TabPage → TabControl

# Erstellen & Hinzufügen
$tabControl.TabPages.Add("Dritter Tab") # Text = "Dritter Tab"
$tabControl.TabPages.Add("tabPage4", "Vierter Tab") # Name = "tabPage4", Text = "Vierter Tab"
```

---

### Controls hinzufügen

```powershell
$button = New-Object System.Windows.Forms.Button
$button.Text = "Klick mich"

$tabPage1.Controls.Add($button)

```

Wichtig:  
Controls werden **immer direkt zur TabPage hinzugefügt**, nicht zum TabControl.

---

### Tab wechseln (programmatisch)

```powershell
$tabControl.SelectedTab = $tabPage2

```

oder

```powershell
$tabControl.SelectedIndex = 1

```

---

### Events

`TabPage` selbst hat keine super-spezifischen Events wie das `TabControl`.  
Sie verhält sich eher wie ein normales `Panel`.

Trotzdem gibt es ein paar relevante:

- **Enter**  
    Wird ausgelöst, wenn die TabPage aktiv wird (Tab wird ausgewählt)
- **Leave**  
    Wird ausgelöst, wenn die TabPage verlassen wird
- **Click**  
    Klick irgendwo auf der TabPage (nicht super hilfreich in der Praxis)
- **ControlAdded / ControlRemoved**  
    Wenn Controls dynamisch hinzugefügt oder entfernt werden
- **Paint**  
    Wenn die TabPage neu gezeichnet wird (Custom Drawing, eher selten)

---

### Wichtig: Tab-Wechsel-Events liegen am TabControl

Wenn du wirklich auf Tab-Wechsel reagieren willst, bist du hier falsch unterwegs:

- `SelectedIndexChanged`
- `Selecting`
- `Selected`

Die gehören zum **TabControl**, nicht zur TabPage.

Das ist einer dieser klassischen Momente, wo WinForms dich still verarscht.

---

### Typische Stolperfallen

- **Controls erscheinen nicht**  
    → falscher Parent (nicht der TabPage hinzugefügt)
- **Layout wirkt kaputt**  
    → falsches `Dock` / `Anchor` innerhalb der TabPage
- **Events feuern nicht wie erwartet**  
    → falsches Objekt (TabPage vs. TabControl verwechselt)

---

### Mentales Modell

Denk nicht in "Tabs".  
Denk in **Panels mit Umschalter**.

Jede `TabPage` ist einfach ein eigenes Panel.  
Das `TabControl` entscheidet nur, welches sichtbar ist.

---

### Wann sinnvoll?

- Einstellungen gruppieren
- Inhalte strukturieren
- Platz sparen

---

### Wann vermeiden?

- Wenn Nutzer ständig wechseln müssen
- Wenn Inhalte stark zusammenhängen

---

# GroupBox

Eine `<a href="https://doku.borinas.com/books/powershell-programmierung/page/groupbox" title="GroupBox">GroupBox</a>` ist ein Container zur visuellen Gruppierung von Controls.  
Sie dient hauptsächlich dazu, zusammengehörige Eingabefelder, Optionen oder Steuerelemente optisch voneinander abzugrenzen.

Der Text der `GroupBox` wird als Überschrift im Rahmen dargestellt

---

## **Basics**

Das `GroupBox` selbst enthält keine besondere Logik.

- `GroupBox` → Container mit Beschriftung
- enthaltene Controls → eigentlicher Inhalt

#### **GroupBox erstellen**

```powershell
# Klassisch
$groupBox = New-Object System.Windows.Forms.GroupBox

# .NET-Style
$groupBox = [System.Windows.Forms.GroupBox]::new()

```

#### **Controls hinzufügen**

Controls werden über die `Controls`-Collection hinzugefügt.

```powershell
$groupBox.Controls.Add($textBox)

$groupBox.Controls.AddRange(@(
    $label,
    $button
))

```

#### **Controls entfernen**

```powershell
$groupBox.Controls.Remove($textBox)

$groupBox.Controls.Clear()

```

---

## **Eigenschaften**

<details id="bkmrk-eigenschaften-%2F-prop"><summary>Eigenschaften / Propertys</summary>

- **Text** – Überschrift der GroupBox
- **Controls** – Enthaltene Controls
- **Anchor** – Verankerung an den Rändern des Parent-Containers
- **Dock** – Automatische Ausrichtung im Parent-Container
- **AutoSize** – Größe automatisch an Inhalt anpassen
- **Enabled** – Aktiviert oder deaktiviert enthaltene Controls
- **Visible** – Sichtbarkeit der GroupBox
- **Font** – Schriftart der Überschrift
- **ForeColor** – Farbe der Überschrift
- **Padding** – Innenabstand für enthaltene Controls

</details>#### **Text** \[System.String\]

Der Wert von `Text` bestimmt die Beschriftung der GroupBox.

Standardmäßig ist der Wert leer.

```powershell
$groupBox.Text = "Office Installation"

```

#### **Controls** \[System.Windows.Forms.Control.ControlCollection\]

Enthält alle Controls, die sich innerhalb der GroupBox befinden.

```powershell
$groupBox.Controls.Add($button)

```

#### **Enabled** \[System.Boolean\]

Legt fest, ob die GroupBox aktiviert ist.

Wird `Enabled` auf `$false` gesetzt, werden auch alle enthaltenen Controls deaktiviert.

```powershell
$groupBox.Enabled = $false

```

#### **Padding** \[System.Windows.Forms.Padding\]

Legt den Innenabstand fest, der zwischen Rahmen und enthaltenen Controls eingehalten wird.

```powershell
$groupBox.Padding = 10

```

---

## **Methoden**

### *GroupBox.Controls*

<details id="bkmrk-methoden-groupbox.co"><summary>Methoden – Controls</summary>

- **Add** – Fügt ein Control hinzu
- **AddRange** – Fügt mehrere Controls hinzu
- **Remove** – Entfernt ein Control
- **Clear** – Entfernt alle Controls

</details>#### **Add()**

```powershell
$_.Controls.Add($control)

```

Die Methode `Add()` fügt ein Control zur `Controls`-Collection der GroupBox hinzu.

#### **AddRange()**

```powershell
$_.Controls.AddRange(@(
    $label,
    $textbox,
    $button
))

```

Die Methode `AddRange()` fügt mehrere Controls gleichzeitig zur `Controls`-Collection hinzu.

#### **Remove()**

```powershell
$_.Controls.Remove($control)

```

Die Methode `Remove()` entfernt ein bestimmtes Control aus der GroupBox.

#### **Clear()**

```powershell
$_.Controls.Clear()

```

Die Methode `Clear()` entfernt alle enthaltenen Controls.

---

## **Events**

<details id="bkmrk-events-click-%E2%80%93-mausk"><summary>Events</summary>

- **Click** – Mausklick auf die GroupBox
- **DoubleClick** – Doppelklick auf die GroupBox
- **MouseDown** – Maustaste wurde gedrückt
- **MouseUp** – Maustaste wurde losgelassen
- **MouseMove** – Maus wurde bewegt
- **MouseEnter** – Mauszeiger betritt die GroupBox
- **MouseLeave** – Mauszeiger verlässt die GroupBox
- **Enter** – Fokus betritt die GroupBox
- **Leave** – Fokus verlässt die GroupBox
- **ControlAdded** – Ein Control wurde hinzugefügt
- **ControlRemoved** – Ein Control wurde entfernt
- **Resize** – Größe wurde geändert
- **Paint** – GroupBox wird neu gezeichnet

</details>```powershell
$groupBox.Add_*({
    param($sender, $e)
})

```

- `$sender` → Die GroupBox selbst (`$this`)
- `$e` → EventArgs des jeweiligen Events

#### **ControlAdded / ControlRemoved**

Werden ausgelöst, wenn Controls zur `Controls`-Collection hinzugefügt oder daraus entfernt werden.

```powershell
$groupBox.Add_ControlAdded({
    param($sender, $e)

    Write-Host "$($e.Control.Name) wurde hinzugefügt"
})

```

---

## **Tipps &amp; Tricks**

### Typische Stolperfallen

- **Controls erscheinen nicht**
    
    
    - Position liegt außerhalb der GroupBox
- **Alle Controls werden deaktiviert**
    
    
    - `GroupBox.Enabled = $false`
- **Padding erzeugt kein automatisches Layout**
    
    
    - Controls müssen weiterhin selbst positioniert werden
- **GroupBox für Layout verwendet**
    
    
    - Für komplexe Layouts meist besser: `Panel`, `FlowLayoutPanel` oder `TableLayoutPanel`

---

## Mentales Modell

Die `GroupBox` ist ein **Container mit Beschriftung**.

Sie gruppiert Controls optisch und logisch, besitzt jedoch keine eigene Inhaltslogik.

---

## Wann sinnvoll?

- Einstellungen gruppieren
- Formulare strukturieren
- Optionen zusammenfassen
- RadioButtons logisch gruppieren

---

## Wann vermeiden?

- Komplexe Layouts
- Scrollbare Bereiche
- Dynamische Containerlogik
- Wenn lediglich ein Rahmen benötigt wird