Archiv der Kategorie: Programmierung

Neuronale Netze (KNN) / KI-Training: Das Format / der Aufbau vom MNIST-Datensatz (MNIST Datenbank) der Dateien t10k-images-idx3-ubyte, t10k-labels-idx1-ubyte, train-images-idx3-ubyte, train-labels-idx1-ubyte

Intention

Zum Auffrischen des eigenen Wissens über künstliche neuronale Netze (KNN) möchte man sich mit Frameworks wie PyTorch oder TensorFlow auseinandersetzen.

Problem

In den ersten Tutorials ist meistens die Rede vom „MNIST-Datensatz“ oder der „MNIST Datenbank“ mit 70.000 handgeschriebenen Ziffern im Format 28×28 mit 256 Grauwerten je Pixel (also je Byte). 60.000 Bilder davon sind zum Trainieren, 10.000 Bilder zum Testen eines neuronalen Netzes. Die Dateiendung der entpackten Dateien lässt sich nicht einfach in *.bmp umbenennen und zum Beispiel mit Paint öffnen. Man weiß erstmal nicht in welchem Format die Dateien sind um sich einzelne Zahlen anzusehen.

Laut „https://yann.lecun.com/exdb/mnist“ (manchmal nur über einen archive.org-Snapshot erreichbar) handelt es sich bei diesem Format nicht um ein Standard-Bildformat. Man muss ein eigenes Programm schreiben um diese Bilder zu interpretieren.

Analyse

train-images-idx3-ubyte, t10k-images-idx3-ubyte

Diese Dateien sind mit GZip (Endung *.gz) gepackt und lassen sich in Windows direkt mit einem Doppelklick öffnen oder mit einem Rechtsklick extrahieren:

Die *-images*-Dateien enthalten Bilder von handgeschriebenen Ziffern zwischen 0 und 9, die von Studenten und Mitarbeitern der Universität von South Carolina Beaufort im Jahre 1994 gesammelt wurden.

Öffnet man die extrahierten Dateien in einem Hexadezimaleditor wie zum Beispiel dem kostenlosen HxD-Editor und stellt die Spaltenanzahl auf 28 um, ist bereits ein Muster der enthaltenen Zahlen erkennbar:

Die ersten 16 Byte haben den folgenden Aufbau:

[offset] [type]          [value]          [description]
0000     32 bit integer  0x00000803(2051) magic number
0004     32 bit integer  60000            number of images
0008     32 bit integer  28               number of rows
0012     32 bit integer  28               number of columns
0016     unsigned byte   ??               pixel
0017     unsigned byte   ??               pixel
........
xxxx     unsigned byte   ??               pixel

Die 0x08 des dritten Bytes in der Magic Number sagt aus, dass es sich hierbei um UByte-Werte anhandelt. Das dritte Byte kann dabei die folgenden Werte annehmen:

The third byte codes the type of the data:
0x08: unsigned byte
0x09: signed byte
0x0B: short (2 bytes)
0x0C: int (4 bytes)
0x0D: float (4 bytes)
0x0E: double (8 bytes)

Das vierte Byte in der Magic Number hat hier den Wert 0x03, was bedeutet das unsere Daten 3 Dimensionen für den Pixel haben (x-Pos, y-Pos, Pixelwert/Grauwert[0-255]).

Entfernt man den markierten Header mit den ersten 16 Bytes (siehe obiges Bild) z.B. im HxD, indem man einfach die Entfernen-Taste drückt, ist das Schriftmuster bereits im HEX-Editor erkennbar:

Wie bereits erwähnt, hat jeder Pixel einen Wert zwischen 0 (weiß) und 255 (schwarz) [Magic Number: 3. Byte], wobei die Zwischenwerte lineare Abstufungen für Grauwerte sind.

Hier noch ein Beispiel der Fashion-MNIST-Datenbank mit Kleidungsstücken (von Zalando):

train-labels-idx1-ubyte, t10k-labels-idx1-ubyte

Der Aufbau der *-labels*-Dateien ist ähnlich. Als Label werden hier die Zahlen mit den Werten zwischen 0 bis 9 in der selben Reihenfolge wie in den *-images*-Dateien aufgeführt. Diese beginnen nach dem Header an Position 8 (hier 5 und 0 / unten wie oben im Screenshot):

Das Format ist also:

[offset] [type]         [value]          [description]
0000 32 bit integer 0x00000801(2049) magic number
0004 32 bit integer 60000 number of images
0008 byte [0-9] Ziffer zw. 0-9
……..

C#.NET: QR Code erzeugen mit QRCoder.dll / QR-Codes mit C# oder VB.NET erstellen / generieren

Beispiel: Kostenloser QR Code Generator

Hier ein kostenloser QR Code Generator, welcher in ASP.NET mit C# als Backend realisiert wurde:

https://www.my-asp-experiments.com/QRCode

Beschreibung

QRCoder ermöglicht das Erstellen von QR-Codes mit einer einfachen Bibliothek. Solange keine QR-Code-Version angegeben wird, passt sich die Größe der generierten QR-Codes an den zu codierenden Text an.

Die folgenden Parameter werden unterstützt:

  • ECC Level: Fehlerkorrekturlevel. Gibt an wieviel Redundanzen eingebaut werden um im Fehlerfalle trotzdem lesbare Q- Codes zu erhalten. Je höher der Level ist, desto weniger Text kann im QR-Code einer bestimmten Größe gespeichert werden. Es gibt 4 ECC-Level={L, M, Q, H}, die in der folgenden ENUM definiert sind:
    QRCodeGenerator.ECCLevel
  • Version (optional): Die QR-Code-Version legt eine feste Größe (Modulanzahl) für den generierten QR Code fest. Die QR-Code-Größe definiert sich durch die Anzahl der „Module“ in der Matrix. Ein „Module“ definiert ein Quadrat im QR-Code. Wird keine Version angegeben, vergrößert sich der QR-Code automatisch auf die entsprechende Version.
    Beispiele: Version 1 hat eine Größe von 21×21 Modulen, also 1(21×21), Version 2(25×25), 3(29×29), 4(33×33), 5(37×37), 6(41×41), 7(45×45), 8(49×49), 9(53×53), 10(57×57), 11(61×61) …
  • Pixel pro Modul: Die Anzahl der verwendeten Pixel für die Darstellung eines Moduls im QR-Code. Da Module im QR-Code quadratisch sind, gilt der Parameter für Breite und Höhe. Durch die exakte Festlegung der Pixel kann verhindert werden, dass der QR-Code wegen einer Skalierung unscharf oder verschwommen wird (wg. Aliasing).
  • Text (bzw. Textformat des zu codierenden Textes): Ein codierter Text kann numerische, alphanumerische, binäre oder Kanji-Zeichen enthalten. Die Reihenfolge dieser möglichen Zeichensätze legt von links nach rechts fest, wieviel Text insgesamt im QR-Code enthalten sein kann. Rein numerische Zeichen benötigen wenig Platz im QR-Code, wodurch für eine festgelegte Größe (Version) z.B. viel mehr Text codiert werden kann. Wenn ein Text aber nur ein einziges Kanji-Zeichen beinhaltet, wird der gesamte Textinhalt für Kanji codiert, was dazu führt dass weniger Text für eine Version (festgelegte Anzahl von Modulen) codiert werden kann.

Die folgende Tabelle verdeutlicht anhand der oben genannten Parameter, welche Datenkapazität (Textlänge) nach Version, ECC Level und verwendetem Zeichensatz erwartet werden kann:

Version (Modulzahl)NumerischAlphanumerischBinärKanji
LMQHLMQHLMQHLMQH
1(21)4134271725201610171411710874
2(25)7763483447382920322620142016128
3(29)1271017758776147355342322432262015
4(33)187149111821149067507862463448382821
5(37)255202144106154122876410684604465523727
6(41)32225517813919515410884134106745882654536
7(45)37029320715422417812593154122866495755339
8(49)46136525920227922115712219215210884118936652
9(53)552432312235335262189143230180130981411118060
10(57)6525133642883953112211742712131511191671319374
11(61)77260442733146836625920032125117713719815510985
12(65)88369148937453541929622736728720315522617712596
13(69)1022796580427619483352259425331241177262204149109
14(73)1101871621468667528376283458362258194282223159120
15(77)1250991703530758600426321520412292220320254180136
16(81)14081082775602854656470365586450322250361277198154
17(85)15481212876674938734531408644504364280397310224173
18(89)172513469487461046816574452718560394310442345243191
19(93)1903150010638131153909644493792624442338488384272208
20(97)2061160011599191249970702557858666482382528410297235
21(101)22321708122496913521035742587929711509403572438314248
22(105)2409187213581056146011348236401003779565439618480348270

Installation von QRCoder.dll

Die Bibliothek „QRCoder“ kann über die gängigen Quellen bezogen werden und muss dann ggfs. als Verweis hinzugefügt werden:


Einige der genannten Quellen tun dies allerdings automatisch

GitHub

https://github.com/codebude/QRCoder

.NET CLI

dotnet add package QRCoder --version 1.4.3

Package Manager

NuGet\Install-Package QRCoder -Version 1.4.3

Package Reference

<PackageReference Include="QRCoder" Version="1.4.3" />

Paket CLI

paket add QRCoder --version 1.4.3

Script & Interactive

#r "nuget: QRCoder, 1.4.3"

Cake

// Install QRCoder as a Cake Addin
#addin nuget:?package=QRCoder&version=1.4.3

// Install QRCoder as a Cake Tool
#tool nuget:?package=QRCoder&version=1.4.3

C# Konsolenanwendung

Die folgende C# Konsolenanwendung erzeugt drei QR-Codes der Version 5 (also 37×37 Module) mit dem Fehlerkorrekturlevel L (niedrigster Korrekturlevel für viel Platz) und 4×4 Pixel pro Modul. Der Text ist binär, da er die Sonderzeichen einer URL aber keine Kanji-Zeichen beinhaltet. Der erste QR-Code beinhaltet nur den Text „d“ und wird deswegen als alphanumerisch klassifziert.

Außerdem wird der „Ruhebereich“ des QR-Codes (weißer Rand) abgeschnitten, was nicht immer zu empfehlen ist, da diese Trennung für einen korrekten Scan benötigt wird.

using QRCoder;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QRCodeTestConsole
{
    public class Program
    {
        public static Program prg;
        public const int version5QRCodeSize = 148 + 32;
        public const int pixelPerModule = 4;
        public const int moduleVersion = 5;
        public const int croppedBorderPixels = 16; 
        public const QRCodeGenerator.ECCLevel eccLevel = QRCodeGenerator.ECCLevel.L;

        public Program()
        {
            string[] content = { "d", 
                                 "https://www.youtube.com/playlist?list=PLA4OWG_-fY-JLoj-bHIPiUU1SJquB9X8J",
                                 "https://www.youtube.com/playlist?list=PLA4OWG_-fY-JLoj-bHIPiUU1SJquB9X8J----------------------...........oipipoiopiopipoipiooipo"};
            
            for (int i = 0; i < content.Length; i++)
            {
                string current = content[i];              

                string msgResult = this.GenerateQRCode(current, "QRCode" + i + ".bmp");
                
                if (!msgResult.ToLower().Equals("ok"))
                {
                    Console.WriteLine(msgResult);
                }
            }

            Console.ReadLine();
        }

        public string GenerateQRCode(string content, string fileName)
        {
            try
            {
                QRCodeGenerator qrGenerator = new QRCodeGenerator();
                QRCodeData qrCodeData = qrGenerator.CreateQrCode(content, eccLevel, false, false, QRCodeGenerator.EciMode.Default, moduleVersion);
                QRCode qrCode = new QRCode(qrCodeData);
                Bitmap qrCodeImage = new Bitmap(qrCode.GetGraphic(pixelPerModule));

                // Ruhezone abschneiden - falls nicht 
                // gewünscht bei qrCodeImage.Save aufrufen 
                Bitmap croppedBmp = qrCodeImage.Clone(new Rectangle(croppedBorderPixels, croppedBorderPixels, qrCodeImage.Width - 2 * croppedBorderPixels, qrCodeImage.Height - 2 * croppedBorderPixels), qrCodeImage.PixelFormat);
                croppedBmp.Save("CROP_" + fileName);
            }
            catch (Exception e)
            {
                return e.ToString();
            }

            return "ok";
        }

        public static void Main(string[] args)
        {
            prg = new Program();
        }
    }
}

AngularJS + NodeJS Schulung

Vorraussetzung

Installation der folgenden Komponenten:

NodeJS

Konsolenbefehle

npm init -y // erstellt die package.json und bestätigt alles mit y
npm install typescript --save-dev // Mit Typescript entwickeln - fügt in package.json eine devDependencies hinzu
npx tsc --init // Erzeugt tsconfig.json (enthält z.B. die Ecma-Script-Version)
npx tsc --watch // Schaut nach Änderungen
node src/hello.js // Interpretiert das Javascript damit wir keinen Browser brauchen mit Node kann man aufs Dateisystem zugreifen
npm installiert pakete
npx temporäres Paket

Aufgabe 1: Klassen & Methoden

Baue eine Klasse die etwas zurückgibt bei folgendem Code

let s: Student = new Student();
let label:string = s.getLabel(123457);
console.log(label); // "Student mit Matrikelnummer: 123456"

Lösung 1

class Student {
    oldNumber:number = -1;

    getLabel(matNr:number):string {
        if (matNr == 123456)
        {
            this.oldNumber = matNr;
            let zusammenGebaut = "Die Nr. " + this.oldNumber;
            return zusammenGebaut;
        }
        else
        {
            return `Student mit Matrikelnummer: ${matNr}`;
        }
    }
  }

Aufgabe 2: Konstrukturen

Sorge dafür, dass diese Ausgabe über einen Konstruktor funktioniert

let s: Student = new Student("Max", 123456);
let label: string = s.getLabel();
console.log(label); // "Student Max mit Matrikelnummer: 123456";

Lösung 2

class Student {
    name: string;
    mNr: number;

    constructor(_name: string, _mNr: number) {
      this.name = _name;
      this.mNr = _mNr;
    }

    getLabel() {
        return `Student ${this.name} mit Matrikelnummer ${this.mNr}`;
    }
  }

Aufgabe 3: Auslagern in andere Dateien

Lagere die Klasse Student in eine andere Datei aus, so dass sie mit den folgenden Kommandos wieder instanziert werden kann

import {Student} from "./student"

let s: Student = new Student("Max", 123456);
let label: string = s.getLabel();
console.log(label); // "Student Max mit Matrikelnummer: 123456";

Lösung 3

Lege die Klasse student.js im gleichen Verzeichnis wie hello.js an und kopiere die Student-Klasse von Lösung 2 rein:

export class Student {
    oldNumber:number = -1;
    name: string;
    mNr: number;

    constructor(_name: string, _mNr: number) {
      this.name = _name;
      this.mNr = _mNr;
    }

    getLabel() {
        return `Student ${this.name} mit Matrikelnummer ${this.mNr}`;
    }
  }

Aufgabe 4: ESLint für sauberen Code

Säubere den Code mit ESLint

Ansatz 4

Führe die folgenden Konsolenbefehle aus um ESLint zu verwenden:

npm init @eslint/config
npx eslint src/hello.ts

Beobachtung 4

ESLint meckert:

Lösung 4

Ändere den Code so, dass aus den Variablen aus Lösung 3 anstelle von let das Keyword const verwendet wird:

import {Student} from "./student"

const s: Student = new Student("Max", 123456);
const label: string = s.getLabel();
console.log(label); // "Student Max mit Matrikelnummer: 123456";

Microsoft Excel VBA: How to delete a worksheet in a workbook / Ein Arbeitsblatt in einer Arbeitsmappe löschen

Problem

A worksheet within a workbook shall be deleted via VBA in Excel.
Ein Arbeitsblatt soll in einer Arbeitsmappe gelöscht werden.

Approach / Ansatz

  • Create a new SUB method with the worksheet name as parameter / Definition einer neuen Sub-Methode mit Übergabe des Namen des zu löschenden Arbeitsblattes
  • Iterate all worksheets / über alle Arbeitsblätter iterieren
    • Check the name of the current worksheet / Namen der Worksheets prüfen
      • When the name matches the SUB paramter, the worksheet will get deleted via .Delete method / Wenn der Name des übergebenen Parameters entspricht, wird das worksheet gelöscht mit der .Delete Methode

Solution / Lösung

Sub DeleteWorksheet(worksheetName As String)
Dim ws As Worksheet, wb As Workbook

Set wb = ActiveWorkbook

Application.DisplayAlerts = False

For Each ws In wb.Worksheets
    If ws.Name = worksheetName Then
        ws.Delete
        Exit For
    End If
Next

Application.DisplayAlerts = True

End Sub

C# Threads: Was bedeutet „signaled“ beim AutoResetEvent-Konstruktor und im Kontext von AutoResetEvent?

Problemfeld

AutoResetEvent bietet über einen globalen oder übergeordneten Kontext die Möglichkeit durch den Aufruf der WaitOne-Methode innerhalb des Thread-Codes, Threads zu blockieren bis ein Signal aus einem anderem Kontext gesendet wird. Der andere Kontext sollte dabei auch Zugriff auf das steuernde AutoResetEvent-Objekt haben.

Konstruktor-Parameter

AutoResetEvent event_1 = new AutoResetEvent(true);
AutoResetEvent event_2 = new AutoResetEvent(false);

Ein Thread, welcher event_1.WaitOne() aufruft, blockiert nicht da signaled auf TRUE steht.

Ein Thread, welcher event_2.WaitOne() aufruft, blockiert da signaled über den Konstruktor mit FALSE initialisiert wurde.

Ruft ein Thread die Methode „WaitOne“ auf, wird dieser allerdings nur blockiert, wenn AutoResetEvent mit false initialisiert wurde.

Event auf „signaled“ ohne Konstruktor setzen

Um die blockierten Threads nun aus einem anderen Kontext, welcher auch Zugriff auf das AutoResetEvent-Objekt haben muss, zu steuern können die Methoden .Set() und .Reset() des AutoResetEvents aus dem steuerenden Kontext heraus genutzt werden:

// Setzt den Status auf "nicht signalisiert" / "non signaled" 
// Threads blockieren nach Aufruf von AutoResetEvent.WaitOne()
event_1.Reset(); 

// Setzt den Status auf "signalisiert" / "signaled"
// Threads blockieren NICHT nach Aufruf von AutoResetEvent.WaitOne()
event_2.Set(); 

Nur einer, der wartenden Threads erhält ein Signal wenn Set() aufgerufen wird. Will man alle wartenden Threads befreien, muss man Set() mehrfach aufrufen.

Unterschied zu ManualResetEvent

  • ManualResetEvent signalisiert allen wartenden Threads, dass diese die WaitOne()-Methode passieren dürfen. Sie ist also wie eine Tür, die sich für alle öffnet. ManualResetEvent.Reset(); schließt, die Tür, ManualResetEvent.Set(); öffnet die Tür.
  • AutoResetEvent signaliert maximal einem wartendendem Thread, dass er nach Aufruf der AutoResetEvent.WaitOne()-Methode weitermachen darf. Um alle blockierten Threads zu befreien, muss AutoResetEvent .Set() entsprechend der Anzahl wartender Threads aufgerufen werden. Wenn kein AutoResetEvent auf „signaled“ gesetzt wurde, wird auch niemand durchgelassen. Das entspricht der Warteschlange auf Ämtern oder Ärzten, wo man eine Nummer ziehen muss und immer nur einer aufgerufen wird.

Visual Studio : Project or Subproject does not compile / is not compiling or only compiles when the Build function in the context menu of the project explorer or in the VS menu is called explicitly

Problem

The project always shows an old status or an old version of the application when the „Start“-Button of Visual Studio has been clicked.

When the Build process is started manually before pressing the start button, the program version is the current / acutal one.

The target state should be that the program has always the current version without calling the BUILD funtion explicit.

Assumption

The root cause of this issue only occurs in a Visual Studio solution with more than one project.

Solution

  1. Make sure the project (i.e. „TestProject“) is set as startup project with the context menu (it appears bold in the project explorer).
  2. Right-click the Visual Studio Solution and select „Properties“.
  3. Got to the „configuration“ page in the appearing window (left side of the dialog)
  4. Select the „Build“ checkbox for the project that always shows the old status
  5. Press the OK button and try again.

WPF DataGrid MouseBinding Gesture List

Problem

The MouseBinding Tag in XAML is declaring a property „Gesture“, but it is hard to find a full reference / a complete list for all gestures of the MouseBinding tag.

Solution

Accepted gestures can be found in the enumeration MouseAction

LeftClick1A left mouse button click.
LeftDoubleClick5A left mouse button double-click.
MiddleClick3A middle mouse button click.
MiddleDoubleClick7A middle mouse button double-click.
None0No action.
RightClick2A right mouse button click.
RightDoubleClick6A right mouse button double-click.
WheelClick4A mouse wheel rotation.
List of possible gestures

Tortoise SVN: Show log with older messages and version history entries / it looks like commits are truncated or deleted after a point in time.

Problem

When you right click a folder in the Windows Explorer and you choose „TortoiseSVN -> Show log“ you are not able to see all entries. Even when you change the date range on the upper right of the window you can not fetch all entries.

It seems like that older SVN Server History entries are deleted. But in fact this is not the case.

Solution

First check the date range on the upper right of the window.

After that try to fetch the „lost“ entries by pressing the „Show All“ button on the lower left of the window.

C#.NET : Custom Collections anstelle von List erstellen über die mit foreach( … ) iteriert werden kann

Intention

Um die Klasse List<T> vor Zugriffen zu schützen / zu beschränken oder diese mit Notification-Events (z.B. ObservableCollection) auszustatten werden für den jeweiligen Verwendungszweck eigene Collections verwendet.

Ansatz

Um eine eigene Collection zu erstellen, welche mit foreach( … ) über die Elemente der eingekapselten Struktur (also z.B. List<T>) zu iterieren, benötigt man eine Enumerator-Klasse, welche die Methoden …

  • MoveNext()
  • Reset()
  • … und die Property „Current“ implementiert

implementiert (hier MyCollectionEnumerator) … sowie natürlich die gecustomizte Collection-Klasse selber, die die Datenstruktur enthält (hier MyCollection).

Lösung

using System;
using System.Collections.Generic;

namespace Irrsinn
{
    // Diese Klasse definiert mit MoveNext / Reset und der Property
    // Current aus dem Interface IEnumerator wie foreach(String s in col)
    // über die eingekapselte Liste iterieren darf.
    public class MyCollectionEnumerator : System.Collections.IEnumerator
    {
        private List<string> StringList;
        private int Counter = -1;

        // Im Konstruktor wird die Liste übergeben um die Operationen
        // MoveNext / Reset / Current darauf ausführen zu können
        public MyCollectionEnumerator(List<string> _StringList)
        {
            this.StringList = _StringList;
        }

        // Geht auf den nächsten Datensatz, der in der Liste 
        // gespeichert ist. Wenn MoveNext() false zurückgibt
        // ist das Ende der Liste erreicht
        public bool MoveNext()
        {
            Counter++;
            return Counter < StringList.Count;
        }

        // Setzt die Collection zurück.
        public void Reset()
        {
            Console.WriteLine("RESET!");
            Counter = -1;
        }

        // Gibt immer das aktuelle Element zurück, was mit 
        // MoveNext erreicht wurde
        public object Current
        {
            get
            {
                return StringList[Counter];
            }
        }
    }

    public class MyCollection
    {
        // Intern ist sind Collections meistens vom Typ List<T>
        // aber die eigene Implementierung gibt uns die Möglichkeit
        // den Zugriff auf die Liste anzupassen.
        private List<string> _StringList = new List<string>();

        // Hinzufügen eines Strings
        public void AddString(string newString)
        {
            this._StringList.Add(newString);
        }

        // GetEnumerator wird benötigt um mit foreach(...)
        // über die Collection zu iterieren
        public System.Collections.IEnumerator GetEnumerator()
        {
            return new MyCollectionEnumerator(_StringList);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Gecustomizte Collection
            MyCollection myCol = new MyCollection();
            myCol.AddString("Schatzi schenk mir ein Foto");
            myCol.AddString("schenk mir ein Foto vom Bier");
            myCol.AddString("Schatzi schenk mir ein Foto");
            myCol.AddString("Dann schenk ich Dir auch eins");
            myCol.AddString("vom Tier (also von den Muppets)");


            foreach (string s in myCol)
            {
                Console.WriteLine(s);
            }

            Console.ReadLine();
        }
    }
}

WPF MVVM: Was ist der Sinn hinter RelayCommand und wozu braucht man RelayCommand?

Problemfeld

RelayCommand (s) erfüllen den Zweck von Actions / Delegates und sind somit Funktionszeiger die es erlauben, zu einem späteren Zeitpunkt eine Methode aufzurufen.

Sie implementieren das Interface ICommand, welches von den WPF Controls genutzt wird, um bei Bedarf die Execute()-Methode aufzurufen. D.h. die Execute()-Methode soll nur dann aufgerufen werden, wenn das WPF Control das Ereignis tatsächlich auslöst und nicht bereits zur Bindung.

Ohne RelayCommands müsste man für jeden Command eine eigene Klasse schreiben, welche vom Interfache ICommand erbt. Sie helfen somit Code einzusparen.

Verwendung in einem MVVM Pattern

Häufige Verwendung: In einer XAML Datei wird ein Button definiert, der an einen RelayCommand gebunden wird, welcher zu einem späteren Zeitpunkt aufgerufen werden soll:

Das ViewModel, welches die View instanziert, muss einen RelayCommand „PerformCalibration“ definieren, der erst aufgerufen wird, wenn der Button geklickt wird (Relay) und nicht sofort bei der Bindung an den ButtonCommand.

Für diesen Zweck beinhaltet das ViewModel, welches die View instanziert, eine Funktion die den RelayCommand zurückgibt:
 

Der Button ruft die Funktion „Execute“ des implementierten RelayCommands auf, wenn er geklickt wird.

Da RelayCommands „Actions“ sind, haben Sie keinen Rückgabewert.