<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Visual Basic .NET Archive - Robert Skibbe</title>
	<atom:link href="https://robbelroot.de/blog/category/allgemein/tutorials/vbnet/feed/" rel="self" type="application/rss+xml" />
	<link>https://robbelroot.de/blog/category/allgemein/tutorials/vbnet/</link>
	<description>alias RobbelRoot – Freelance Full Stack Developer .NET</description>
	<lastBuildDate>Wed, 25 Mar 2026 08:51:51 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://robbelroot.de/wp-content/uploads/2020/12/cropped-favicon-32x32.png</url>
	<title>Visual Basic .NET Archive - Robert Skibbe</title>
	<link>https://robbelroot.de/blog/category/allgemein/tutorials/vbnet/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Visual Basic .NET lernen</title>
		<link>https://robbelroot.de/blog/visual-basic-net-lernen/</link>
					<comments>https://robbelroot.de/blog/visual-basic-net-lernen/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Sat, 21 Mar 2026 15:49:46 +0000</pubDate>
				<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET lernen]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=19607</guid>

					<description><![CDATA[<p>Visual Basic NET lernen möchte wirklich gelernt sein. Ich erinnere mich zu gut an meine Anfänge um das Jahr 2007 herum. Obwohl ich vermutlich wie jeder Anfänger alles Mögliche versucht habe in mein Gehirn aufzusaugen, gab es viele Schwierigkeiten. Es ging dabei in erster Linie nicht darum, dass das vermeintlich &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/visual-basic-net-lernen/">Visual Basic .NET lernen</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<style>
 .become-a-student-btn {
     color: #fff;
     background-color: #ff8c00;
     padding: 10px 30px;
     border: solid #e9c46a 1px;
     box-shadow: rgba(0, 0, 0, 0.15) 0px 2px 8px;
     border-radius: 5px;
     transition : 1000ms;
     transform: translateY(0);
     display: flex;
     flex-direction: row;
     align-items: center;
     cursor: pointer;
}
 .become-a-student-btn:hover{
     transition : 1000ms;
     padding: 10px 50px;
     transform : translateY(-0px);
     background-color: #fff;
     color: #22cc00;
     border: solid 1px #25bf07;
}

@media only screen and (max-width: 480px) {
  .become-a-student-btn {
        padding: 25px 30px;
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: center;
  }
}
</style>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2026/03/Visual-Basic-.NET-lernen.png"><img fetchpriority="high" decoding="async" width="1536" height="1024" src="https://robbelroot.de/wp-content/uploads/2026/03/Visual-Basic-.NET-lernen.png" alt="Visual Basic .NET lernen — Einstieg mit Unterricht und Mentoring" class="wp-image-19619"/></a><figcaption class="wp-element-caption">Visual Basic .NET lernen — Einstieg mit Unterricht und Mentoring</figcaption></figure>






<p>Visual Basic NET lernen möchte wirklich gelernt sein. Ich erinnere mich zu gut an meine Anfänge um das Jahr 2007 herum. Obwohl ich vermutlich wie jeder Anfänger alles Mögliche versucht habe in mein Gehirn aufzusaugen, gab es viele Schwierigkeiten. Es ging dabei in erster Linie nicht darum, dass das vermeintlich Gelernte nicht funktioniert hat! Es war viel mehr ein Problem, an benötigte Informationen zu kommen, bzw. das wonach ich suchte überhaupt betiteln zu können!</p>



<p>Klicke auf den nachstehenden Button, um Dich direkt als Schüler zu registrieren (keine Angst, erstmal nur eine unverbindliche Anfrage). Besuche alternativ meine <strong><a href="https://robbelroot.de/visual-basic-net-unterricht/" target="_blank" rel="noreferrer noopener">Unterseite zum VB.NET Unterricht</a></strong> und erfahre mehr durch z. B. meine dafür vorbereitete <strong><a href="https://robbelroot.de/vbnet-unterricht-praesi/" target="_blank" rel="noreferrer noopener">Präsentation</a></strong>.</p>



<div class="mb-2" style="display:flex;justify-content:center;">
  <a class="become-a-student-btn" href="https://robbelroot.de/programmieren-lernen" target="_blank" rel="noopener"><span style="font-size:30px;">&#x1f393;</span> Jetzt Schüler werden</a>
</div>



<h2 class="wp-block-heading">Läuft doch, aber!?</h2>



<p>Eine extrem wichtige Rolle – wie ich mit den Jahren an Erfahrung festgestellt habe – spielt gerade die Qualität des Codes. Und was das WIRKLICH heißt, lernt man erst mit Praxiserfahrung, Code-Reviews und bei der Arbeit in Teams. Zu meinen Anfängen bin ich mit völlig anderen Augen an die Sache gegangen. In erster Linie war es mir wichtig, dass der Code, den ich schreibe, überhaupt funktioniert. Ich wollte, dass das Programm genau das von mir Erdachte macht.</p>



<h2 class="wp-block-heading">Warum Visual Basic NET lernen?</h2>



<p>&#8222;Warum sollte ich Visual Basic NET lernen?&#8220;. Diese Frage habe ich mir am Anfang natürlich auch gestellt, da kann ich Dich beruhigen! Ebenso habe ich mich gefragt: &#8222;Warum sollte ich gerade VB.NET lernen?&#8220;. VB.NET, wie es abgekürzt heißt, ist für mich eine der absolut tollsten Sprachen für Einsteiger. Allerdings soll das auf keinen Fall heißen, dass diese Programmiersprache nur für Einsteiger ist. Es ist damit möglich, alles vom Hobby- bis zum Enterprise-Niveau zu entwickeln.</p>



<h3 class="wp-block-heading">Einfacher Einstieg möglich</h3>



<p>VB.NET glänzt vor allem bei Anfängern durch die Orientierung an der englischen Sprache. Das macht es den Menschen wesentlich einfacher, Welche noch nicht so in der Programmier-Materie drin stecken. Diese Menschen finden sich daher oft vergleichsweise einfacher in die Thematik ein.</p>



<h3 class="wp-block-heading">Für Quereinsteiger geeignet</h3>



<p>Visual Basic NET ist wie oben bereits erwähnt im Vergleich zu anderen Programmiersprachen einfach(er) zu erlernen. Daher ist die Sprache auch meiner Meinung nach eher als andere Programmiersprachen für Quereinsteiger geeignet. Einen gewissen Hang zu Sprachen im Allgemeinen ist von Vorteil. Ebenso vorteilhaft ist natürlich eine gewisse Basis an Englisch-Kenntnissen. Besonders wenn es um Dokumentationen geht, bleibt einem häufig nur die englische Sprache.</p>



<h3 class="wp-block-heading">Der Beginn der Programmierer-Karriere</h3>



<p>Als ich meine persönlichen ersten Gehversuche in der Softwareentwicklung gemacht habe, war ich mit &#8222;Unmanaged C&#8220;-Code maßlos überfordert. Ich sah die ganzen kryptischen Symbole und hatte einige Zeit versucht, mich dort zurechtzufinden, jedoch erfolglos. Sicherlich hatte ich einige kleine Programme mit entsprechender Zeit zum Laufen bekommen, aber es war insgesamt nicht zufriedenstellend. Zuletzt habe ich mich dann vor lauter Frust dazu entschieden, &#8222;Unmanaged C&#8220; vorerst ruhen zu lassen.</p>



<p>Mein Interesse an der Programmierung an sich, wurde dadurch keinesfalls vermindert, im Gegenteil. Kurze Zeit darauf bin ich dann durch Zufall auf Visual Basic NET gestolpert. Ich kann aus eigener Erfahrung sagen, dass mir VB.NET damals den Einstieg in die Welt der Programmierung ermöglicht hat. Schnell konnte ich mich in die englische Syntax einfinden und schnellstens eigene kleine Programme erstellen.</p>



<h2 class="wp-block-heading">Die Hindernisse im Lernprozess</h2>



<p>Auf dem Weg des Programmierers gibt es viele Hindernisse. Einige davon lassen sich sicherlich vermeiden. Hingegen lassen sich andere Hindernisse eventuell nur optimieren. Leider gibt es aber auch viele Hindernisse, Denen man sich zwangsweise stellen muss.</p>



<h3 class="wp-block-heading">Wonach suche ich eigentlich?</h3>



<p>Eins der Hauptprobleme als Programmier-Anfänger ist, dass man die zu suchenden Sachen häufig nicht betiteln kann. Woher soll man wissen, dass man nach einem &#8222;vertikalen Label&#8220; suchen soll, wenn man nicht weiß, dass es Label heißt? Wie soll man erkennen, dass man nach &#8222;Handler dynamisch hinzufügen&#8220; suchen müsste, wenn man nicht weiß, was ein &#8222;Handler&#8220; ist? Man kann natürlich die für sich bekannten, oder logisch erscheinenden Wörter verwenden, aber ob das dann zum Ziel führt – häufig ja, jedoch ebenso häufig, nein.</p>



<h3 class="wp-block-heading">Schreib, schreib, schreib den Code!</h3>



<p>Auch wenn es nervig klingt und oft auch sehr viel Arbeit ist: Tippe alles mindestens – und ich meine wirklich mindestens – dreimal ab! So groß die Verführung – auch von meinen Code-Snippets oder Projekten – ist, tippe selbst! Nur so prägt sich das Genannte ein und Du hast nachher den Kopf frei für &#8222;wie setze ich die Logik um&#8220; und nicht &#8222;Ach verdammt, wie ging nochmal eine Verzweigung?&#8220;. Du kannst noch so oft kopieren und zum Laufen bringen. Du kannst noch so oft vor dem Bildschirm sitzen und Dir nickend &#8222;hab ich verstanden&#8220; denken. Sobald Du dann – häufig – selbst davor sitzt und es &#8222;los geht&#8217;s&#8220; heißt, wirst Du Dich &#8222;wie ging das nochmal?&#8220; fragen. Daher: Schreib, schreib, schreib den Code!</p>



<h3 class="wp-block-heading">Foren und deren Mitglieder</h3>



<p>Bevor ich genauer auf das Thema Foren eingehe, möchte ich hier erwähnen, dass nicht immer alle Fragestellungen – sagen wir mal – &#8222;optimal&#8220; formuliert sind. Daher sind &#8222;schlechte&#8220; Antworten teilweise nicht so verwunderlich. Allerdings setze ich im Folgenden voraus, dass eine angemessene Frage gestellt wurde.</p>



<p>Das setzt neben der korrekten Fragestellung auch eine entsprechende Vorrecherche und eventuell vorhandenes Test-Material voraus. Man möchte seinen potenziellen Helfern natürlich alle möglichen Informationen zur Verfügung stellen.</p>



<p>Auch wenn ich jetzt vermutlich ein Feuerwerk entzünde, ist das eben so&#8230; Dennoch warne ich vor! Foren mögen freilich auch ihr Gutes haben, allerdings wundere Dich nicht, wenn Du dort:</p>



<ul class="wp-block-list">
<li><strong>unfreundlich </strong>behandelt wirst</li>



<li>motiviert wirst, Dein Projekt <strong>aufzugeben</strong></li>



<li><strong>Diskussionen </strong>führen musst, wo Du Dich fragst: &#8222;Muss das sein?&#8220;</li>



<li>bombardiert wirst, was Du denn alles <strong>falsch machst</strong></li>



<li>ewig auf Antworten <strong>wartest</strong></li>
</ul>



<h4 class="wp-block-heading">Varions Video zum Thema</h4>



<p>Hier findest Du eine lustige Parodie von dem YouTube-Kanal &#8222;Varion&#8220;. So lustig diese Parodie auch sein mag, es entsprecht schon ziemlich der Realität.</p>


<div class="async-youtube" data-embed="vcb08_S8KRY" data-alt="Varions YouTube Video zum Thema unfreundliche Forenmitglieder">
    <div class="play-button"></div>      
  </div>



<h4 class="wp-block-heading">Mein klares Wort dazu</h4>



<p>Mein klares Wort dazu ist Folgendes: Machen! Du wächst nicht über Dich hinaus, wenn Du Dich nicht selbst forderst. Das soll natürlich nicht bedeuten, dass Du Dich ständig in komplexe Sachen verirrst, für die Du keinerlei Basis hast, aber es soll auch nicht heißen, dass Du deine Ziele und Wünsche dafür fallen lässt. Wenn Du vorhast Projekt X zu realisieren, dann tu es! Lerne dafür, was notwendig ist und wenn Du es Dir 17 Mal anschaust. Dafür kannst Du durch die &#8222;heutigen&#8220; Möglichkeiten: Videos pausieren, Arbeitsblätter beschreiben, Apps verwenden und und und! Lass Dir nicht von den Leuten aus Foren etc. – oder von irgendwem anders – sagen, was Du zu tun und zu lassen hast, bzw. ob es &#8222;Sinn&#8220; ergibt, was Du da tust. Wer etwas mit Willen / Disziplin und eigener Motivation macht, lernt besser.</p>



<h3 class="wp-block-heading">Schlechte Code-Qualität</h3>



<p>Ein weiteres Problem im Lernprozess von Visual Basic – und vielen anderen Dingen – ist die Qualität der Quelle und des Codes selbst. Was hab ich mich am Anfang toll gefühlt, als ich meine erste Datenbankanwendung zum Laufen bekommen habe – ach ja.. Um so genervter war ich dann, als ich gemerkt habe, dass das Ganze zwar funktioniert hatte, aber völlig unsicherer Quatsch war. Verbindungen, die nicht sauber geschlossen werden, Verbindungsdetails, die jeder aus der ausführbaren Anwendung auslesen konnte und und und – ein Graus!</p>



<h3 class="wp-block-heading">Falscher Geiz</h3>



<p>Oh man, bei diesem Thema muss ich mir selbst ordentlich an den Kopf fassen. Die menschliche Psyche ist ein wenig komisch. Ich selbst kenne diese Vorgehensweise – vor allem aus meinen Anfangszeiten – sehr gut. Lieber geht man hin, investiert 2 Tage, 3 Tage und sucht wie ein Irrer und verballert so seine wichtige Zeit, anstatt einfach die paar Euronen in die Hand zu nehmen und zu investieren. Es gibt so viele Zweigstellen – nicht nur in der Programmierung – an denen man sich in die falsche Richtung verirren kann, bzw. wo man gar nicht erst zum Ergebnis kommt. Man sollte man sich überlegen, von welcher Ressource man mehr hat: Zeit, oder Geld? Wenn ich ganz klar sagen kann: &#8222;Hey, mich stört es nicht, wenn ich jetzt 3 Tage zeitlich investiere.&#8220;, dann ist alles okay. Aber auch ich habe als Mensch mit starkem finanziellen Bewusstsein verstanden, dass die Investition manchmal der klügere &#8222;Trade&#8220; ist.</p>



<h2 class="wp-block-heading">Der Einarmige Bandit &#8211; Ein beliebtes Projekt</h2>



<p><strong>Nachfolgend </strong>möchte ich Dir ein <strong>sehr beliebtes Projekt</strong> zeigen, wofür ich <strong>immer mal wieder angeschrieben</strong> werde. Das Projekt &#8222;<strong>Der einarmige Bandit</strong>&#8220; war vor längerer Zeit <strong>aus Spaß entstanden</strong>. Es war <strong>eins dieser Projekte</strong>, wo ich mir dachte: „<strong>Ich möchte</strong> jetzt mal xy <strong>machen</strong>!”. Genau da kommen wir auf <strong>DEN Punkt</strong> zu sprechen: <strong>Programmieren zu können</strong> bzw. <strong>zu lernen</strong>, bedeutet sich <strong>Freiheit zu schaffen</strong>. Ich kann als Entwickler <strong>frei entscheiden</strong>, was ich programmieren möchte!</p>


<div class="async-youtube" data-embed="hh6zbjNx37g" data-alt="Einarmiger Bandit / Slot Machine in VB.NET umgesetzt">
    <div class="play-button"></div>      
  </div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Zeitmangel</h3>



<p>Die Zeit jeder Person ist heutzutage rar. Die Menschen leben im permanenten Stress und versuchen Zeit und Kosten zu sparen, wo es geht. Sobald man mit der Programmierung beginnt, wird man relativ schnell verstehen, dass es häufig um Geschwindigkeit und diesbezüglicher Entscheidungen geht. Nehme ich mir die Zeit heraus, jetzt nochmal alles durchzuplanen und eventuell zu verbessern? Oder lasse ich es so wie es ist – es funktioniert ja schließlich! Auch wenn es als Anfänger gängig ist, laufende Sachen auch laufen zu lassen. Früher oder später werden die Anforderungen mehr. Visual Basic NET zu lernen heißt im ersten Schritt nur, die besonderen Eigenschaften dieser objektorientierten Sprache und deren Syntax zu verstehen. Zu einem Programmierer-Dasein gehört aber noch viel mehr dazu!</p>



<h4 class="wp-block-heading">Womit ich euch zu unterstützen versuche</h4>



<p>Für den optimalen Lernweg habe ich hier drunter die wichtigsten Hilfe-Themen aufgelistet. Ich möchte euch damit die Möglichkeit bieten, die Such-Orgien zu vermeiden, die ich durchlebt habe. Obwohl es viele Foren, Bücher und natürlich auch Videos gibt, haben wir alle auch viele negative Erfahrungen gemacht.</p>



<h4 class="wp-block-heading">Meine angebotene Hilfe</h4>



<ul class="wp-block-list">
<li><a href="https://youtube.com/robbelroot" target="_blank" rel="noreferrer noopener"><strong>YouTube Kanal</strong></a> – <strong>Problemlösungen</strong>, <strong>Beispiele </strong>und viel <strong>mehr</strong></li>



<li><strong><a href="https://robbelroot.de/visual-basic-net-unterricht/" target="_blank" rel="noreferrer noopener">Unterricht</a></strong> über <strong>Skype </strong>und <strong>Fernwartung</strong></li>



<li><strong><a href="https://robbelroot.de/auftragsprogrammierung/" target="_blank" rel="noreferrer noopener">Auftragsprogrammierung</a></strong> als normale <strong>Dienstleistung</strong></li>



<li>mehr in Planung&#8230;</li>
</ul>



<p>Visual Basic NET lernen und anzuwenden zu können, bedeutet in erster Linie Freiheit. Ob man als Privatperson nun Hobby-Projekte umsetzen möchte, oder auch als (neben-) beruflichen Zweig gewerbliche Applikationen erstellt. Beides ist mit Visual Basic NET möglich. Das zu erwerbende, oder bereits erworbene Wissen kann ein gutes Grundgerüst für eine Ausbildung, Festanstellung oder sogar die Selbstständigkeit sein.</p>



<p>Jederzeit wird es Dir möglich sein <a href="https://robbelroot.de/dateien-tutorials/"><strong>Beispielprojekte herunterzuladen</strong></a>, oder mir Fragen via <a href="https://robbelroot.de/kontakt/" target="_blank" rel="noopener noreferrer"><strong>Kontaktformular</strong></a> oder <a href="https://www.youtube.com/user/RobbelRoot/about" target="_blank" rel="noopener noreferrer"><strong>YouTube-Nachrichten</strong></a> zu stellen. Bitte schaue auch unter <a href="https://robbelroot.de/blog/category/empfehlungen/" target="_blank" rel="noopener noreferrer"><strong>Empfehlungen</strong></a> vorbei, dort findest Du Materialien in verschiedenster Form, die mir persönlich sehr geholfen haben. Um meine freiwilligen Leistungen ein wenig zu honorieren, habe ich hier und da Anzeigen von Google platziert. Nach kurzer Zeit wirst Du merken, dass die Welt der Programmierung einige wundervolle Türen in jeder Hinsicht öffnen kann.</p>



<h2 class="wp-block-heading">Der ideale Einstieg</h2>



<p>Den idealen Einstieg in die Welt der Programmierung erhältst Du durch meine persönlich angebotenen <a href="https://robbelroot.de/visual-basic-net-unterricht/" target="_blank" rel="noopener noreferrer"><strong>Unterrichtsstunden</strong></a> via Fernwartung. Den Unterricht vor Ort habe ich mittlerweile so gut wie abgeschafft, da meine Schüler zumeist aus weiterer Entfernung kommen. Falls Du doch in greifbarer Nähe wohnen solltest und Interesse hast, schreibe mich gerne an, dann können wir schauen, was sich machen lässt.</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/visual-basic-net-lernen/">Visual Basic .NET lernen</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/visual-basic-net-lernen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Fortschritt im Icon der Taskleiste mit C# oder VB.NET anzeigen</title>
		<link>https://robbelroot.de/blog/fortschritt-im-icon-der-taskleiste-mit-csharp-oder-vbnet-anzeigen/</link>
					<comments>https://robbelroot.de/blog/fortschritt-im-icon-der-taskleiste-mit-csharp-oder-vbnet-anzeigen/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Thu, 30 Mar 2023 03:14:46 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[WPF Problemlösungen]]></category>
		<category><![CDATA[anzeige]]></category>
		<category><![CDATA[bar]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[dotnet]]></category>
		<category><![CDATA[fortschritt]]></category>
		<category><![CDATA[icon]]></category>
		<category><![CDATA[indikator]]></category>
		<category><![CDATA[leiste]]></category>
		<category><![CDATA[progressbar]]></category>
		<category><![CDATA[task]]></category>
		<category><![CDATA[taskbar]]></category>
		<category><![CDATA[taskleiste]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[visual basic]]></category>
		<category><![CDATA[windows]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=15419</guid>

					<description><![CDATA[<p>Um Fortschritt in der Taskleiste, bzw. im Icon der Taskleiste anzuzeigen, kannst Du z. B. bei WPF-Anwendungen die &#8222;TaskbarItemInfo&#8220;-Klasse verwenden. Setze einen passenden Zustand in die &#8222;ProgressState&#8220;-Eigenschaft (z. B. &#8222;Normal&#8220;) und einen entsprechenden Fortschritt via &#8222;ProgressValue&#8220;-Eigenschaft. Eine ProgressBar in der Taskleiste? Nice! Yes! Gerade eben bin ich mit den einzelnen &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/fortschritt-im-icon-der-taskleiste-mit-csharp-oder-vbnet-anzeigen/">Fortschritt im Icon der Taskleiste mit C# oder VB.NET anzeigen</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-in-Taskleistex640.png"><img decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-in-Taskleistex640.png" alt="Fortschritt im Icon der Taskleiste mit C# und VB.NET anzeigen" class="wp-image-15427" title="Fortschritt im Icon der Taskleiste mit C# und VB.NET anzeigen"/></a><figcaption class="wp-element-caption">Fortschritt im Icon der Taskleiste mit C# und VB.NET anzeigen</figcaption></figure>






<p><strong>Um Fortschritt in der Taskleiste, bzw. im Icon der Taskleiste anzuzeigen, kannst Du z. B. bei WPF-Anwendungen die &#8222;TaskbarItemInfo&#8220;-Klasse verwenden. Setze einen passenden Zustand in die &#8222;ProgressState&#8220;-Eigenschaft (z. B. &#8222;Normal&#8220;) und einen entsprechenden Fortschritt via &#8222;ProgressValue&#8220;-Eigenschaft.</strong></p>



<h2 class="wp-block-heading">Eine ProgressBar in der Taskleiste? Nice!</h2>



<p>Yes! Gerade eben bin ich mit den einzelnen Teilen meines aktuellen YouTube-Videos fertig geworden. Und jetzt? Naja, jetzt muss ich das Ganze noch schneiden! Dazu verwende ich immer das bekannte Programm &#8222;DaVinci Resolve&#8220;. Alles zurechtgerückt und geschnitten, startete ich also im nächsten Schritt den Render-Prozess und da kam es mir in den Kopf!</p>



<p class="info-banner">Wenn Du es eilig hast, kannst Du natürlich die obigen Kapitel aus <strong><a href="#toc_container">dem Inhaltsverzeichnis</a></strong> nutzen. Springe zum Beispiel hier zum <strong><a href="#wpf-beispiel">WPF-</a></strong> und hier zum <strong><a href="#winforms-beispiel">Windows Forms Beispiel</a></strong>.</p>



<style>
.info-banner {
  background: #03a9f4;
  padding: 2em;
  border-radius: 0.5em;
  color: whitesmoke;
}
.info-banner a {
   color: #FED766;
}
</style>



<p>In der Taskleiste sah ich die Fortschrittsanzeige (ProgressBar), Welche unter dem Taskleisten-Icon angezeigt wird. An der Stelle habe ich mich dann gefragt: &#8222;Öhm, hast Du als YouTuber im Bereich .NET überhaupt schon ein Tutorial dazu gemacht?&#8220;. Die Antwort war – nein! Also begann ich im nächsten Schritt direkt mit diesem Beitrag hier, wozu auch bald darauf das Video erscheinen wird.</p>



<h2 class="wp-block-heading">Im Videoformat</h2>



<p>Zu viel Text? Keine Sorge, schaue Dir einfach das passende YouTube-Video auf meinem Kanal an. So kannst Du Dir alles in Ruhe anschauen und Dich berieseln lassen. Ich rate Dir dennoch &#8211; zumindest ergänzend zum Beitrag – da ich hier nochmal alles im Detail erkläre.</p>


<div class="async-youtube" data-embed="nD4SF3duVWQ" data-alt="">
    <div class="play-button"></div>      
  </div>



<h3 class="wp-block-heading">Ein Beispiel</h3>



<p>Wie gleich folgend könnte es z. B. in Deinem Programm (bei mir unter Windows 11) aussehen. Ich habe (wie Du unten noch sehen wirst) den Fortschritt jede Sekunde erhöht. Das hat natürlich zur Folge, dass diese kleine Fortschrittsbalken unter dem Icon wächst.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-in-der-Taskleiste-unter-.NET_.gif"><img decoding="async" width="164" height="64" src="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-in-der-Taskleiste-unter-.NET_.gif" alt="Fortschrittsanzeige in der Taskleiste unter .NET" class="wp-image-15497" title="Fortschrittsanzeige in der Taskleiste unter .NET"/></a><figcaption class="wp-element-caption">Fortschrittsanzeige in der Taskleiste unter .NET</figcaption></figure>



<p>Besonders für Rendering-Softwares oder Programmen mit ähnlich langen Prozessen meines Erachtens nach unabdingbar! Alternativ gibt es auch noch eine Art &#8222;ich bin einfach busy&#8220;-Status, also wenn man den genauen Fortschritt vielleicht nicht darstellen kann.</p>



<h2 class="wp-block-heading" id="wpf-beispiel">Die TaskBarInfo-Klasse in WPF</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-ProgressBar-mit-Csharp-oder-VB.NET-in-der-Taskleiste-anzeigen-WPF.png"><img loading="lazy" decoding="async" width="1280" height="720" src="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-ProgressBar-mit-Csharp-oder-VB.NET-in-der-Taskleiste-anzeigen-WPF.png" alt="Fortschrittsanzeige ProgressBar mit Csharp oder VB.NET in der Taskleiste anzeigen - WPF" class="wp-image-15486" title="Fortschrittsanzeige ProgressBar mit Csharp oder VB.NET in der Taskleiste anzeigen - WPF"/></a><figcaption class="wp-element-caption">Fortschrittsanzeige ProgressBar mit Csharp oder VB.NET in der Taskleiste anzeigen &#8211; WPF</figcaption></figure>



<p>Um unser Vorhaben, also eine Fortschrittsanzeige unter dem Programm-Icon in der Taskleiste umzusetzen, gibt es von WPF einfache Bordmittel. Hierbei handelt es sich um die &#8222;TaskbarItemInfo&#8220;-Klasse, Welche sich im &#8222;System.Windows.Shell&#8220;-Namespace versteckt. Diese, bzw. Instanzen davon bieten verschiedene Eigenschaften an, die wir mit passenden Werten versehen können.</p>



<h3 class="wp-block-heading">Eine Fortschrittsanzeige zum Fenster hinzufügen</h3>



<p>Um überhaupt eine Art Fortschritt anzeigen zu können, müssen wir erstmal eine passende Instanz der jeweiligen Klasse erstellen. Danach muss diese Instanz der passenden Window-Eigenschaft zugewiesen werden. Dadurch weiß das WPF-Fenster: &#8222;Aha, meine Info finde ich also da..&#8220;. Klingt kompliziert, ist aber eigentlich ganz einfach.</p>



<p>In typischer WPF-Manier, sprich im XAML-Code, sieht das dann wie gleich folgend aus. Problem ist nur, dass Du aktuell praktisch immer &#8222;30%&#8220; als Fortschritt sehen würdest. Das liegt natürlich daran, dass wir den Wert für die &#8222;ProgressValue&#8220;-Eigenschaft hard gecoded haben.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="XAML" data-enlighter-group="">&lt;Window x:Class="blabla.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
    
    &lt;Window.TaskbarItemInfo>
        &lt;TaskbarItemInfo ProgressState="Normal" ProgressValue="0.3" />
    &lt;/Window.TaskbarItemInfo>
   
    &lt;Grid>

    &lt;/Grid>
&lt;/Window>
</pre>



<h3 class="wp-block-heading" id="moegliche-wpf-zustaende">Mögliche Zustände</h3>



<p>Im nächsten Schritt können wir z. B. festlegen, was wir eigentlich darstellen möchten, z. B. eine Art Fehler im Vorgang. Oder wollen wir einfach nur den &#8222;normalen&#8220; Fortschritt darstellen? Das machen wir mit der &#8222;ProgressState&#8220;-Eigenschaft, der &#8222;TaskbarItemInfo&#8220;-Klasse. Die gängigen Werte kannst Du hier nachlesen:</p>



<ul class="wp-block-list">
<li>None</li>



<li>Normal</li>



<li>Indeterminate</li>



<li>Paused</li>



<li>Error</li>
</ul>



<p>Je nach Einstellung färbt sich die Fortschrittsanzeige in der Taskleiste dementsprechend. Ebenso haben wir dadurch die Möglichkeit, die ProgressBar zu deaktivieren, sowie auf eine Art &#8222;auf unbestimmte Dauer beschäftigt&#8220; zu setzen. Für Letzteres ist z. B. der Zustand &#8222;Indeterminate&#8220; gedacht.</p>



<h3 class="wp-block-heading">Fortschritt verändern</h3>



<p>Das obige Beispiel dürfte wie schon erwähnt relativ langweilig sein, da unser Fortschritt nur auf einer Stelle stehenbleibt. Gehen wir also nun die Fortschritts-Veränderung an, dazu brauchen wir nur die passende Eigenschaft im Code verändern. Beachte, dass &#8222;20%&#8220; z. B. &#8222;0,2&#8220; wären, nicht &#8222;20&#8220;!</p>



<p>Beachte ebenso, dass wir Zustandsveränderungen nur auf dem Thread der grafischen Oberfläche durchführen sollten. Ansonsten wirst Du vermutlich eine Cross-Thread Fehlermeldung bekommen! Schaue Dir zu diesem Thema gerne auch meinen &#8222;<strong><a href="https://robbelroot.de/blog/was-ist-configureawait-false-und-warum-sollte-es-der-standard-sein/" target="_blank" rel="noreferrer noopener">Beitrag zu ConfigureAwait</a></strong>&#8220; und den Beitrag zum Thema &#8222;<strong><a href="https://robbelroot.de/blog/ungueltiger-threaduebergreifender-vorgang-zugriff-auf-steuerelement-dotnet/" target="_blank" rel="noreferrer noopener">Ungültiger threadübergreifender Vorgang</a></strong>&#8220; an.</p>



<p>Mit der &#8222;Thread-Kenntnis&#8220; starten wir nun eine einfach &#8222;Fire and Forget&#8220;-Task, Welche den Fortschritt immer wieder nach kurzer Verzögerung anpasst. Dadurch, dass &#8222;ConfigureAwait&#8220; standardmäßig auf &#8222;true&#8220; steht, brauchen wir uns hier keine Gedanken über den Thread der Oberfläche machen. Denn so hüpfen wir immer wieder in den Ursprungs-Kontext = die grafische Oberfläche (um&#8217;s einfach zu halten..).</p>



<p>Generiere im nächsten Schritt einfach einen &#8222;Loaded&#8220;-Ereignishandler, indem Du innerhalb des Window-Tags &#8222;Loaded&#8220; schreibst und dann Tab drückst. In den Bereich des Loaded-Handlers kannst Du dann folgenden Code unterbringen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="fortschritt-veraendern">// sonstiger Fenster-Code

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        _ = SimulateProgress();
    }

    private async Task SimulateProgress()
    {
        while (TaskbarItemInfo.ProgressValue &lt; 1)
        {
            await Task.Delay(1000);
            TaskbarItemInfo.ProgressValue += 0.1;
        }
        Debug.WriteLine("completed!");
        TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
    }

// sonstiger Fenster-Code</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="fortschritt-veraendern">' sonstiger Fenster-Code

Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
    Dim fireAndForgetTask = SimulateProgress()
End Sub

Private Async Function SimulateProgress() As Task
    While TaskbarItemInfo.ProgressValue &lt; 1
        Await Task.Delay(1000)
        TaskbarItemInfo.ProgressValue += 0.1
    End While

    Debug.WriteLine("completed!")
    TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None
End Function

' sonstiger Fenster-Code</pre>



<p>Sobald das Fenster geladen ist, zeigen wir die ProgressBar an, denn wir haben Sie im MainWindow XAML-Code sichtbar geschaltet! Danach beginnt der Code von &#8222;SimulateProgress&#8220; zu wirken. Wir warten letztendlich immer 1 Sekunde (solange wir nicht bei 100% sind). Und erhöhen dann die Fortschrittsanzeige um 10%. Wenn wir fertig sind, geben wir dies in einer kleinen Debug-Zeile aus und schalten die Fortschrittsanzeige aus.</p>



<p>Im Gegensatz zu vielen anderen Online-Codes, verzichte ich hier auf einen BackgroundWorker, dessen Benutzung Du gerne in meinem <strong><a href="https://robbelroot.de/blog/backgroundworker-beispiel-in-vb-net/" target="_blank" rel="noreferrer noopener">Background Worker Guide</a></strong> erfahren kannst. Ich bevorzuge die gezeigte Variante, wo wir eine einfache Task verwenden :).</p>



<h2 class="wp-block-heading" id="winforms-beispiel">Für Windows Forms</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-ProgressBar-mit-Csharp-oder-VB.NET-in-der-Taskleiste-anzeigen-Windows-Forms.png"><img loading="lazy" decoding="async" width="1280" height="720" src="https://robbelroot.de/wp-content/uploads/2023/03/Fortschrittsanzeige-ProgressBar-mit-Csharp-oder-VB.NET-in-der-Taskleiste-anzeigen-Windows-Forms.png" alt="Fortschrittsanzeige ProgressBar mit Csharp oder VB.NET in der Taskleiste anzeigen - Windows Forms" class="wp-image-15484" title="Fortschrittsanzeige ProgressBar mit Csharp oder VB.NET in der Taskleiste anzeigen - Windows Forms"/></a><figcaption class="wp-element-caption">Fortschrittsanzeige ProgressBar mit Csharp oder VB.NET in der Taskleiste anzeigen &#8211; Windows Forms</figcaption></figure>



<p>Wenn Du im Gegensatz zum obigen Beispiel die guten alten Windows Forms verwendest, bist Du hier natürlich ebenfalls genau richtig! Im Endeffekt ist die Verwendung der TaskBar und die darin befindlichen in Winforms nicht sooo viel schwerer. Das einzige Problem ist zunächst, dass Du auf &#8222;die TaskBar&#8220; keinen so einfachen Zugriff hast.</p>



<h3 class="wp-block-heading">Paket installieren</h3>



<p>Um einen einfachen Zugriff auf die TaskBar zu erhalten, installierst Du einfach erst einmal folgendes Paket. Öffne dazu (wie so häufig im .NET-Bereich) den NuGet Paket-Manager. Dies kannst Du z. B. durch einen Rechtsklick auf Dein Projekt und dem passenden Menüeintrag machen. Entweder führst Du dann den unteren Befehl in der NuGet Konsole aus, oder suchst via GUI manuell nach dem Paket &#8222;WindowsAPICodePack-Shell&#8220;.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Install-Package WindowsAPICodePack-Shell</pre>



<h3 class="wp-block-heading">Mögliche Zustände</h3>



<p>Nach der Installation des oben angegebenen Paketes, kannst Du <strong><a href="#moegliche-wpf-zustaende">analog zu den Zuständen oben</a></strong>, Ebendiese schalten. Diese befinden sich in einer Enumeration namens &#8222;TaskbarProgressBarState&#8220;.</p>



<p>Um zu einem jeweiligen Zustand zu schalten, verwendest Du nun die &#8222;SetProgressState&#8220;-Methode der &#8222;TaskbarManager&#8220;-Klasse, bzw. dessen &#8222;Instance&#8220;-Eigenschaft. Somit kannst Du nun zwischen den verschiedenen Modi wie &#8222;NoProgress&#8220;, &#8222;Indeterminate&#8220; und mehr, hin und her schalten. Normal heißt auch hier: &#8222;Zeige einen Fortschritt an&#8220;.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal, Handle)</pre>



<h3 class="wp-block-heading">Fortschritt verändern</h3>



<p>Um den Fortschritt der Taskbar-ProgressBar nun zu verändern, kannst Du die analoge &#8222;SetProgressValue&#8220;-Methode verwenden. Hier setze ich z. B. &#8222;20%&#8220; von &#8222;100%&#8220; als aktuellen Wert:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">TaskbarManager.Instance.SetProgressValue(20, 100, Handle)</pre>



<h3 class="wp-block-heading">Fortschritt ausblenden</h3>



<p>Möchtest Du den Fortschritt wieder ausblenden, verwendest Du einfach dies hier:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, Handle)</pre>



<h3 class="wp-block-heading">Achtung: Bekannte Stolpersteine</h3>



<p>Als ich das erste Mal mit der Thematik &#8222;ProgressBar in Taskleiste&#8220; in Verbindung kam, hatte ich zumindest bei Windows Forms Projekten einige Probleme. Zuerst einmal fehlte die jeweilig verwendete Klasse und dann blieb der Aufruf wirkungslos, seufz.. Z. B. Folgendes ging nicht:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal)</pre>



<h4 class="wp-block-heading">Das falsche Paket</h4>



<p>Mit ein wenig Gefummel, habe ich dann rausbekommen, dass ich das falsche Paket installiert hatte, bzw. nicht alles. Ich hatte das &#8222;<strong>WindowsAPICodePack-Core</strong>&#8220; installiert, allerdings hatte ich da keine Klasse namens &#8222;TaskbarManager&#8220;, auf die ich zugreifen, geschweige denn importieren konnte. Achte daher darauf, dass Du das korrekte Paket namens &#8222;<strong>WindowsAPICodePack-Shell</strong>&#8220; installierst! Dann kannst Du zumindest die genannte Klasse namens &#8222;TaskbarManager&#8220; verwenden.</p>



<h4 class="wp-block-heading">Fehlendes Handle</h4>



<p>Im nächsten Schritt stieß ich auf das Problem, dass die Aufrufe an sich durchgingen, aber irgendwie keine Wirkung hatten. Hier und da ein wenig rumprobiert, schon stieß ich auch auf die diesbezügliche Lösung: Man muss beim Aufruf ein Form-Handle angeben! Das Form-Handle der jeweiligen Form, für die ich eine ProgressBar in der Taskleiste anzeigen möchte. Gesagt, getan – aus Aufruf ohne Handle, wurde ein Aufruf mit Handle:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">TaskbarManager.Instance.SetProgressValue(&lt;currentValue>, &lt;maxValue>, Handle)</pre>



<p>Beachte hierbei, dass ich diesen Code aus der Form selbst aufrufe, nur deshalb habe ich hier den einfachen Zugriff auf dessen &#8222;Handle&#8220;-Eigenschaft!</p>



<h4 class="wp-block-heading">Fehlende PresentationFramework Assembly</h4>



<p>Die letzte Hürde ließ nicht lange auf sich warten, denn der geänderte Aufruf führte zu folgendem Fehler:</p>



<p></p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/04/Fehler-Es-ist-ein-Verweis-auf-die-Assembly-PresentationFramework-erforderlich.-Fuegen-Sie-dem-Projekt-einen-Verweis-hinzu..png"><img loading="lazy" decoding="async" width="689" height="158" src="https://robbelroot.de/wp-content/uploads/2023/04/Fehler-Es-ist-ein-Verweis-auf-die-Assembly-PresentationFramework-erforderlich.-Fuegen-Sie-dem-Projekt-einen-Verweis-hinzu..png" alt="&quot;Fehler: Es ist ein Verweis auf die Assembly PresentationFramework erforderlich. Fügen Sie dem Projekt einen Verweis hinzu.&quot;" class="wp-image-15521" title="&quot;Fehler: Es ist ein Verweis auf die Assembly PresentationFramework erforderlich. Fügen Sie dem Projekt einen Verweis hinzu.&quot;"/></a><figcaption class="wp-element-caption"><em>&#8222;Fehler: Es ist ein Verweis auf die Assembly PresentationFramework erforderlich. Fügen Sie dem Projekt einen Verweis hinzu.&#8220;</em></figcaption></figure>



<p>Auch dieser Fehler ist relativ einfach zu lösen! Mache einen Rechtsklick auf Dein Projekt, klicke auf Projektdatei bearbeiten, und füge die &#8222;UseWPF->True&#8220; Konfiguration im Bild hinzu:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/04/NET-Projektdatei-UseWpf-auf-true.png"><img loading="lazy" decoding="async" width="457" height="196" src="https://robbelroot.de/wp-content/uploads/2023/04/NET-Projektdatei-UseWpf-auf-true.png" alt="NET Projektdatei - UseWpf auf true" class="wp-image-15524" title="NET Projektdatei - UseWpf auf true"/></a><figcaption class="wp-element-caption">NET Projektdatei &#8211; UseWpf auf true</figcaption></figure>



<h2 class="wp-block-heading">Downloads</h2>



<p>Hast Du keine Lust alles händisch nachzubauen? Kein Problem, lade Dir doch einfach die gebrauchsfertigen Projekte herunter ;)!</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=a53abc65-ec9f-4ea0-b4ef-8ebe1431501f" target="_blank" rel="noreferrer noopener">TaskBarProgressVB.zip</a></div>
</div>
<p>Der Beitrag <a href="https://robbelroot.de/blog/fortschritt-im-icon-der-taskleiste-mit-csharp-oder-vbnet-anzeigen/">Fortschritt im Icon der Taskleiste mit C# oder VB.NET anzeigen</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/fortschritt-im-icon-der-taskleiste-mit-csharp-oder-vbnet-anzeigen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>.NET SerialPort-Kommunikation mit Arduino &#038; Co. – 2024 Guide</title>
		<link>https://robbelroot.de/blog/net-kommunikation-mit-arduino-und-co-ein-kompletter-guide/</link>
					<comments>https://robbelroot.de/blog/net-kommunikation-mit-arduino-und-co-ein-kompletter-guide/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 22 Mar 2023 15:48:24 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[arduino]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[com]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[datareceived]]></category>
		<category><![CDATA[ereignis]]></category>
		<category><![CDATA[kommunikation]]></category>
		<category><![CDATA[mikrocontroller]]></category>
		<category><![CDATA[port]]></category>
		<category><![CDATA[serialport]]></category>
		<category><![CDATA[usb]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vb.net]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[write]]></category>
		<category><![CDATA[writeline]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=15132</guid>

					<description><![CDATA[<p>USB-Kommunikation zwischen SerialPort und Mikrocontroller RFID-Chips für Zeiterfassungssysteme auslesen, mit LED&#8217;s spielen, oder anderer Interessanter Kram ist möglich – mit Mikrocontrollern. Der Bekannteste in der Mikrocontroller-Szene ist wohl der &#8222;Arduino&#8220;, Welcher mit seinen verschiedenen Ausfertigungen viele Bastler-Bedürfnisse abdeckt. Im heutigen Beitrag erfährst Du, wie Du gängige Mikrocontroller mit Hilfe der &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/net-kommunikation-mit-arduino-und-co-ein-kompletter-guide/">.NET SerialPort-Kommunikation mit Arduino &#038; Co. – 2024 Guide</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/NET-SerialPort-Kommunikation-mit-Arduino-und-Co-C-VB.NET_.png"><img loading="lazy" decoding="async" width="640" height="320" src="https://robbelroot.de/wp-content/uploads/2023/03/NET-SerialPort-Kommunikation-mit-Arduino-und-Co-C-VB.NET_.png" alt="NET SerialPort-Kommunikation mit Arduino und Co - C# VB.NET" class="wp-image-15140" title="NET SerialPort-Kommunikation mit Arduino und Co - C# VB.NET"/></a><figcaption class="wp-element-caption">NET SerialPort-Kommunikation mit Arduino und Co &#8211; C# VB.NET</figcaption></figure>






<h2 class="wp-block-heading">USB-Kommunikation zwischen SerialPort und Mikrocontroller</h2>



<p>RFID-Chips für Zeiterfassungssysteme auslesen, mit LED&#8217;s spielen, oder anderer Interessanter Kram ist möglich – mit Mikrocontrollern. Der Bekannteste in der Mikrocontroller-Szene ist wohl der &#8222;Arduino&#8220;, Welcher mit seinen verschiedenen Ausfertigungen viele Bastler-Bedürfnisse abdeckt. Im heutigen Beitrag erfährst Du, wie Du gängige Mikrocontroller mit Hilfe der .NET SerialPort-Klasse via C#, oder auch VB.NET ansteuern kannst.</p>



<p class="info-banner">Du hast es eilig? Nutze gerne <a href="#toc_container"><strong>das Inhaltsverzeichnis</strong></a>, um an eine passende Stelle zu hüpfen! Du hast Anregungen? Ich habe mich vertan? Schreib gerne ein sachliches Kommentar in <strong><a href="#reply-title">die Kommentar-Sektion</a></strong> weiter unten!</p>



<style>
.info-banner {
  background: #03a9f4;
  padding: 2em;
  border-radius: 0.5em;
  color: whitesmoke;
}
.info-banner a {
  color: #FED766;
}
</style>



<h2 class="wp-block-heading">Eine kleine Einführung in Mikrocontroller</h2>



<p>Im Endeffekt kann man Mikrocontroller als extremst kleine, erweiterbare Computer bezeichnen. Üblicherweise schließt man Diese mit daran verlöteten Dingen an z. B. PC&#8217;s an, oder lässt Sie im Solo-Betrieb laufen. So kann man einen Mikrocontroller beispielsweise anweisen, Temperatur-Daten zu sammeln und Diese via USB, oder sogar Wi-Fi zu übermitteln. Aufgrund ihrer Größe, sind Mikrocontroller jedoch häufig in Ihrer Leistung eingeschränkt. Heutzutage muss man glücklicherweise anerkennen, zu welcher Leistung Sie im Vergleich zu früheren Geräten fähig sind.</p>



<p>An dieser Stelle möchte ich jedoch nicht weiter, gar zu tief in die Materie abtauchen. Ich schätze, dass Dir bereits bewusst ist, was z. B. der Arduino, oder etwa ein Rasperry Pi ist. Vermutlich wirst Du gerade deswegen auf diesen Beitrag gestoßen sein: Du möchtest herausfinden, wie Du über .NET (egal ob nun Visual Basic .NET, oder C#) mit diesen Mini-PC&#8217;s kommunizieren kannst. Grundsätzlich geht das in .NET via der SerialPort-Klasse, Welche sich zu einem vorhandenen COM-Port verbinden kann.</p>



<p>Ab dann beginnt das Abenteuer aber erst, denn sich nur zu verbinden, reicht leider nicht aus!</p>



<h2 class="wp-block-heading">Ein &gt;wirklich&lt; einfaches Beispiel – reicht es?</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Simples-Beispiel-.NET-Mikrocontroller-SerialPort-Kommunikation.png"><img loading="lazy" decoding="async" width="640" height="320" src="https://robbelroot.de/wp-content/uploads/2023/03/Simples-Beispiel-.NET-Mikrocontroller-SerialPort-Kommunikation.png" alt="Simples Beispiel - .NET Mikrocontroller SerialPort Kommunikation" class="wp-image-15240" title="Simples Beispiel - .NET Mikrocontroller SerialPort Kommunikation"/></a><figcaption class="wp-element-caption">Simples Beispiel &#8211; .NET Mikrocontroller SerialPort Kommunikation</figcaption></figure>



<p>Da wir nun wissen, dass es die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport?view=dotnet-plat-ext-7.0" target="_blank" rel="noreferrer noopener">SerialPort-Klasse</a></strong> ist, Welche wir verwenden müssen, ist ja alles geklärt, oder!? Ich meine wir müssen doch dann eigentlich nur – eben typisch Objektorientierte Programmierung (OOP) – eine Instanz der jeweiligen Klasse erstellen und verwenden. Machen wir das doch einfach mal im ersten Schritt, wir erstellen eine Instanz der Klasse SerialPort.</p>



<h3 class="wp-block-heading">Die Installation des System.IO.Ports NuGet-Paket</h3>



<p>Bevor wir jedoch die SerialPort-Klasse verwenden können, müssen wir erst einmal das NuGet-Paket namens &#8222;System.IO.Ports&#8220; installieren. Begib Dich dazu einfach auf einem der üblichen Wege in den genannten Paket-Manager. Z. B. durch einen Rechtsklick auf Dein Projekt im Projektmappen-Explorer und einem anschließenden Linksklick auf &#8222;NuGet-Pakete verwalten..&#8220;.</p>



<p>Nun suche nach dem jeweiligen Paket und installiere es. Wähle es dazu aus, begib Dich ins rechte Fenster und hake Dein Projekt an &#8211; final kannst Du dann &#8222;installieren&#8220; drücken und die NuGet-Magie passiert.</p>



<h3 class="wp-block-heading">Eine simple SerialPort-Instanz</h3>



<p>Danach kannst Du endlich wie folgt eine einfache Instanz der SerialPort-Klasse erstellen. Hierbei geben wir in den meisten Fällen zwei wichtige Dinge an:</p>



<ul class="wp-block-list">
<li>Den zu nutzenden COM-Port</li>



<li>Die zur Kommunikation verwendete Baudrate</li>
</ul>



<p>In meinem Fall ist mein Arduino am &#8222;COM4&#8220; angeschlossen, daher übergebe ich dies als String. Ebenso befindet sich Code auf meinem Arduino, Welche eine Verbindung mit der Baudrate &#8222;19200&#8220; initialisiert. Damit keine komischen Zeichen bei uns ankommen, sollten wir auch seitens .NET die gleiche Baudrate verwenden.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="einfache-instanz">Dim port = New SerialPort("COM4", 19200)</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="einfache-instanz">var port = new SerialPort("COM4", 19200);</pre>



<h3 class="wp-block-heading">Weitere Konfiguration</h3>



<p>Neben den nun bekannten Einstellungen, gibt es noch Weitere, wie z. B. die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.parity" target="_blank" rel="noreferrer noopener">Parität</a></strong>, die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.databits?view=dotnet-plat-ext-7.0" target="_blank" rel="noreferrer noopener">DataBits</a></strong> und die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.stopbits?view=netframework-4.8" target="_blank" rel="noreferrer noopener">StopBits</a></strong>. Grundsätzlich reicht es meist aus, Diese Einstellungen auf ihren Standardwerten zu lassen. Dies passiert, wenn Du Sie wie im obigen Beispiel nicht angibst.</p>



<ul class="wp-block-list">
<li>Parität – Standardwert = Parity.None</li>



<li>DataBits – Standardwert = 8</li>



<li>StopBits – Standardwert = StopBits.One</li>
</ul>



<p>Restliche Informationen dazu müsstest Du zumeist Deiner Gegenstelle, also dem Gerät entnehmen, zu dem Du Dich verbindest!</p>



<h3 class="wp-block-heading">Und nü? Die Verbindung zum SerialPort öffnen!</h3>



<p>Nun haben wir ein konkretes Objekt eines Bauplans (Klasse) namens &#8222;SerialPort&#8220; erstellt, aber was nun? Nunja, bisher können wir nicht viel damit machen, denn die Verbindung ist noch nicht so wirklich hergestellt. Eine tatsächliche Verbindung zum Port öffnen wir einfach, indem wir die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.open?view=dotnet-plat-ext-7.0" target="_blank" rel="noreferrer noopener">Open-Methode</a></strong> verwenden. Sprich also einfach Deinen eben erstellten Port an und verwende Ebendiese Methode:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="verbindung-öffnen">Dim port = New SerialPort("COM4", 19200)
port.Open()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="verbindung-öffnen">var port = new SerialPort("COM4", 19200);
port.Open();</pre>



<h3 class="wp-block-heading">Daten senden</h3>



<p>Die schnellste und einfachste Möglichkeit Daten zu senden, ist die Vereinbarung eines Trennzeichens und das Abschicken, bzw. Auslesen von Daten, die dieses Trennzeichen beinhalten. Ein simples &#8222;Hin-und-her&#8220; kann man also so durchführen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="einfaches-senden-und-empfangen">Dim port = New SerialPort("COM4", 19200)
port.NewLine = vbLf
port.Open()
port.WriteLine("ID:?")

' blockiert solange BIS eine Antwort mit Daten UND dem vereinbarten Linefeed kommt!
Dim data = port.ReadLine()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="einfaches-senden-und-empfangen">var port = new SerialPort("COM4", 19200);
port.NewLine = "\n";
port.Open();
port.WriteLine("ID:?");

// blockiert solange BIS eine Antwort mit Daten UND dem vereinbarten Linefeed kommt!
var data = port.ReadLine();</pre>



<p>Die Betonung liegt hier allerdings auf &#8222;simpel&#8220;, daher lies ruhig weiter und schaue Dir zum Beispiel besonders die Kapitel über Trennzeichen und Co. an.</p>



<h2 class="wp-block-heading">Vorhandene COM-Ports auslesen / anzeigen</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/NET-COM-Ports-auflisten-und-anzeigen.png"><img loading="lazy" decoding="async" width="640" height="320" src="https://robbelroot.de/wp-content/uploads/2023/03/NET-COM-Ports-auflisten-und-anzeigen.png" alt=".NET COM Ports auflisten und anzeigen" class="wp-image-15244" title=".NET COM Ports auflisten und anzeigen"/></a><figcaption class="wp-element-caption">.NET COM Ports auflisten und anzeigen</figcaption></figure>



<p>Im obigen Beispiel haben wir bisher nur einen fixen Port verwendet. Das ist nicht nur relativ unflexibel per se, sondern kann sich auch sogar je nach verwendetem Anschluss – trotz gleichem Gerät – am PC ändern. Schöner wäre es hingegen, wenn wir auflisten könnten, welche COM-Ports tatsächlich zur Verfügung stehen. Die gute Nachricht ist: Das ist ziemlich easy – wenn auch nicht immer ausreichend!</p>



<h3 class="wp-block-heading">Kurz und bündig</h3>



<p>Die einfachste Option, die vorhandenen COM-Ports auszulesen ist, die statische (in VB.NET &#8222;Shared&#8220;) SerialPort-Funktion namens &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.getportnames?view=dotnet-plat-ext-8.0" target="_blank" rel="noreferrer noopener">GetPortNames</a></strong>&#8220; zu verwenden. Anzeigen könnte man diese Ports dann natürlich in jedem beliebigen Listen-Steuerelement, wie z. B. der ComboBox. Hier haben wir natürlich auch direkt die Möglichkeit, einen Port für unser Programm zu wählen.</p>



<h3 class="wp-block-heading">Tipp: Direkt als bindungsfähige Liste</h3>



<p>Ich empfehle hier übrigens die direkte &#8222;Umwandlung&#8220; des zurückkommenden String-Arrays in eine <a href="https://learn.microsoft.com/de-de/dotnet/api/system.componentmodel.bindinglist-1?view=net-7.0" target="_blank" rel="noreferrer noopener"><strong>BindingList</strong></a>. Wenn Du WPF verwendest, könntest Du hier eine passende Alternative, wie z. B. die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.collections.objectmodel.observablecollection-1?view=net-7.0" target="_blank" rel="noreferrer noopener">ObservableCollection </a></strong>verwenden. Das bietet den Vorteil, später auf Veränderungen zu reagieren. Wir können dann z. B. bei Trennung einer physischen Verbindung den jeweiligen COM-Port entfernen, oder einen Port beim Herstellen einer USB-Verbindung der Auflistung hinzufügen.</p>



<h3 class="wp-block-heading">Winforms-Beispiel</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="com-ports-auflisten-anzeigen">Public Class Form1

  ' sonstiger Form-Code..
  
  Public ReadOnly Property Ports As BindingList(Of String)

  Sub New ()
    Dim availablePorts = SerialPort.GetPortNames()
    Ports = New BindingList(Of String)(availablePorts)
    AddHandler Me.Load, AddressOf Form1_Load
  End Sub

  Private Sub Form1_Load(sender As Object, e As EventArgs)
    ComboBox1.DataSource = Ports
  End Sub
 
  ' sonstiger Form-Code..

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="com-ports-auflisten-anzeigen">public class Form1 : Form
{

  // sonstiger Form-Code..

  public BindingList&lt;string> Ports { get; }

  public Form1()
  {
    var availablePorts = SerialPort.GetPortNames();
    Ports = new BindingList&lt;string>(availablePorts);
    Load += Form1_Load;
  }

  private void Form1_Load(object sender, EventArgs e)
  {
    ComboBox1.DataSource = Ports;
  }

  // sonstiger Form-Code..

}</pre>



<h2 class="wp-block-heading">Daten zum Mikrocontroller schicken</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Daten-via-SerialPort-an-Mikrocontroller-wie-Arduino-senden.png"><img loading="lazy" decoding="async" width="640" height="320" src="https://robbelroot.de/wp-content/uploads/2023/03/Daten-via-SerialPort-an-Mikrocontroller-wie-Arduino-senden.png" alt="Daten via SerialPort an Mikrocontroller wie Arduino senden" class="wp-image-15248" title="Daten via SerialPort an Mikrocontroller wie Arduino senden"/></a><figcaption class="wp-element-caption">Daten via SerialPort an Mikrocontroller wie Arduino senden</figcaption></figure>



<p>Nachdem wir nun eine Möglichkeit gesehen haben, eine Verbindung zum Mikrocontroller aufzubauen, können wir nun weiter gehen. Das Wichtigste wäre wohl nun, tatsächliche Daten zum z. B. Arduino zu senden. Durch die serielle Verbindung (Serial&#8230;Port) schicken wir Daten – naja, eben seriell, sprich hintereinander – in die offene Verbindung. Die Reihenfolge der gesendeten Daten spielt hierbei natürlich eine essenzielle Rolle, da zuerst Gesendetes auch dementsprechend zuerst am Gerät ankommt. Schaue Dir gerne weiter unten die <strong><a href="#write-methode">Write</a></strong>-, bzw. die <strong><a href="#writeline-methode">WriteLine-Methode</a></strong> an.</p>



<h3 class="wp-block-heading">Serielle Kommunikation &amp; Protokoll</h3>



<p>Dies sollte auch bei der Kommunikation im Allgemeinen beachtet werden, da hier eine Art parallele Kommunikation so natürlich relativ &#8222;unmöglich&#8220; ist. Letztendlich entscheidet dann der Konsument über die passende Verarbeitung. Ich kann ja z. B. 3 Dinge kurz nacheinander empfangen, jedoch das Dritte Ding zuerst verarbeiten. Hierbei spielt also eine Art Protokoll, sowie eine Art Warteschlange eine sehr große Rolle.</p>



<h3 class="wp-block-heading" id="write-methode">Die Write-Methode</h3>



<p>Verwende nun also den obigen Code, um eine Verbindung zum (erneut &#8211; z. B.) Arduino herzustellen und wähle dafür entweder die flexible &#8222;Auswahl-Methode&#8220; der ComboBox, oder gebe den Portnamen fix an. Daten kannst Du dann ganz einfach via <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.write?view=dotnet-plat-ext-7.0" target="_blank" rel="noreferrer noopener">&#8222;Write&#8220;-Methode</a></strong>, bzw. eine der für Dich passenden Überladungen übersenden. Der Einfachheit halber, werde ich hier simple Strings übermitteln – ich verzichte also für&#8217;s Erste auf ein vollends implementiertes Protokoll. Schaue Dir alternativ auch die <strong><a href="#writeline-methode">WriteLine-Methode</a></strong> an.</p>



<h3 class="wp-block-heading">Strings an Arduino &amp; Co. senden</h3>



<p>Senden wir nun also einmal beispielhafte Daten an unseren Arduino. Dazu habe ich in diesem Beispiel einen kleinen Befehl aus meinem eigenen kleinen Protokoll verwendet. &#8222;ID&#8220; spiegelt hier letztendlich eine Art Befehl und das &#8222;?&#8220; die dazugehörige Statusabfrage wieder. Übersetzt könnte man sagen: &#8222;Ich möchte gerne Deine ID abrufen&#8220;.</p>



<p>Hierbei sollte man darauf achten, dass die SerialPort-Klasse (laut Doku) das ASCII-Encoding standardmäßig verwendet.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="in-com-serialport-schreiben">Dim port = New SerialPort("COM4", 19200)
port.Open()
port.Write("ID:?")</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="in-com-serialport-schreiben">var port = new SerialPort("COM4", 19200);
port.Open();
port.Write("ID:?");</pre>



<h3 class="wp-block-heading">Bytes via Write-Methode senden</h3>



<p>Wenn wir im Gegensatz zu einem String rohe Bytes schicken wollen, können wir dies natürlich auch sehr einfach tun. Dazu nutzen wir einfach die andere Überladung der &#8222;Write&#8220;-Methode. Hierbei müssen wir die Bytes mit einem passenden Encoding dann selbst erfassen. Dafür verwende ich hier beispielsweise das UTF8-Encoding:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="in-com-serialport-schreiben-bytes">Dim port = New SerialPort("COM4", 19200)
port.Open()
Dim textToSend = "ID:?"
Dim data = System.Text.Encoding.UTF8.GetBytes(textToSend)
port.Write(data, 0, data.Length)</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="in-com-serialport-schreiben-bytes">var port = new SerialPort("COM4", 19200);
port.Open();
var textToSend = "ID:?";
var data = System.Text.Encoding.UTF8.GetBytes(textToSend);
port.Write(data, 0, data.Length);</pre>



<h3 class="wp-block-heading" id="writeline-methode">WriteLine-Methode – mit Nachrichten-Trenner</h3>



<p>&#8222;Das hat doch kein Ende..&#8220; – das könnte man sich mit Sicherheit in so diversen alltäglichen Situation denken. Allerdings meine ich hier eher gesagt unsere Kommunikation zum Mikrocontroller, wie z. B. den Arduino. Mit unserem bisherigen Code weiß unser Kommunikationspartner leider gar nicht, wann eine &#8222;Nachricht&#8220;, oder eine Übermittlung beendet ist. Dem gleichen Problem treten wir auch entgegen, wenn wir die Richtung Arduino-&gt;.NET Software betrachten.</p>



<p>Für gewöhnlich verwendet man hier eine Art &#8222;Message-Terminator&#8220;, also eine Art Trennzeichen, Welches signalisiert: &#8222;Hey, ab hier ist die Übertragung der letzten Nachricht abgeschlossen&#8220;. Theoretisch könnte man natürlich auch – analog z. B. zu HTTP &amp; Co. – eine Art Header senden. Hier muss man schlichtweg sein eigenes Protokoll wählen / designen, Welches für den jeweiligen Einsatz geeignet ist.</p>



<h3 class="wp-block-heading">NewLine-Eigenschaft mit Trennzeichen</h3>



<p>In unserem Beispiel können wir mit dem SerialPort vorab ein Trennzeichen über die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.newline?view=netframework-4.8">&#8222;NewLine&#8220;-Eigenschaft</a></strong> vereinbaren. Ich weiß, ein verwirrender Name.. Setze nun einmal beispielhaft das &#8222;LineFeed&#8220;-Zeichen als das Trennzeichen ein, dies geht wie gleich folgenden. Beachte, dass zukünftige &#8222;WriteLine&#8220;-Aufrufe nun automatisch das Trennzeichen angeheftet bekommen! ReadLine wird hingegen solang blockieren, bis es dieses Trennzeichen ankommend vom Gerät bei einem Lesevorgang erkennt!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="newline-trennzeichen">' irgendwo bei der Initialisierung..
_port = New SerialPort("COM4", 19200)
' hier setzen wir das Zeichen 10 -> Linefeed
' als Trennzeichen der Nachrichten ein!
_port.NewLine = vbLf</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="newline-trennzeichen">// irgendwo bei der Initialisierung..
_port = new SerialPort("COM4", 19200);
// hier setzen wir das Zeichen 10 -> Linefeed
// als Trennzeichen der Nachrichten ein!
_port.NewLine = "\n";</pre>



<h3 class="wp-block-heading">Nachwort zum Thema Protokoll</h3>



<p>In meinerseits geschriebenen und professionell eingesetzten Anwendungen, habe ich hier ein ganzes Protokoll in Kombination mit dynamischen Zusammensetzungen von Maschinen-Objekten gebaut. Dies würde hier allerdings mehr als den Rahmen sprengen. Eventuell werde ich dies in einem zukünftigen Beitrag einmal demonstrieren.</p>



<p>Meines Erachtens nach ist dies der einzig richtige Weg, da man auf die obige Art und Weise ggf. schnell auf gewisse Grenzen stößt. Wenn ich z. B. warten möchte bis gewisse Aufgaben (siehe Tasks, TaskCompletionSources, etc.) erledigt sind, habe ich aktuell keine Ahnung, Welches herausgesendete Kommando, zu welcher eingehenden Antwort gehört.</p>



<h2 class="wp-block-heading">Antwortdaten empfangen</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Daten-von-Mikrocontrollern-wie-Arduino-ueber-SerialPort-in-VB.NET-und-C-empfangen.png"><img loading="lazy" decoding="async" width="640" height="320" src="https://robbelroot.de/wp-content/uploads/2023/03/Daten-von-Mikrocontrollern-wie-Arduino-ueber-SerialPort-in-VB.NET-und-C-empfangen.png" alt="Daten von Mikrocontrollern wie Arduino über SerialPort in VB.NET und C# empfangen" class="wp-image-15257" title="Daten von Mikrocontrollern wie Arduino über SerialPort in VB.NET und C# empfangen"/></a><figcaption class="wp-element-caption">Daten von Mikrocontrollern wie Arduino über SerialPort in VB.NET und C# empfangen</figcaption></figure>



<p>Bisher war die Kommunikation mit der jeweiligen Gegenstelle relativ einseitig, oder? Es ist also mehr als an der Zeit, auch eine wirkliche Antwort des Geräts zu empfangen, sowie zu verarbeiten. Auch hier gibt es verschiedene Lösungen, von Denen ich schon Einige durch hatte. Viele Entwickler – darunter auch ich – erfuhren in Vergangenheit von gewissen Problemen, wie z. B. abgeschnittene Daten, usw.</p>



<p>Für unser Beispiel werde ich auch hier die womöglich einfachste Variante vorstellen. Melde Dich gerne in den Kommentaren unten, falls Du komisches Verhalten feststellen, oder etwas anderes zu berichten hast! Nun aber zurück zum eigentlichen Thema..</p>



<h3 class="wp-block-heading">Polling ist &#8222;plöde&#8220;, gell!?</h3>



<p>Wie schon erwähnt, gibt es verschiedene Möglichkeiten Daten aus dem SerialPort zu empfangen. Darunter wäre einerseits das typische Polling. Wenn Dir dieser Begriff nichts sagt, stell&#8216; Dir einfach das gute alte Kind auf dem Rücksitz vor: &#8222;Sind wir schon da?.. Sind wir schon da?.. Sind wir schon da?.. usw.&#8220;. Im Endeffekt fragt etwas einfach die Gegenstelle permanent, ob was anliegt, bzw. ob sich was geändert hat. Für gewöhnlich passiert das dann in einer Art Endlosschleife, oder a la &#8222;solange Gerät verbunden&#8220;.</p>



<h3 class="wp-block-heading">Ereignisse zur Rettung!</h3>



<p>Wenn wir also keine Lust auf das permanente, nervige und ineffiziente &#8222;Sind wir schon da?&#8220; haben, brauchen wir eine Alternative. Dazu gibt uns die SerialPort-Klasse ein Ereignis namens &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.datareceived?view=dotnet-plat-ext-7.0" target="_blank" rel="noreferrer noopener">DataReceived</a></strong>&#8220; mit an die Hand. Nun müssen wir also nur noch einen Handler an geeigneter Stelle verknüpfen und schon können wir die empfangenen Daten verarbeiten.</p>



<p>Ein einfaches Beispiel könnte dazu wie gleich folgend aussehen. Beachte hierbei, dass ich die Instanz des SerialPorts als Form-Member ausgelagert habe, damit ich auch in der Handler-Sub/-Void darauf zugreifen kann. Das Schreiben des &#8222;Befehls&#8220; kann natürlich in z. B. einen Button-Click-Handler o. Ä. ausgelagert werden. Für ein einfaches Beispiel lasse ich es hier im Konstruktor.</p>



<p>Für das Auslesen der empfangenen Daten an sich (aus dem Puffer), verwende ich die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.readexisting?view=dotnet-plat-ext-7.0" target="_blank" rel="noreferrer noopener">&#8222;ReadExisting&#8220;-Funktion</a></strong>. Diese gibt uns einen String der aktuell vorhandenen Daten im Puffer zurück. Anschließend würde es dann Deinem Protokoll unterliegen, diesen String korrekt zu verarbeiten.</p>



<h3 class="wp-block-heading">Achtung – Stückelungen möglich!</h3>



<p>Hierbei ist es wie gesagt ganz wichtig zu verstehen, dass wir einfach nur einen Haufen serieller (aufeinanderfolgender) Daten bekommen. Diese kommen auch nicht unbedingt immer an einem Stück! Wenn ich in meinem vorherigen Beispiel z. B. &#8222;ID:?&#8220; zum Arduino schicke, könnte ich die gesamte Antwort in einem Schritt bekommen: &#8222;ID:Arduino Nano&#8220;.</p>



<p>Es könnte aber auch genauso gut sein, dass ich erst &#8222;ID:&#8220; beim ersten Aufruf des Ereignisses bekomme und dann in einem zweiten Durchlauf &#8222;Arduino Nano&#8220;. Das korrekte Auseinanderfriemeln bleibt also mehr oder weniger unsere Aufgabe!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="daten-empfangen-ereignis">Public Class Form1

  ' sonstiger Form-Code..

  Private _port As SerialPort
  
  Sub New ()
    _port = New SerialPort("COM4", 19200)
    AddHandler _port.DataReceived, AddressOf Port_DataReceived
    _port.Open()
    _port.Write("ID:?")
  End Sub

  Private Sub Port_DataReceived(sender As Object, e As SerialDataReceivedEventArgs)
    Dim data = _port.ReadExisting()
    Debug.Write($"data received: {data}")
  End Sub
 
  ' sonstiger Form-Code..

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="daten-empfangen-ereignis">public class Form1 : Form
{

  // sonstiger Form-Code..

  SerialPort _port;

  public Form1()
  {
    _port = new SerialPort("COM4", 19200);
    _port.DataReceived += Port_DataReceived;
    _port.Open();
    _port.Write("ID:?");
  }
  
  private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
  {
      var data = _port.ReadExisting();
      Debug.Write($"data received: {data}");
  }
  
  // sonstiger Form-Code..

}</pre>



<h3 class="wp-block-heading">Cross-Thread Probleme</h3>



<p>Beachte, dass das DataReceived-Ereignis, bzw. dessen Handler auf einem Hintergrund-Thread ausgeführt wird. Um also auf die grafische Oberfläche zugreifen zu können, müsstest Du zurück auf Deinen UI-Thread &#8222;hüpfen&#8220;. Das funktioniert z. B. so:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="ui-invoke">    Private Sub Port_DataReceived(sender As Object, e As SerialDataReceivedEventArgs)
        Dim data = _port.ReadExisting()
        BeginInvoke(Sub()
                        TextBox1.AppendText(data)
                    End Sub)
    End Sub</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="ui-invoke">private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  var data = _port.ReadExisting();
  BeginInvoke(() => TextBox1.AppendText(data));
}</pre>



<h2 class="wp-block-heading">Achtung – typische SerialPort Fehler!</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Gaengige-Fehler-bei-der-Verwendung-der-.NET-SerialPort-Klasse.png"><img loading="lazy" decoding="async" width="640" height="320" src="https://robbelroot.de/wp-content/uploads/2023/03/Gaengige-Fehler-bei-der-Verwendung-der-.NET-SerialPort-Klasse.png" alt="Gängige Fehler bei der Verwendung der .NET SerialPort Klasse" class="wp-image-15259" title="Gängige Fehler bei der Verwendung der .NET SerialPort Klasse"/></a><figcaption class="wp-element-caption">Gängige Fehler bei der Verwendung der .NET SerialPort Klasse</figcaption></figure>



<p>Eigentlich ist der obige Code relativ simpel, aber natürlich stößt man auch hier auf gewisse Probleme. Was ist z. B., wenn ich einen COM-Port öffnen möchte, der gar nicht &#8222;existiert&#8220;!? Was, wenn ich einen bereits geöffneten COM-Port versuche, erneut zu öffnen? Die Antwort ist ganz einfach: Dann gibt es dazu passende Ausnahmen (Exceptions).</p>



<h3 class="wp-block-heading">Öffnen eines nicht existenten COM-Ports</h3>



<p>Der vermutlich häufigste Fehler ist gleichzeitig der Offensichtlichste: Du öffnest die Verbindung zu einem COM-Port, Welcher gar nicht verfügbar ist. Eventuell liegt es daran, dass der Nutzer in Deinen App-Einstellungen, in Vergangenheit einen falschen Port gewählt hatte. Vielleicht hat er/sie es auch einfach vergessen, das Gerät via USB anzuschließen.</p>



<p>In so einem Fall wird Dich mit aller Wahrscheinlichkeit der folgende Fehler begrüßen: <em>System.IO.FileNotFoundException: &#8222;Could not find file &#8218;COM6&#8216;.&#8220;</em>.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Ausnahme-FileNotFoundException-beim-Oeffnen-eines-fehlenden-COM-Ports.png"><img loading="lazy" decoding="async" width="408" height="120" src="https://robbelroot.de/wp-content/uploads/2023/03/Ausnahme-FileNotFoundException-beim-Oeffnen-eines-fehlenden-COM-Ports.png" alt="Ausnahme FileNotFoundException beim Öffnen eines fehlenden COM-Ports" class="wp-image-15173" title="Ausnahme FileNotFoundException beim Öffnen eines fehlenden COM-Ports"/></a><figcaption class="wp-element-caption">Ausnahme FileNotFoundException beim Öffnen eines fehlenden COM-Ports</figcaption></figure>



<h3 class="wp-block-heading">Doppelte Verbindungen auf einem COM-Port</h3>



<p>Versuchst Du z. B. eine COM-Port-Verbindung zu öffnen, obwohl Du bereits eine offene Verbindung hast, kommt es zu einem Fehler. Dies geschieht auch, wenn eine andere Software bereits die Verbindung auf diesem COM-Port geöffnet hat. Hierbei handelt es sich um die &#8222;UnauthorizedAccessException&#8220; aus dem folgenden Bild. Dazu habe ich einfach zwei Instanzen der SerialPort-Klasse für den gleichen Port (COM4) erstellt und versucht, Beide zu öffnen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="doppelte-com-verbindung-fehler">Dim portA = New SerialPort("COM4", 19200)
Dim portB = New SerialPort("COM4", 19200)
portA.Open()
' wird einen Fehler werfen!
portB.Open()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="doppelte-com-verbindung-fehler">var portA = new SerialPort("COM4", 19200);
var portB = new SerialPort("COM4", 19200);
portA.Open();
// wird einen Fehler werfen!
portB.Open();</pre>



<p>Der resultierende Fehler:<em> &#8222;System.UnauthorizedAccessException: &#8222;Access to the path &#8218;COM4&#8216; is denied.&#8220;&#8220;</em></p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Ausnahme-UnauthorizedAccessException-bei-doppelter-Verbindung-zum-COM-Port.png"><img loading="lazy" decoding="async" width="408" height="132" src="https://robbelroot.de/wp-content/uploads/2023/03/Ausnahme-UnauthorizedAccessException-bei-doppelter-Verbindung-zum-COM-Port.png" alt="Ausnahme UnauthorizedAccessException bei doppelter Verbindung zum COM-Port" class="wp-image-15163"/></a><figcaption class="wp-element-caption">Ausnahme UnauthorizedAccessException bei doppelter Verbindung zum COM-Port</figcaption></figure>



<h3 class="wp-block-heading">Komische Zeichen in den Antwort-Daten</h3>



<p>Der wohl – ich würde sagen – dritt-häufigste Fehler ist es, komische Zeichen in seinen SerialPort Antwort-Daten zu haben. Da tauchen dann unter anderem komische Striche, bzw. auch Fragezeichen auf. Sieht häufig blöd aus, aber die Lösung ist eigentlich relativ einfach: Du verwendest eine falsche Baudrate! Ebenso kommt das Problem zustande, wenn Du schon einmal Daten mit falscher Baudrate gesendet hast und DANN auf die richtige Baudrate wechselst!</p>



<p>Im Screenshot kannst Du sehen, wie so eine &#8222;fehlerhafte&#8220; Kommunikation aussehen könnte. Wenn Du im zweiten Versuch (nach dem Erhalt einer solchen Nachricht) eine erneute Nachricht sendest, sollte die Kommunikation normal laufen.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Falsche-Baudrate-bei-der-.NET-Kommunikation-mit-einem-SerialPort.png"><img loading="lazy" decoding="async" width="473" height="269" src="https://robbelroot.de/wp-content/uploads/2023/03/Falsche-Baudrate-bei-der-.NET-Kommunikation-mit-einem-SerialPort.png" alt="Komische Zeichen – Falsche Baudrate bei der .NET Kommunikation mit einem SerialPort" class="wp-image-15293" title="Komische Zeichen – Falsche Baudrate bei der .NET Kommunikation mit einem SerialPort"/></a><figcaption class="wp-element-caption">Komische Zeichen – Falsche Baudrate bei der .NET Kommunikation mit einem SerialPort</figcaption></figure>



<h2 class="wp-block-heading">Downloads &amp; Weiteres</h2>



<p>Falls Du es eilig hast, oder einfach nur loslegen möchtest, kannst Du Dir das passende Beispielprojekt hier direkt herunterladen. Alternativ empfehle ich Dir auch die Verwendung eines <strong><a href="https://robbelroot.de/blog/category/general/nuget-packages/" target="_blank" rel="noreferrer noopener">meiner NuGet-Pakete</a></strong>, damit geht es ggf. ebenso schnell.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=09488885-c321-424e-8494-fcac946c66e0" target="_blank" rel="noreferrer noopener">SerialPortExampleVB.zip</a></div>



<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=20bc81c2-9f8d-4c9d-8bda-98dac047425c" target="_blank" rel="noreferrer noopener">SerialPortExampleCS.zip</a></div>
</div>
<p>Der Beitrag <a href="https://robbelroot.de/blog/net-kommunikation-mit-arduino-und-co-ein-kompletter-guide/">.NET SerialPort-Kommunikation mit Arduino &#038; Co. – 2024 Guide</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/net-kommunikation-mit-arduino-und-co-ein-kompletter-guide/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>.NET Dependency Injection (DI) mit Autofac</title>
		<link>https://robbelroot.de/blog/net-dependency-injection-di-mit-autofac/</link>
					<comments>https://robbelroot.de/blog/net-dependency-injection-di-mit-autofac/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 15 Mar 2023 12:12:25 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[abhängigkeiten]]></category>
		<category><![CDATA[autofac]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[control]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[dependencies]]></category>
		<category><![CDATA[dependency]]></category>
		<category><![CDATA[di]]></category>
		<category><![CDATA[icontainer]]></category>
		<category><![CDATA[injection]]></category>
		<category><![CDATA[inversion]]></category>
		<category><![CDATA[ioc]]></category>
		<category><![CDATA[of]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vb.net]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[visual basic]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=14833</guid>

					<description><![CDATA[<p>Sauberere Programmierung durch Dependency Injection (DI) Du möchtest Deinen VB.NET, bzw. C# Anwendungen einen &#8222;Level Up&#8220; durch Dependency Injection (kurz DI) verpassen? Oder möchtest Du einfach mehr über &#8222;das Thema mit den Abhängigkeiten&#8220; lernen? Anders gefragt: Baust Du Dir auch jedes Mal einen neuen Hammer, nur weil Du einen weiteren &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/net-dependency-injection-di-mit-autofac/">.NET Dependency Injection (DI) mit Autofac</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Dependency-Injection-in-VB-und-C-.NET-mit-Autofac.png"><img loading="lazy" decoding="async" width="1280" height="720" src="https://robbelroot.de/wp-content/uploads/2023/03/Dependency-Injection-in-VB-und-C-.NET-mit-Autofac.png" alt="Dependency Injection in VB und C# .NET mit Autofac" class="wp-image-14987" title="Dependency Injection in VB und C# .NET mit Autofac"/></a><figcaption class="wp-element-caption">Dependency Injection in VB und C# .NET mit Autofac</figcaption></figure>






<h2 class="wp-block-heading">Sauberere Programmierung durch Dependency Injection (DI)</h2>



<p>Du möchtest Deinen VB.NET, bzw. C# Anwendungen einen &#8222;Level Up&#8220; durch Dependency Injection (kurz DI) verpassen? Oder möchtest Du einfach mehr über &#8222;das Thema mit den Abhängigkeiten&#8220; lernen? Anders gefragt: Baust Du Dir auch jedes Mal einen neuen Hammer, nur weil Du einen weiteren Nagel in die Wand hämmerst &#8211; ne, oder!?</p>



<p>Aus diesem und ähnlichen Gründen macht es auch in der Programmierung keinen Sinn. Welche das genau sind und wie wir unsere .NET Anwendungen mit DI verbessern können, schauen wir uns im heutigen Beitrag an &#x1f913;! Stell&#8216; Dir einmal vor, Du gehst zum Zahnarzt und Du müsstest vorher selbst Dein Auto bauen. Beim Zahnarzt angekommen, müsste Dieser erstmal alle Geräte bauen und so setzt sich die Kette fort.. Bitte nicht!</p>



<h2 class="wp-block-heading">Kurzanleitungen</h2>



<p>Hast Du es eilig? Kenne ich.. Bekomme einen Überblick ohne Blabla, indem Du Dir dieser Kurzanleitungen anschaust. Wenn Du was nicht verstehst, oder mehr Infos benötigt, kannst Du zu den passenden Sektionen springen. verwende hierzu z. B. das Inhaltsverzeichnis. Lade Dir auch gerne den zu Deinem Projekt passenden Beispielcode herunter, Diesen findest Du unter &#8222;<strong><a href="#downloads">Downloads</a></strong>&#8222;.</p>



<h3 class="wp-block-heading">Für VB.NET-Projekte</h3>



<ol class="wp-block-list">
<li><strong><a href="#Los-geht8217s-Dependency-Injection-Helfer-installieren">Installiere Autofac</a></strong> via NuGet Paket Manager</li>



<li>Erstelle die <strong><a href="#der-debug-logger">DebugLogger Klasse</a></strong> als Datei im Wurzelverzeichnis</li>



<li><strong><a href="#Anwendungsframework-deaktivieren">Deaktiviere das Anwendungsframework</a></strong> in den Projekteinstellungen</li>



<li>Erstelle einen <strong><a href="#Manuellen-Start-Prozess-definieren">manuellen Start-Prozess</a></strong></li>



<li>Falls 4 nicht funktioniert, <strong><a href="#Wichtiger-Hinweis">passe das in der Projektdatei an</a></strong></li>



<li>Instanziiere einen ContainerBuilder und registriere die Abhängigkeiten</li>



<li>Lass&#8216; den ContainerBuilder den Container erstellen und merke Dir Diesen in einer statischen Eigenschaft z. B. in der Program.vb</li>



<li>Lasse die Form vom DI zusammenbauen und zeige Sie an</li>
</ol>



<h3 class="wp-block-heading">Für C#-Projekte</h3>



<ol class="wp-block-list">
<li><strong><a href="#Los-geht8217s-Dependency-Injection-Helfer-installieren">Installiere Autofac</a></strong> via NuGet Paket Manager</li>



<li>Erstelle die <strong><a href="#der-debug-logger">DebugLogger Klasse</a></strong> als Datei im Wurzelverzeichnis</li>



<li>Instanziiere einen ContainerBuilder und registriere die Abhängigkeiten</li>



<li>Lass&#8216; den ContainerBuilder den Container erstellen und merke Dir Diesen in einer statischen Eigenschaft z. B. in der Program.vb</li>



<li>Lasse die Form vom DI zusammenbauen und zeige Sie an</li>
</ol>



<h2 class="wp-block-heading">Was ist Dependency Injection?</h2>



<p>Nunja, bevor wir gleich überhaupt irgendwie mit dem &#8222;Wie&#8220; starten können, sollten wir uns vorerst um ein grundsätzlich Verständnis über DI kümmern. Bitte bedenke, dass ich diesen Beitrag hier nicht sprengen möchte! Daher werde ich das Thema &#8222;Was ist Dependency Injection&#8220;, bzw. den Fokus auf der sachlichen DI-Thematik zu einem späteren Zeitpunkt, separat und in mehr Detail bearbeiten. </p>



<p>Grundsätzlich könnte man die zu Deutsch &#8222;Injektion von Abhängigkeiten&#8220; wie folgt, ganz einfach und in ca. 2-3 Sätzen beschreiben:</p>



<ol class="wp-block-list">
<li>Dependency Injection ist eine Planungs-/Vorgehensweise bei der Softwareentwicklung.</li>



<li>Dabei achtet man darauf, dass Objekte, weitere Objekte (Abhängigkeiten) bekommen, Welche Sie zur Erledigung ihrer Arbeiten benötigen.</li>



<li>Objekte erstellen also nicht selbst ihre notwendigen Werkzeuge / Abhängigkeiten.</li>
</ol>



<p>Hierbei kann man ganz leicht viele Beispiele finden:</p>



<ol class="wp-block-list">
<li>Der Programmierer mit seinem PC und Co.</li>



<li>Die Erzieherin im Kindergarten</li>



<li>Der Holzfäller mit seiner Axt</li>



<li>Die Friseurin mit Schere und Co.</li>



<li>oder auch der Taxifahrer mit seinem Fahrzeug.</li>
</ol>



<p>Wie Du Dir schon vorstellen kannst, wäre es nicht wirklich effizient / toll, wenn jede dieser Personen ihre Werkzeuge selbst bauen müsste &#8211; ohoh.. Zum Schluss dieses Abschnitts noch ein Hinweis: Streng genommen geht DI auch noch mit einem anderen Prinzip namens &#8222;Inversion of Control&#8220; kurz &#8222;IoC&#8220; einher. Darüber würde ich dann aber in einem anderen, zukünftigen Beitrag philosophieren.</p>



<h2 class="wp-block-heading">Warum ist DI gerade in .NET wichtig?</h2>



<p>Auch wenn folgendes sicherlich ein gewagter Kommentar ist, werde ich Ihn trotzdem aussprechen. Die Erfahrungen aus den vielen Jahren meiner Entwicklerkarriere zeigen leider, dass besonders VB.NET-Entwickler gegen solche Dinge wie DI &#8222;verstoßen&#8220;, oder es für Viele eben ein Fremdwort ist. Klar, es ist letztendlich – wie immer – nur eine Art &#8222;Hilfs-Regelwerk&#8220;, aber man sieht schon allein an diesen Dingen häufig die tatsächliche Erfahrung eines Entwicklers. Ich behaupte: Jeder professionelle Entwickler sollte wissen, was DI ist, wo es konkret unterstützt und wie man es einigermaßen umsetzt.</p>



<p>Häufig höre ich von Menschen aus dem .NET-Umfeld: &#8222;Ich programmiere schon 30 Jahre&#8220;.. Das freut mich als .NET-Entwickler-Urgestein natürlich, allerdings bringt einem das gar nichts, wenn man selbst nach 30 Jahren immer noch nicht mit DI in Berührung gekommen ist. Ob man das jetzt für ein jeweiliges Projekt nutzt oder nicht, ist immer wieder eine neue Entscheidung. Häufig höre ich dies besonders aus dem VB.NET-Bereich, sprich meinen eigenen Wurzeln.</p>



<p>Bitte verstehe mich an dieser Stelle nicht falsch, ich möchte niemanden geißeln o. Ä., letztendlich ist es ja auch nie für Weiterbildung zu spät. Besonders in der IT gilt ja sowieso: &#8222;Bilde Dich weiter, oder Du bleibst auf der Strecke&#8220; und final muss man selbst als Unternehmer oder Angestellter entscheiden, Welche Werkzeuge angesichts der jeweiligen Situation (Budget, Auflagen des Teams, etc.) zum gewünschten Endergebnis führen.</p>



<h2 class="wp-block-heading">Los geht&#8217;s – Dependency Injection Helfer installieren</h2>



<p>Nachdem Du nun einen kurzen Crashkurs in die Welt der <strong><a href="https://de.wikipedia.org/wiki/Dependency_Injection" target="_blank" rel="noreferrer noopener">Dependency Injection</a></strong> an sich erhalten hast, widmen wir uns nun der Programmierung – ich weiß, endlich.. Wie immer gilt: &#8222;Erfinde das Fahrrad nicht neu, außer es bringt Dir im Vergleich mehr Vorteile, als es Dich Zeit kostet&#8220;. Deshalb werden wir auch hier auf vorhandene und vor Allem etablierte Helferlein zurückgreifen.</p>



<p>Hierbei spreche ich von dem <a href="https://autofac.org/" target="_blank" rel="noreferrer noopener"><strong>NuGet-Paket namens &#8222;Autofac&#8220;</strong></a>, Welches mit (aktuell) 201 Millionen Downloads eine ordentliche Hausnummer darstellt. Dementsprechend kann man sich natürlich auch dessen Resonanz in der Entwicklerszene vorstellen. Gehe also bitte für den Anfang hin und installiere dieses Paket aus dem NuGet Paket Manager.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/Autofac-NuGet-Paket-Bibliothek.png"><img loading="lazy" decoding="async" width="675" height="278" src="https://robbelroot.de/wp-content/uploads/2023/03/Autofac-NuGet-Paket-Bibliothek.png" alt="Autofac NuGet Paket Bibliothek" class="wp-image-14994" title="Autofac NuGet Paket Bibliothek"/></a><figcaption class="wp-element-caption">Autofac NuGet Paket Bibliothek</figcaption></figure>



<p>Klicke dazu z. B. mit Rechtsklick auf Dein Projekt im Projektmappenexplorer (oben rechts – nicht auf die Projektmappe selbst!) und wähle &#8222;NuGet-Pakete verwalten&#8220; aus. Danach suchst Du wie oben im Bild gezeigt nach &#8222;autofac&#8220; und solltest den dementsprechenden Eintrag finden.</p>



<p>Wähle das Paket aus und bestätige die Installation für Dein Projekt auf der rechten Seite des aktuellen Fensters. Nach einem kurzen Moment sollte die Installation abgeschlossen und durch einen kleinen Haken auf dem Icon (links) bestätigt sein. Manchmal hängt Visual Studio jedoch, bzw. ist ein wenig buggy, öffne den NuGet Paketmanager dann einfach nochmal.</p>



<h2 class="wp-block-heading" id="der-debug-logger">Injizierbares Werkzeug erstellen</h2>



<p>Um einem Objekt ein anderes, weiteres Objekt zur Verfügung zu stellen (es zu injizieren), benötigen wir erst einmal das Erste. Objekt A wird in unserem Beispiel die obligatorische &#8222;Form1&#8220; sein, denn Diese haben wir bereits beim Start eines neuen Projekts. Auch in WPF-Anwendungen können selbstverständlich ähnlich verfahren. Ich denke jedoch, dass jeder .NET-Entwickler die guten alten Windows Forms kennt, daher fange ich der Einfachheit halber damit an.</p>



<p>Nun erstellen wir das Werkzeug, Welches in die Form1 injiziert werden soll. Natürlich kann es anschließend auch in viele andere Ziele injiziert werden.</p>



<p>Erstelle nun eine Klasse namens &#8222;DebugLogger&#8220;, Welche nur eine kleine Methode namens &#8222;Log&#8220; beinhalten wird. Ich möchte den Fokus an dieser Stelle nicht zu sehr auf die Klasse ziehen, daher betrachte Diese hier nicht als mehr, als einen Bauplan für ein Werkzeug.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="debug-logger">public class DebugLogger
{

  public DebugLogger()
  {
    
  }

  public void Log(string msg)
  {
    Debug.WriteLine($"{DateTime.Now:HH:mm:ss} {msg}");
  }

}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="debug-logger">Public Class DebugLogger

  Sub New()
    
  End Sub

  Public Sub Log(msg As String)
    Debug.WriteLine($"{DateTime.Now:HH:mm:ss} {msg}")
  End Sub

End Class</pre>



<h2 class="wp-block-heading">Ein eigener Start-Prozess</h2>



<p>Wenn ich mit heutigem Datum ein VB.NET Projekt erstelle, habe ich das Problem, dass ich – mehr oder weniger – nicht auf den Start-Prozess eingreifen kann. Es ist ohne weitere Kenntnisse nicht wirklich erkenntlich, wo das Programm eigentlich startet. Bei C# ist das schon wesentlich einfacher, bzw. auch schon für Anfänger, durch die separate &#8222;Program.cs&#8220;-Datei ersichtlicher. <strong>Wenn Du also ein C#-Projekt erstellen solltest, wird es hier einfacher und Du könntest Diese Sektion / Schritte überspringen.</strong></p>



<h3 class="wp-block-heading">Anwendungsframework deaktivieren</h3>



<p>Begib Dich hierzu zuerst einmal in die Einstellungen Deines VB.NET-Projektes und deaktiviere das Anwendungsframework. Das ist eine Checkbox, Welche Du unter dem Reiter &#8222;Anwendung&#8220; findest. Nur dann hatte ich bisher die Möglichkeit einen manuellen Start-Prozess zu definieren.</p>



<p>Beachte, dass Du diesen Menüpunkt bei modernen Projekttemplates / Visual Studio Versionen einfach via Suchleiste suchen kannst!</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/VB.NET-Anwendungsframework-in-Windows-Forms-App-deaktivieren.png"><img loading="lazy" decoding="async" width="874" height="674" src="https://robbelroot.de/wp-content/uploads/2023/03/VB.NET-Anwendungsframework-in-Windows-Forms-App-deaktivieren.png" alt="VB.NET Anwendungsframework in Windows Forms App deaktivieren" class="wp-image-15043" title="VB.NET Anwendungsframework in Windows Forms App deaktivieren"/></a><figcaption class="wp-element-caption">VB.NET Anwendungsframework in Windows Forms App deaktivieren</figcaption></figure>



<h3 class="wp-block-heading" id="manuellen-startpunkt">Manuellen Start-Prozess definieren</h3>



<p>Bereiten wir also nun das VB.NET Windows Forms Projekt vor, damit wir hier auf einem &#8222;gleichen Stand&#8220;, wie bei einem neuen C#-Projekt sind. Erstelle analog zu einem C#-Projekt einfach eine &#8222;Program.vb&#8220;-Datei innerhalb des Wurzelverzeichnisses Deines Projekts.</p>



<p>Des Weiteren benötigt jede gängige Anwendung eine &#8222;Main&#8220;-Methode. Diese erstellen wir nun in einem typischen Sinne. Um unsere Anwendung dann tatsächlich starten / ausführen zu lassen, benötigen wir die &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.windows.forms.application.run?view=windowsdesktop-7.0" target="_blank" rel="noreferrer noopener">Application.Run</a></strong>&#8222;-Methode. Für gewöhnlich erstellt man einfach eine Instanz der jeweiligen zu anzeigenden Form und übergibt Dieser der erwähnten Run-Methode.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="main-methode">public class Program
{

  static void Main(string[] args)
  {
    Application.EnableVisualStyles();

    // App wird geschlossen, wenn Form1 geschlossen wird
    var frm = new Form1();
    Application.Run(frm);

    // erlaubt mehrere Formulare
    // du musst dich manuell um das Schließen der App kümmern!
    // var frm = new Form1();
    // frm.Show();
    // Application.Run();
  }

}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="main-methode">Public Class Program

    Public Shared Sub Main(args As String())
      Application.EnableVisualStyles()

      ' App wird geschlossen, wenn Form1 geschlossen wird
      Dim frm = New Form1()
      Application.Run(frm)

      ' erlaubt mehrere Formulare
      ' du musst dich manuell um das Schließen der App kümmern!
      ' Dim frm = New Form1()
      ' frm.Show()
      ' Application.Run()
    End Sub

End Class</pre>



<p>Wenn Du nicht möchtest, dass sich das Programm schließt, wenn Du diese Form schließt, dann muss der Aufruf anders aussehen. Schaue Dir dazu das untere Kommentar und den dazugehörigen Code an. Schaue auch gerne in <strong><a href="https://robbelroot.de/blog/vbnet-form-oeffnen/" target="_blank" rel="noreferrer noopener">meinem Beitrag über Windows Formulare und Dialoge</a></strong> vorbei.</p>



<p>Wähle nun final die &#8222;neue&#8220; Main-Methode in den Projekteinstellungen aus, damit Dein Programm auch tatsächlich dort loslegt. Begib Dich dazu erneut zu &#8222;Projekt-&gt;Eigenschaften-&gt;Anwendung&#8220; und wähle bei &#8222;Startobjekt&#8220; die jeweilige Main-Methode. Beachte, dass dies alles bei C# nicht notwendig ist, da Du eine fertige Konfiguration in der &#8222;Program.cs&#8220; finden wirst.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/VB.NET-Anwendung-Startobjekt-manuell-festlegen.png"><img loading="lazy" decoding="async" width="950" height="537" src="https://robbelroot.de/wp-content/uploads/2023/03/VB.NET-Anwendung-Startobjekt-manuell-festlegen.png" alt="VB.NET Anwendung Startobjekt manuell festlegen" class="wp-image-15104" title="VB.NET Anwendung Startobjekt manuell festlegen"/></a><figcaption class="wp-element-caption">VB.NET Anwendung Startobjekt manuell festlegen</figcaption></figure>



<h3 class="wp-block-heading">Wichtiger Hinweis</h3>



<p>Falls Du Deine Main-Methode nicht in der obigen Combobox finden kannst, habe ich eine Lösung für Dich. Beim meinem Visual Studio 2022 und dem Projekttemplate &#8222;Windows Forms Anwendung&#8220; für .NET 6 musst ich auch kurz fummeln – aber gut.. Das macht man dann einmal und gut ist – danach weiß man es ja schließlich. Sollte leider auch nur VB.NET Projekte betreffen..</p>



<p>Ich gehe nun im ersten Schritt davon aus, dass Du die Main-Methode korrekt angelegt hast:</p>



<ul class="wp-block-list">
<li>In einer Klasse</li>



<li>Public</li>



<li>Shared</li>



<li>Mit der typischen &#8222;args As String()&#8220; Signatur</li>
</ul>



<p>Falls Sie trotzdem nicht auftaucht, kannst Du das Problem manuell fixen. Mache rechts im Projektmappenexplorer einen Rechtsklick auf Dein Projekt und wähle &#8222;Projektdatei bearbeiten&#8220; aus. Dort kannst Du Dein Startobjekt nun mit einem passenden Tag angeben.</p>



<p>Schreibe dazu folgendes Tag hinein:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;StartupObject>{HierDerNamespaceDeinesProgramms}.{HierDasStartobjekt}&lt;/StartupObject></pre>



<p>Sieht dann final wie folgt aus &#8211; bitte nur das rote Rechteck anpassen, da Du sonst Dein Projekt zerschießen könntest:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/VB.NET-Anwendung-Startobjekt-manuell-in-Projektdatei-festlegen.png"><img loading="lazy" decoding="async" width="599" height="648" src="https://robbelroot.de/wp-content/uploads/2023/03/VB.NET-Anwendung-Startobjekt-manuell-in-Projektdatei-festlegen.png" alt="VB.NET Anwendung Startobjekt manuell in Projektdatei festlegen" class="wp-image-15112" title="VB.NET Anwendung Startobjekt manuell in Projektdatei festlegen"/></a><figcaption class="wp-element-caption">VB.NET Anwendung Startobjekt manuell in Projektdatei festlegen</figcaption></figure>



<h2 class="wp-block-heading">Autofac kommt in&#8217;s Spiel</h2>



<p>Nachdem wir im vorherigen Schritt alle Vorbereitungen getroffen haben, müsste Dein Projekt jetzt den gleichen Startpunkt wie im Code hier drüber haben. Bei C# musstest Du dafür nicht wirklich etwas tun, denn dort gibt es die &#8222;Program.cs&#8220;-Datei, sprich wo alles gestartet wird, von vornherein. Bei VB.NET muss man allerdings ein wenig Konfigurationsarbeit leisten, <strong><a href="#manuellen-startpunkt">schaue dazu in den passenden Abschnitt</a></strong>.</p>



<p>Nun können wir Autofac benutzen, um tatsächliche Abhängigkeiten – wie <strong><a href="#der-debug-logger">unseren &#8222;DebugLogger&#8220;</a></strong> – zu registrieren. Dies geschieht in einer von Autofac bereitgestellten Klasse namens &#8222;ContainerBuilder&#8220;, also natürlich in einer Instanz davon. Dieser Builder erstellt uns anschließend einen fertigen, sogenannten &#8222;Container&#8220;, wenn wir die &#8222;Build&#8220;-Methode aufrufen.</p>



<p>Dieser beinhaltet alle notwendigen Abhängigkeiten, bzw. auch die Pläne, wie Diese erstellt werden können. Das hängt letztendlich von der Registrierungs-Art innerhalb des sogenannten DI-Containers ab. Hier gehe ich aber nicht zu tief in diesem Beitrag. Den erstellten Container müssen wir dann zwischenspeichern, damit wir später Zugriff darauf haben. In diesem Fall ist meines Erachtens nach auch ein öffentliches, statisches Feld / Eigenschaft passend.</p>



<p>Wir passen den Inhalt der Program-Datei also wie gleich folgend an. Beachte auch, dass nicht nur der &#8222;DebugLogger&#8220;, sondern auch die &#8222;Form1&#8220; selbst registriert wird.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="container-builder">// wo die Imports halt hinkommen..
using Autofac;

// außerhalb der Main-Methode, aber innerhalb der Program-Datei
public static IContainer Container { get; set; }

// Inhalt der Main-Methode
var builder = new ContainerBuilder();
builder.RegisterType&lt;Form1>();
builder.RegisterType&lt;DebugLogger>();
Container = builder.Build();</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="container-builder">' wo die Imports halt hinkommen..
Imports Autofac

' außerhalb der Main-Methode, aber innerhalb der Program-Datei
Public Shared Property Container As IContainer

' Inhalt der Main-Methode
Dim builder = new ContainerBuilder()
builder.RegisterType(Of Form1)()
builder.RegisterType(Of DebugLogger)()
Container = builder.Build()</pre>



<h2 class="wp-block-heading">Eine (korrekte) Abhängigkeit erschaffen</h2>



<p>Im vorletzten Schritt müssen wir unserem Programm nun beibringen, dass die Form1 tatsächlich von unserem &#8222;DebugLogger&#8220; abhängig ist. Anders gesprochen: ..dass unsere Form den DebugLogger benötigt, um Ihre Arbeit / Funktionlität zu erledigen.</p>



<p>Der wichtigste Unterschied ist nun nach all Dem, dass wir den fertigen, gleichen DebugLogger in die Form injizieren werden. Okay, um genau zu sein, tun wir dies nicht selbst, es geschieht stattdessen über den DI-Container, Welchen wir vorher durch den &#8222;ContainerBuilder&#8220; erstellt haben.</p>



<p>Nun bringen wir unserer Form bei: &#8222;Hey, ich benötige den DebugLogger zur Erledigung meiner Arbeit&#8220;. Dies geschieht in den meisten Fällen über die sogenannte Konstruktor-Injektion, naja, eben weil Sie im Konstruktor stattfindet:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="korrekte-abhaengigkeit">public class Form1
{

  // Deklaration der Abhängigkeit
  DebugLogger _logger;

  public Form1(DebugLogger logger)
  {
    InitializeComponent();
    // der logger kommt in die Instanz "hinein"
    // von nun an, kann Dieser überall wo notwendig
    // verwendet werden
    _logger = logger;
    Load += Form1_Load;
  }

  private void Form1_Load(object sender, EventArgs e)
  {
   // die erste Verwendung unseres Loggers!!
    _logger.Log("Yay, Form1_Load wurde aufgerufen!");
  }

}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="korrekte-abhaengigkeit">Public Class Form1

  ' Deklaration der Abhängigkeit
  Private _logger As DebugLogger

  Sub New(logger As DebugLogger)
    InitializeComponent()
    ' der logger kommt in die Instanz "hinein"
    ' von nun an, kann Dieser überall wo notwendig
    ' verwendet werden
    _logger = logger
  End Sub

  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ' die erste Verwendung unseres Loggers!!
    _logger.Log("Yay, Form1_Load wurde aufgerufen!")
  End Sub

End Class</pre>



<h2 class="wp-block-heading">Die Form richtig &#8222;auflösen&#8220;</h2>



<p>Wichtig: Hierbei geht es nicht um eine Art Brausetablette! Auflösen bedeutet im Kontext der Dependency Injection, dass man die jeweilig definierten Abhängigkeiten aus dem Container abruft. Das Ziel-Objekt kann dann anschließend damit bestückt werden. Dazu bietet uns die Container-Instanz der Autofac-Bibliothek eine Methode namens &#8222;Resolve&#8220; an.</p>



<p>Hier kannst Du nun den finalen Code mit ein wenig Refactoring sehen, beachte noch den Form-Code aus dem vorherigen Abschnitt:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="finaler-code">using Autofac;

public class Program
{

  public static IContainer Container { get; set; }

  static void Main(string[] args)
  {
    PrepareDI();
    RunApp();
  }

  private static void PrepareDI()
  {
    var builder = new ContainerBuilder();
    builder.RegisterType&lt;DebugLogger>();
    builder.RegisterType&lt;Form1>();
    Container = builder.Build();
  }

  private static void RunApp()
  { 
    Application.EnableVisualStyles();
    var frm = Container.Resolve&lt;Form1>();
    frm.Show();
    Application.Run();
  }

}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="finaler-code">Imports Autofac

Public Class Program

    Public Shared Property Container As IContainer

    Private Shared Sub Main(args As String())
        PrepareDI()
        RunApp()
    End Sub

    Private Shared Sub PrepareDI()
        Dim builder = New ContainerBuilder()
        builder.RegisterType(Of DebugLogger)()
        builder.RegisterType(Of Form1)()
        Container = builder.Build()
    End Sub

    Private Shared Sub RunApp()
        Application.EnableVisualStyles()
        Dim frm = Container.Resolve(Of Form1)()
        frm.Show()
        Application.Run()
    End Sub

End Class</pre>



<h2 class="wp-block-heading" id="downloads">Downloads</h2>



<p>Wie (fast) immer biete ich Dir hier den kompletten Code zum Beitrag als Download an, so kannst Du alles schnell nachbauen und testen. Ziehe Dir einfach das Projekt Deiner Wahl, ob nun VB.NET oder C#!</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=0912694c-d442-4467-b8c3-8c2153fd8054" target="_blank" rel="noreferrer noopener">AutofacDependencyInjectionVB.zip</a></div>



<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=fc78cf2a-219d-43e2-b0f0-c0b54a74cddc" target="_blank" rel="noreferrer noopener">AutofacDependencyInjectionCS.zip</a></div>
</div>
<p>Der Beitrag <a href="https://robbelroot.de/blog/net-dependency-injection-di-mit-autofac/">.NET Dependency Injection (DI) mit Autofac</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/net-dependency-injection-di-mit-autofac/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>INotifyPropertyChanged in .NET automatisch implementieren</title>
		<link>https://robbelroot.de/blog/inotifypropertychanged-in-net-automatisch-implementieren-fuer-winforms-und-wpf/</link>
					<comments>https://robbelroot.de/blog/inotifypropertychanged-in-net-automatisch-implementieren-fuer-winforms-und-wpf/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 01 Mar 2023 11:51:09 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[C# Problemlösungen]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET Problemlösungen]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[WPF Problemlösungen]]></category>
		<category><![CDATA[bindung]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[datenbindung]]></category>
		<category><![CDATA[fody]]></category>
		<category><![CDATA[inotifypropertychanged]]></category>
		<category><![CDATA[mvvm]]></category>
		<category><![CDATA[notification]]></category>
		<category><![CDATA[notify]]></category>
		<category><![CDATA[propertychanged]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[wpf]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=14475</guid>

					<description><![CDATA[<p>Kennst Du das auch: Du sitzt vor Deinen .NET Klassen und musst für WPF und Co. die INotifyPropertyChanged-Schnittstelle das gefühlte tausendste Mal implementieren? Eigentlich hättest Du Besseres zu tun und der Kunde (oder Dein Chef) sitzt Dir schon im Nacken? Damit ist nun Schluss – also mit dem repetitiven implementieren &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/inotifypropertychanged-in-net-automatisch-implementieren-fuer-winforms-und-wpf/">INotifyPropertyChanged in .NET automatisch implementieren</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/INotifyPropertyChanged-in-.NET-automatisch-implementieren.png"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2023/03/INotifyPropertyChanged-in-.NET-automatisch-implementieren.png" alt="INotifyPropertyChanged in .NET automatisch implementieren" class="wp-image-14501" title="INotifyPropertyChanged in .NET automatisch implementieren"/></a><figcaption class="wp-element-caption">INotifyPropertyChanged in .NET automatisch implementieren</figcaption></figure>






<p>Kennst Du das auch: Du sitzt vor Deinen .NET Klassen und musst für WPF und Co. die INotifyPropertyChanged-Schnittstelle das gefühlte tausendste Mal implementieren? Eigentlich hättest Du Besseres zu tun und der Kunde (oder Dein Chef) sitzt Dir schon im Nacken? Damit ist nun Schluss – also mit dem repetitiven implementieren der Schnittstelle –, denn dafür gibt es Abhilfe!</p>



<p><strong>Installiere dazu einfach die beiden NuGet-Pakete &#8222;PropertyChanged.Fody&#8220; und &#8222;Fody&#8220;. Konfiguriere Diese mit einer passenden &#8222;FodyWeavers.xml&#8220;-Datei und erstelle eine Klasse, Welche die INotifyPropertyChange-Schnittstelle implementiert, fertig!</strong></p>



<h2 class="wp-block-heading">Keine Lust auf &#8222;Monkey Work&#8220; &#8211; Hilfe muss her!</h2>



<p>Jeder .NET Entwickler, Welcher mit Daten und dessen Änderungen in Verbindung kommt, kennt es. Man muss der grafischen Oberfläche &#8222;Hey, der Vorname hier, der hat sich geändert, bitte zeige Den einmal aktualisiert an!&#8220;, mitteilen. Für gewöhnlich passiert das im .NET-Umfeld über die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-7.0" target="_blank" rel="noreferrer noopener">INotifyPropertyChanged-Schnittstelle</a></strong>. Diese kann nicht wirklich viel – wie auch als Interface –, Sie gibt uns jedoch den Schlachtplan für genau diese Frage: &#8222;Welche Eigenschaft hat sich geändert?&#8220;.</p>



<p>Wie oben schon erwähnt kann es aber nun ziemlich langweilig und vor Allem nervig werden. Schließlich besteht unsere Anwendung für gewöhnlich aus zig Klassen, Welche alle gewisse Daten beinhalten, dessen Änderungen nach außen mitgeteilt werden möchten. Tun wir dies nicht, bekommt die grafische Oberfläche davon nichts mit und kann die aktualisierten Daten auch nicht anzeigen.</p>



<p>Uns bleibt also nichts Anderes übrig, als die Schnittstelle in jeder benötigten Klasse zu implementieren, oder? Zum Glück ist dem nicht so! Wie das funktioniert, erfährst Du im weiteren Verlauf.</p>



<h2 class="wp-block-heading">Im Videoformat</h2>



<p>Möchtest Du etwas auf die Augen und die Ohren bekommen? Ist Dir der Text eventuell zu träge? Dann schaue Dir gerne das passende Video auf meinem YouTube-Kanal an. Beachte, dass das deutsche Video VB.NET als Sprache verwendet, allerdings ist dies natürlich genauso gut auf C# anwendbar. Ich empfehle Dir trotzdem, den Beitrag ebenso anzuschauen, da ich hier nochmal in mehr Detail auf die einzelnen Dinge eingehen kann.</p>


<div class="async-youtube" data-embed="FQBWu_fiKO4" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading">Einfacher, ja, aber &#8222;ich dachte da ist mehr drin&#8220;</h2>



<p>Wenn Du meinen <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/" target="_blank" rel="noreferrer noopener">detaillierten Beitrag über die INotifyPropertyChanged-Schnittstelle</a></strong> gelesen / gesehen hast, kennst Du vermutlich schon eine Vereinfachung. Diese haben wir erreicht, indem wir eine mehr oder weniger generischere Variante geschrieben haben. Dort gab es eine gewisse Methode (NotifyOfPropertyChange) innerhalb einer Klasse, Welche den Namen der geänderten Eigenschaft entgegengenommen hat. Das hat uns zumindest das Auslösen des &#8222;PropertyChanged&#8220;-Ereignisses vereinfacht.</p>



<p>Das Tolle daran war auch, dass wir den Namen der geänderten Eigenschaft nicht manuell angeben mussten. Dies konnte durch das <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.runtime.compilerservices.callermembernameattribute?view=net-7.0" target="_blank" rel="noreferrer noopener">&#8222;CallerMemberName&#8220;-Attribut</a></strong> des übergebenen Parameters automatisch bestimmt werden – insofern die Methode aus dem Setter der Eigenschaft aufgerufen wurde. Wenn man dann noch den <strong>&#8222;NameOf&#8220;-Ausdruck</strong> verwendet, hat man schon eine wirklich solide Basis.</p>



<p>Letztendlich stinkt es einem dennoch, wenn man folgenden Code sieht, Dieser ist einfach nur repetitiv und somit nicht gerade <strong><a href="https://de.wikipedia.org/wiki/Don%E2%80%99t_repeat_yourself" target="_blank" rel="noreferrer noopener">DRY</a></strong> getreu. Ebenso bläht er den Code unnötig auf und man hat fast gar keine Lust mehr, Diesen zu lesen. Das ist natürlich fatal..</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="wiederholender-notify-code"> Public Class SomeThing
    Inherits PropertyChangedBase

    Private _company As String

    Public Property Company As String
        Get
            Return _company
        End Get
        Set(value As String)
            If _company = value Then
                Return
            End If
            _company = value
            NotifyOfPropertyChange()
        End Set
    End Property

    ' property 2...

    ' property 3...

    ' ...

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="wiederholender-notify-code">public class SomeThing : PropertyChangedBase
{
    private string _company;

    public string Company
    {
        get => _company;
        set
        {
            if (_company == value)
                return;
            _company = value;
            NotifyOfPropertyChange();
        }
    }

    // property 2...

    // property 3...

    // ...

}</pre>



<h2 class="wp-block-heading">Fody installieren</h2>



<p>Das NuGet-Paket des Vertrauens, Welches uns die Arbeit erleichtern wird, nennt sich &#8222;Fody&#8220;. Also zumindest das Haupt-Paket, zusätzlich brauchen wir ein Sub-Paket namens &#8222;PropertyChanged.Fody&#8220;. Installiere nun also bitte beide Pakete, indem Du entweder die grafische Oberfläche des NuGet Paket-Managers, oder dessen Konsole verwendest.</p>



<p>Die Entwickler von Fody empfehlen es, die Konsolen-Variante zu verwenden, ich hatte bisher aber ehrlich gesagt noch nie Probleme mit der GUI-Variante. Gehe oben in Visual Studios Menüleiste auf &#8222;Extras&#8220; und wähle &#8222;NuGet-Paket-Manager&#8220;. Dann wählst Du &#8222;Paket-Manager-Konsole&#8220;, schon sollte sich ebendiese Konsole öffnen.</p>



<p>Kopiere nun die beiden Befehle und führe Sie einzeln in der Paket-Manager-Konsole aus:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Install-Package Fody
Install-Package PropertyChanged.Fody</pre>



<h2 class="wp-block-heading">Fody konfigurieren</h2>



<p>Leider sind wir noch nicht fertig, nachdem wir die notwendigen Pakete installiert haben, noch nicht! Wir müssen die installierten Pakete noch konfigurieren. Fody weiß ansonsten nicht, was es tun soll, bzw. Welche Sub-Pakete (es gibt noch mehr..) verwendet werden sollen.</p>



<p>Wir haben für unseren Bedarf ja das &#8222;PropertyChanged.Fody&#8220;-Sub-Paket installiert, Dieses konfigurieren wir nun als &#8222;wir möchten das gerne verwenden&#8220;. Lege dazu im nächsten Schritt eine <strong>XML-Datei</strong> in Deinem Projektverzeichnis an. Klicke dazu einfach mit einem Rechtsklick auf Deine Projektmappe und wähle &#8222;Hinzufügen-&gt;Neues Element&#8220;. Beachte bitte, dass die neuste Version von Visual Studio eine neue Darstellung – die Kompaktansicht – gewählt hat.</p>



<p>Diese Kompaktansicht kannst Du ganz einfach &#8222;ausklappen&#8220;, indem Du die jeweilige Option unten links anklickst. Erstelle mit Hilfe des Dialoges nun die XML-Datei und betitle Sie als &#8222;<strong>FodyWeavers.xml</strong>&#8222;. In diese Datei gibst Du nun folgenden Text, für unser Beispiel ein:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="FodyWeavers.xml" data-enlighter-group="">&lt;?xml version="1.0" encoding="utf-8"?>
&lt;Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  &lt;PropertyChanged />
&lt;/Weavers></pre>



<p>Ich hatte hier übrigens ursprünglich nur das &#8222;Weavers&#8220;-Tag und das Kind &#8222;PropertyChanged&#8220; eingefügt. Der Rest wurde später automatisch ergänzt (ehrlich gesagt, keine Ahnung woher, vermutlich durch VS oder Fody selbst). Das sah vorher so aus und reichte wohl auch:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="FodyWeavers.xml" data-enlighter-group="">&lt;Weavers>
  &lt;PropertyChanged />
&lt;/Weavers></pre>



<h2 class="wp-block-heading">Unsere finale Klasse</h2>



<p>Im letzten Schritt reicht es aus, dass Du einfach nur eine Klasse erstellst, Welche die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle implementiert &#8211; das war&#8217;s! Dies kann z. B. durch Vererbung der im anderen Beitrag gezeigten &#8222;PropertyChangedBase&#8220;-Klasse passieren, oder Du machst es manuell. Ebenso stellt Fody ein Klassen-Attribut zur Vereinfachung bereit.</p>



<h3 class="wp-block-heading">Beispiel 1 &#8211; Vererbung (nach wie vor):</h3>



<p>Hier verwenden wir die ganz &#8222;normale&#8220; Vererbung, mit der <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/#PropertyChangedBase-Klasse" target="_blank" rel="noreferrer noopener">Klasse aus dem anderen Beitrag</a></strong>. Der Vorteil hier ist: Falls wir doch eigene Setter o Ä. schreiben, können wir immer noch auf die Hilfs-Methode &#8222;NotifyOfPropertyChange&#8220; zugreifen.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="finale-klasse-bsp-1"> Public Class SomeThing
    Inherits PropertyChangedBase

    Public Property Company As String

    ' property 2...

    ' property 3...

    ' ...

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="finale-klasse-bsp-1">public class SomeThing : PropertyChangedBase
{

    public string Company { get; set; }

    // property 2...

    // property 3...

    // ...

}</pre>



<h3 class="wp-block-heading">Beispiel 2 &#8211; Mit Attribut von Fody &amp; ohne Vererbung</h3>



<p>Hier benötigen wir keine zusätzliche Klasse! Durch das Attribut über der Klasse, weiß Fody, dass hier am Ende die INPC-Schnittstelle automatisch implementiert werden soll &#8211; nice!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="finale-klasse-bsp-2"> Imports PropertyChanged

 &lt;AddINotifyPropertyChangedInterface>
 Public Class SomeThing
    Inherits PropertyChangedBase

    Public Property Company As String

    ' property 2...

    ' property 3...

    ' ...

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="finale-klasse-bsp-2">using PropertyChanged;

[AddINotifyPropertyChangedInterface]
public class SomeThing
{

    public string Company { get; set; }

    // property 2...

    // property 3...

    // ...

}</pre>



<h2 class="wp-block-heading">&#8222;PrivateAssets&#8220;-Meldung / Warnung</h2>



<p>Durch die Verwendung der vorgestellten Vorgehensweise mit Fody, sparen wir uns (wie Du sicherlich gemerkt haben wirst) eine Menge an Aufwand. Leider kommt es hier und dann noch zu einer kleinen Warnung. Diese Warnung sieht in etwa wie folgt aus:</p>



<p><strong><em>Warnung FodyPackageReference &#8222;Fody: The package reference for PropertyChanged.Fody does not contain PrivateAssets=&#8217;All'&#8220;</em></strong></p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/PackageFodyReference-The-package-reference-does-not-contain-PrivateAssets-All.png"><img loading="lazy" decoding="async" width="686" height="170" src="https://robbelroot.de/wp-content/uploads/2023/03/PackageFodyReference-The-package-reference-does-not-contain-PrivateAssets-All.png" alt="PackageFodyReference - The package reference does not contain PrivateAssets All" class="wp-image-14536" title="PackageFodyReference - The package reference does not contain PrivateAssets All"/></a><figcaption class="wp-element-caption">PackageFodyReference &#8211; The package reference does not contain PrivateAssets All</figcaption></figure>



<p>In meiner aktuellen Installation für diesen Beitrag und dem zugehörigen Video, hatte ich dieses Problem nicht. Vielleicht nimmt das dem ein oder anderen Tester die Angst vor Neuem &#8211; es lohnt sich wirklich.</p>



<h3 class="wp-block-heading">Was bedeutet die &#8222;PrivateAssets&#8220;-Warnung?</h3>



<p>Bevor wir uns an die Problemlösung wagen, sollten wir vielleicht erstmal verstehen, was die Warnung überhaupt bedeutet. Kurzum ist dieses sogenannte &#8222;Metadata-Tag&#8220; (ein Schlüsselwort / Stichwort in den Metadaten) dazu da, um Abhängigkeiten zu steuern.</p>



<p>Ich zitiere dazu einfach mal den Punkt aus der <strong><a href="https://learn.microsoft.com/de-de/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets" target="_blank" rel="noreferrer noopener">offiziellen Dokumentation</a></strong>, wo Du natürlich gerne reinschauen darfst:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Sie verwenden eine Abhängigkeit möglicherweise rein als Entwicklungsumgebung und möchten diese nicht für Projekte verfügbar machen, die Ihr Paket nutzen. In diesem Szenario können Sie dieses Verhalten über die&nbsp;<code>PrivateAssets</code>-Metadaten steuern.</p>
</blockquote>



<p>Kurzum heißt das: Wenn wir nicht möchten, dass unsere verwendeten Helferlein (PropertyChanged.Fody, Fody) nach außen sichtbar sind, verwenden wir dieses Metadaten-Tag. Letztendlich brauchen wir Fody nur zur Entwicklung, daher ist es weniger nach außen relevant.</p>



<h3 class="wp-block-heading">Wie beheben?</h3>



<p>Die Warnung dementsprechend zu beheben ist nicht wirklich schwer, es ist letztendlich nur eine Konfigurationssache. <strong>Klicke </strong>dazu einfach mit einem <strong>Rechtsklick auf Dein Projekt</strong> und wähle &#8222;<strong>Projektdatei bearbeiten</strong>&#8222;. Danach fügst Du einfach das Tag wie im Bild markiert hinzu – schon sollte die Meldung verschwinden!</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/03/PrivateAssets-All-fuer-Fody-NuGet-Paket-festlegen.png"><img loading="lazy" decoding="async" width="792" height="140" src="https://robbelroot.de/wp-content/uploads/2023/03/PrivateAssets-All-fuer-Fody-NuGet-Paket-festlegen.png" alt="PrivateAssets All für Fody NuGet Paket festlegen" class="wp-image-14550" title="PrivateAssets All für Fody NuGet Paket festlegen"/></a><figcaption class="wp-element-caption">PrivateAssets All für Fody NuGet Paket festlegen</figcaption></figure>



<h2 class="wp-block-heading">Downloads</h2>



<p>Keine Lust alles abzutippen und manuell ran zu gehen? Verständlich! Lade Dir einfach das passende Beispielprojekt herunter und starte ohne Verzögerung.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=2fc6b841-79d9-407e-b9d2-e1954dd793a1" target="_blank" rel="noreferrer noopener">InpcAutomatischImplementierenVB.zip</a></div>


</div>
<p>Der Beitrag <a href="https://robbelroot.de/blog/inotifypropertychanged-in-net-automatisch-implementieren-fuer-winforms-und-wpf/">INotifyPropertyChanged in .NET automatisch implementieren</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/inotifypropertychanged-in-net-automatisch-implementieren-fuer-winforms-und-wpf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Eigene WPF Commands in MVVM VB.NET &#038; C# &#8211; der 2024 Guide</title>
		<link>https://robbelroot.de/blog/eigene-wpf-commands-in-mvvm-vbnet-und-csharp-der-guide/</link>
					<comments>https://robbelroot.de/blog/eigene-wpf-commands-in-mvvm-vbnet-und-csharp-der-guide/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Sat, 07 Jan 2023 11:32:01 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[ausführen]]></category>
		<category><![CDATA[befehle]]></category>
		<category><![CDATA[bindung]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[command]]></category>
		<category><![CDATA[commandparameter]]></category>
		<category><![CDATA[commands]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[datenbindung]]></category>
		<category><![CDATA[handler]]></category>
		<category><![CDATA[klick]]></category>
		<category><![CDATA[knopf]]></category>
		<category><![CDATA[knöpfe]]></category>
		<category><![CDATA[modern]]></category>
		<category><![CDATA[mvvm]]></category>
		<category><![CDATA[parameter]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vb.net]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[wpf]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=13398</guid>

					<description><![CDATA[<p>Nutze WPF Commands, vermeide &#8222;Button1_Click&#8220; &#38; Co. Im heutigen Beitrag schauen wir uns das Thema &#8222;eigene WPF Commands&#8220;, bzw. die passende Schnittstelle &#8222;ICommand&#8220; an. Damit werden wir der besonders aus Winforms-Zeiten (Windows Forms) bekannten Vermischung von Design und Quellcode entfliehen. Dazu schauen wir uns erst die Basis der &#8222;ICommand&#8220;-Schnittstelle an &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/eigene-wpf-commands-in-mvvm-vbnet-und-csharp-der-guide/">Eigene WPF Commands in MVVM VB.NET &#038; C# &#8211; der 2024 Guide</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Eigene-Commands-in-VB.NET-WPF-MVVM-mit-PasswordBox-Tricks-und-Co.-Guide-Thumbnail.png"><img loading="lazy" decoding="async" width="1280" height="720" src="https://robbelroot.de/wp-content/uploads/2023/01/Eigene-Commands-in-VB.NET-WPF-MVVM-mit-PasswordBox-Tricks-und-Co.-Guide-Thumbnail.png" alt="Eigene Commands in VB.NET WPF MVVM mit PasswordBox, Tricks und Co. - Guide" class="wp-image-13403" title="Eigene Commands in VB.NET WPF MVVM mit PasswordBox, Tricks und Co. - Guide"/></a><figcaption class="wp-element-caption">Eigene Commands in VB.NET WPF MVVM mit PasswordBox, Tricks und Co. &#8211; Guide</figcaption></figure>






<h2 class="wp-block-heading">Nutze WPF Commands, vermeide &#8222;Button1_Click&#8220; &amp; Co.</h2>



<p>Im heutigen Beitrag schauen wir uns das Thema &#8222;eigene WPF Commands&#8220;, bzw. die passende Schnittstelle &#8222;ICommand&#8220; an. Damit werden wir der besonders aus Winforms-Zeiten (Windows Forms) bekannten Vermischung von Design und Quellcode entfliehen. Dazu schauen wir uns erst die Basis der &#8222;ICommand&#8220;-Schnittstelle an und erstellen auch anschließend eine wiederverwendbare Klasse – wir haben schließlich keinen Bock, alles 10x zu schreiben! Um alles zu verstehen, geht es hier und da natürlich noch einmal Richtung Details, aber gut, so viel Zeit muss sein, wenn man nicht nur &#8222;Copy &amp; Paste&#8220;-Master sein möchte :)!</p>



<p class="info-banner"><strong>&#x1f4a1;</strong> <strong>Hinweis</strong>: Nutze gerne <strong><a href="#toc_container">das Inhaltsverzeichnis</a></strong>, um an passende Stellen zu springen, da es sich hierbei um ein ausführliches Thema handelt, Welches man nicht &#8222;mal eben&#8220; besprechen kann. Falls Du z. B. nur <strong><a href="#finale-delcommand-basisklasse">eine wiederverwendbare Command-Basisklasse</a></strong> benötigst, findest Du Diese weiter unten. Vielleicht möchtest Du auch einen Blick auf <strong><a href="#downloads">die Downloads-Sektion</a></strong> werfen?</p>



<style>
.info-banner {
    background: #03a9f4;
    padding: 2em;
    border-radius: 0.5em;
    color: whitesmoke;
}
.info-banner a {
    color: #FED766;
}
</style>



<p>In diesem Beitrag werden wir synchrone Commands besprechen, die asynchrone Variante werden wir im Detail, in einem der nächsten Beiträge angehen. Ebenso wird das Thema &#8222;Login&#8220; nur als Beispiel thematisiert und nicht vollständig realisiert. Allgemein möchte ich erreichen, dass Du hier rausgehen und &#8222;Nice, ich habe Commands verstanden&#8220; sagen kannst :)!</p>



<h2 class="wp-block-heading">Kurzfassung – .NET WPF Commands in MVVM</h2>



<p>Wenn Du es eilig hast, hier die <strong>Kurzfassung</strong> über .NET MVVM Commands in WPF:</p>



<ul class="wp-block-list">
<li><strong>Ersetze </strong>z. B. die typischen Click-<strong>Handler </strong>von Buttons <strong>durch </strong>passende Datenbindungen an sogenannte <strong>Commands</strong>. Beachte jedoch, dass Commands nicht nur von Buttons, sondern z. B. auch von Kontextmenüs unterstützt werden!</li>



<li>Dazu musst Du Deinem <strong>View </strong>in erster Linie einen <strong>Datenkontext geben</strong>, damit es weiß, <strong>woher </strong>es die &#8222;<strong>Dinge</strong>&#8220; durch Datenbindung <strong>bekommen </strong>kann.</li>



<li><strong>Sage </strong>in Deinem <strong>Button</strong>-XAML dann via <strong>Command-Eigenschafts-Bindung</strong>, dass Du das &#8222;<strong>LoginCommand</strong>&#8220; <strong>verwenden </strong>möchtest &#8211; voilà!</li>



<li><strong>Implementiere </strong>im nächsten Schritt die &#8222;<strong>ICommand</strong>&#8222;-Schnittstelle und <strong>erstelle </strong>danach in Deinem Datenkontext eine passende, bindbare <strong>Eigenschaft</strong>, z. B. &#8222;<strong>LoginCommand</strong>&#8220; zur Verfügung.</li>



<li><strong>Instanziiere </strong>die <strong>Command</strong>-basierte Eigenschaft nun z. B. <strong>in </strong>Deinem <strong>ViewModel</strong>&#8211;<strong>Konstruktor</strong>.</li>



<li><strong>Übergib</strong> manuelle oder gebundene <strong>Parameter</strong>, indem Du <strong>an </strong>die <strong>CommandParameter</strong>&#8211;<strong>Eigenschaft </strong>des (z. B.) Buttons bindest.</li>
</ul>



<p>Damit Du die Implementierung allerdings nicht hunderte Male und für z. B. jeden Knopf wiederholen musst, würde ich Dir eine Abstraktion durch eine Basisklasse vorschlagen. Eine derartige Klasse (für VB.NET &amp; C#) findest Du natürlich auch hier im Beitrag. Scrolle dazu einfach zu &#8222;<strong><a href="#wiederverwendbare-klasse">eine wiederverwendbare Command-Basisklasse</a></strong>&#8222;, oder direkt zu <strong><a href="#downloads">den Downloads</a></strong>.</p>



<h2 class="wp-block-heading">Beitrag als Video ansehen</h2>



<p>Wenn Du diesen Beitrag lieber im Videoformat genießen möchtest, kannst Du natürlich auch das <strong><a href="https://youtu.be/TZD6259Mma8" target="_blank" rel="noreferrer noopener">folgende Video</a></strong> (vielleicht auch ergänzend) ansehen. Besonders die Hinweise bezüglich der &#8222;PasswordBox&#8220; sind sehr wichtig!</p>


<div class="async-youtube" data-embed="TZD6259Mma8" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading" id="projekt-und-ordnerstruktur">Projekt- und Ordnerstruktur</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Projekt-und-Ordnerstruktur-–-VB.NET-WPF-Commands-in-MVVM_640px.png"><img loading="lazy" decoding="async" width="640" height="427" src="https://robbelroot.de/wp-content/uploads/2023/01/Projekt-und-Ordnerstruktur-–-VB.NET-WPF-Commands-in-MVVM_640px.png" alt="Projekt- und Ordnerstruktur – VB.NET WPF Commands in MVVM" class="wp-image-13421" title="Projekt- und Ordnerstruktur – VB.NET WPF Commands in MVVM"/></a><figcaption class="wp-element-caption">Projekt- und Ordnerstruktur – VB.NET WPF Commands in MVVM</figcaption></figure>



<p>Bevor wir mit dem eigentlichen Thema &#8222;WPF Commands in MVVM&#8220; loslegen können, legen wir uns als Erstes unsere Projektstruktur zurecht. Eine vernünftige Ordner- und Organisationsstruktur ist natürlich nicht nur nützlich, wenn man allein arbeitet, um z. B. immer in den gewohnten Ablauf hineinzukommen. Es hilft uns zusätzlich auch dabei, falls wir mal etwas outsourcen, bzw. jemanden um Hilfe bitten müssen – und das kommt häufig schneller vor, als man denkt! Wer will sich außerdem in seinem eigenen Code immer den Ast absuchen müssen, nur weil es jedes Mal anders aussieht? Keiner!</p>



<p>Natürlich könnte ich jetzt hier auch alles wieder von vorn erzählen, möchte es aber im Sinne des Entwicklers (Don&#8217;t repeat yourself &#8211; wiederhole dich nicht) vermeiden. Schaue Dir den Basis-Projektaufbau daher gerne in meinem Beitrag <strong><a href="https://robbelroot.de/blog/ein-wpf-mvvm-projekt-aufsetzen-guide/" target="_blank" rel="noreferrer noopener">&#8222;Wie setzt man ein VB.NET, bzw. WPF MVVM-Projekt auf&#8220;</a></strong> an.</p>



<h2 class="wp-block-heading">Die Rollenverteilung in .NET WPF MVVM</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2021/07/MVVM-getrennte-Entwickler-Designer-Rollen.gif"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2021/07/MVVM-getrennte-Entwickler-Designer-Rollen.gif" alt="Trennung von Designern und Entwicklern" class="wp-image-5074"/></a><figcaption class="wp-element-caption">Trennung von Rollen im MVVM-Entwurfsmuster</figcaption></figure>



<p>Da dieser Beitrag die Verwendung von Commands im Fokus hat, verstehe bitte, dass ich nicht zu tief in das &#8222;Model-View-ViewModel&#8220;-Entwurfsmuster selbst abtauchen kann. Allerdings ist für die Verwendung von WPF Commands natürlich ein gewisses Grundverständnis der MVVM-Architektur erforderlich. Besuche für eine erweiterte Erklärung gerne meinen <strong><a href="https://robbelroot.de/blog/mvvm-csharp/" target="_blank" rel="noreferrer noopener">Beitrag zum MVVM-Entwurfsmuster</a></strong>, auch wenn Dieser C#-geprägt ist, sind alle Erkenntnisse natürlich auch bei VB.NET analog.</p>



<p>Grundsätzlich geht es bei dieser Strukturierung von modernen .NET Applikationen (oder auch Applikationen im Allgemeinen) anhand des MVVM-Musters darum, eine saubere Rollentrennung zu erreichen. Insbesondere spricht man hier von der Trennung von Benutzeroberfläche und Geschäftslogik – woran meist verschiedene Personen wie Designer und Entwickler arbeiten. Dies bietet nicht nur die Möglichkeit, sich fast ganz auf seine Rolle konzentrieren zu können, sondern auch ganz andere praktische Vorteile. So erreicht man durch die einfachere Austauschbarkeit von &#8222;Views&#8220; und &#8222;ViewModels&#8220; etwa eine erhöhte Test- und Wartbarkeit seiner Software im Allgemeinen.</p>



<h3 class="wp-block-heading">Das View – die Aufgabe des Designers</h3>



<p>Je nach Herangehensweise (also View-, oder ViewModel-First) geht z. B. der Designer hin und entwirft nach etwaigen Wireframing-Prozessen die gewünschte Oberfläche mit Hilfe der XAML-Auszeichnungssprache. Mit dem Kunden wurden also ggf. vorherige Absprachen (mit Skizzen &#8211; Wireframes) getroffen, Welche das ungefähre Layout und Design zumindest grob bestimmen sollen. Dies kann dann z. B. ohne eine Zeile Code geschrieben zu haben wie gleich folgend aussehen und eine gemeinsame Basis / einen gemeinsamen Konsens bilden. Zugegebenermaßen ist das folgende Beispiel schon eher auf einem &#8222;Non-Plus-Ultra&#8220;-Level, da man – je nach Unternehmensgröße – eher ein &#8222;mach&#8216; Mal&#8220; geliefert bekommt. Aber gut, träumen darf man als Entwickler noch, oder!?</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/MVVM-WPF-XAML-Wireframing-Prozess.png"><img loading="lazy" decoding="async" width="2880" height="1600" src="https://robbelroot.de/wp-content/uploads/2023/01/MVVM-WPF-XAML-Wireframing-Prozess.png" alt="MVVM WPF XAML Wireframing Prozess – Quelle: moqups Wireframing Tool" class="wp-image-13479" title="MVVM WPF XAML Wireframing Prozess"/></a><figcaption class="wp-element-caption">MVVM WPF XAML Wireframing Prozess – Quelle: moqups.com Wireframing Tool</figcaption></figure>



<p>Wenn unsere Wünsche daher also wahr werden, haben die Designer die Möglichkeit, die skizzierten Wünsche des Kunden relativ unabhängig vom Entwickler (-Team) in XAML umsetzen zu können. Ganz im Stil von WPF und Co. verwendet man hier Datenbindungen, also XAML Binding-Anweisungen, wie z. B.:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;TextBox Text="{Binding WelcomeMessage}" /></pre>



<p>Den Designern kann und soll es an dieser Stelle egal sein, wie genau die &#8222;WelcomeMessage&#8220; erschaffen und verändert wird, solange Sie daran binden können &#8211; ergo sich an die WPF-Mechanismen gehalten wird. Viele verkaufen den utopischen Traum des &#8222;Solo-Separat-Entwickelns&#8220; zu gern, allerdings muss natürlich trotzdem Austausch zwischen Entwicklern und Designern stattfinden. Ein &#8222;wir arbeiten vollkommen getrennt voneinander&#8220; ist also nicht zu 100% möglich, ABER eben viel viel einfacher. Die Designer können ja schließlich z. B. auch nicht raten, ob unsere &#8222;IsSelected&#8220;-Eigenschaft nun eben sinnigerweise so, oder &#8222;Selected&#8220; heißt. Je öfter man zusammenarbeitet, desto leichter erschließen sich solche Muster natürlich in Zukunft und man kann immer unabhängiger, gar blind zusammenarbeiten.</p>



<h3 class="wp-block-heading">Der Entwickler, die ViewModels, Geschäftslogik und Co.</h3>



<p>Nachdem wir als Entwickler – im Optimalfall – also nun unsere Wireframes bekommen haben, können nun auch wir ziemlich isoliert / separat tätig werden. Wir entwerfen die passenden ViewModel-Klassen zu den gezeigten Oberflächen, Welche &#8222;INotifyPropertyChanged&#8220; und Co. verwenden, um durch die Datenbindungen zu kommunizieren. Schaue Dir hierzu gerne <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/" target="_blank" rel="noreferrer noopener">meinen Beitrag zum Thema &#8222;INotifyPropertyChanged&#8220;</a></strong> an. Bei Bedarf erschaffen wir Test-ViewModels, Welche wir durch die neue Herangehensweise, ohne Probleme und Aufwand austauschen und testen können.</p>



<p>Wie erwähnt möchte ich den Exkurs in Richtung &#8222;Model-View-ViewModel-Entwurfsmuster&#8220; nicht zu weit treiben, daher gehe ich hier nicht weiter auf z. B. Models ein. Ich denke sowieso, dass das nicht zielführend wäre, denn zu viel Input ist ja auch schlecht, oder? &#8222;Kleine&#8220; Häppchen machen dann schon eher den Appetit :)! </p>



<h2 class="wp-block-heading">Ein kleines Login-Fenster-Beispiel für WPF Commands</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Ein-erstes-eigenes-WPF-Command-in-MVVM.png"><img loading="lazy" decoding="async" width="640" height="423" src="https://robbelroot.de/wp-content/uploads/2023/01/Ein-erstes-eigenes-WPF-Command-in-MVVM.png" alt="Ein erstes, eigenes WPF Command in MVVM" class="wp-image-13507" title="Ein erstes, eigenes WPF Command in MVVM"/></a><figcaption class="wp-element-caption">Ein erstes, eigenes WPF Command in MVVM</figcaption></figure>



<p>Nach dem kleinen Exkurs von oben, schauen wir uns nun die weitere Reise in Richtung Commands an. Dazu bauen wir uns im nächsten Schritt eine kleine Login-Oberfläche (Achtung, billig..), Welche wie gleich folgend aussehen könnte. Hier werden wir auch direkt aus einer Art Designer-Perspektive agieren, also wir arbeiten in erster Linie &#8222;nur&#8220; am XAML-Code selbst.</p>



<p>Wir tun hier einfach mal so, als hätten wir ein passendes Wireframe vom Kunden vorliegen.. Was macht also ggf. in erster Linie eine Login-Oberfläche aus? Naja, wir brauchen:</p>



<ul class="wp-block-list">
<li>Eine Textbox für die Eingabe des Nutzers / E-Mail-Adresse</li>



<li>Ein Label / Textblock für diese Textbox, sonst weiß der Nutzer nicht was das soll</li>



<li>Eine PasswordBox für die Eingabe des Passworts (beachte hier die Hinweise)</li>



<li>Auch hierfür einen Textblock</li>



<li>Einen Button, wie soll der Benutzer sonst &#8222;Login-Versuch durchführen&#8220; triggern</li>



<li>ggf. weiteren Kram, wie &#8222;Passwort vergessen&#8220; &#8211; lassen wir hier aber aus..</li>
</ul>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Login-Fenster-mit-WPF-Commands-in-MVVM.png"><img loading="lazy" decoding="async" width="789" height="446" src="https://robbelroot.de/wp-content/uploads/2023/01/Login-Fenster-mit-WPF-Commands-in-MVVM.png" alt="Login-Fenster mit WPF Commands in MVVM" class="wp-image-13511" title="Login-Fenster mit WPF Commands in MVVM"/></a><figcaption class="wp-element-caption">Login-Fenster mit WPF Commands in MVVM</figcaption></figure>



<p>Bitte bedenke auch an dieser Stelle – wie oben angekündigt –, dass wir hier kein vollständiges Login-Beispiel realisieren können, da der Beitrag auch so schon lang genug wird &#x1f609;! Einen Login werde ich ggf. mal in einem anderen Beitrag separat behandeln und dort dann ausführen.</p>



<h3 class="wp-block-heading">Datenbindungen des Login-Fensters</h3>



<p>Nachdem wir im obigen Schritt die grundsätzlich benötigten Steuerelemente angemerkt haben, brauchen wir – für MVVM selbstverständlich – auch passende Datenbindungen. Ansonsten wüssten weder die Textboxen, woher Sie Ihren Text bekommen, oder gar &#8222;hinschicken&#8220; sollen. Ebenso hätte der Anmelden-Knopf keine Ahnung, was beim Klick passieren soll (Spoiler: Ein Command ausführen).</p>



<p>Der Designer wird an dieser Stelle hier irgendwie vorher (mit Kunden und Entwicklern) besprochen haben, dass es im ViewModel so etwas namens &#8222;User&#8220; geben muss. Dabei handelt es sich genauer genommen um eine String-Eigenschaft, welche den dementsprechenden Text für die Textbox – also den Benutzer – beinhalten muss. Ebenso muss diese Eigenschaft bei Code-technischen Änderungen nach außen kommunizieren, dass Sie sich geändert hat. Dies geht wie vermutlich bekannt über &#8222;INotifyPropertyChanged&#8220;, was hier allerdings nicht weiter Thema sein wird. Hier nochmal erneut der Hinweis auf <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/">meinen &#8222;INotifyPropertyChanged&#8220;-Beitrag mit &#8222;PropertyChanged&#8220;-Basisklasse</a></strong>.</p>



<p>Schaue Dir nun einmal den XAML-Code zum Fenster an, beachte, dass Du die obigen <strong><a href="#projekt-und-ordnerstruktur">&#8222;Projekt- und Ordnerstruktur&#8220;-Schritte</a></strong> korrekt abgeschlossen hast. Ansonsten wirst Du hier vermutlich Probleme mit Namespace und Co. bekommen. Passe ebenfalls auf, dass Du den Projektnamen umbenennen musst, in meinem Beispiel hieß das Projekt &#8222;WpfCommandsVbTutorial&#8220;.</p>



<h3 class="wp-block-heading">Datenkontext (DataContext) setzen</h3>



<p>Im ersten Schritt ergänzen wir die folgenden zwei Punkte in den XAML-Code des MainWindows (MainViews).</p>



<p>1. Wir mappen via &#8222;xmlns:&lt;name&gt;&#8220; einen Namespace anhand eines Aliases:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;!-- ganz oben, im Abschnitt des Window-Tags -->
xmlns:vm="clr-namespace:WpfCommandsVbTutorial.ViewModels"</pre>



<p>2. Wir geben dem jeweiligen View einen Datenkontext (DataContext-Eigenschaft des Windows) über XAML. Wie Du vielleicht weißt, wäre dies auch via &#8222;Code behind&#8220;-Datei möglich, ich habe mich hier für die XAML Variante entschieden:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;!-- im "Content" des Windows -->
&lt;Window.DataContext>
    &lt;vm:MainViewModel />
&lt;/Window.DataContext></pre>



<p>Du kannst an dieser Stelle übrigens die Styling-relevanten Dinge ignorieren, also praktisch alles, was in &#8222;StackPanel.Resources&#8220; z. B. steht. Dies ist nur zur Anschauung erstellt worden und Du solltest Deinen Fokus größtenteils auf die oben aufgelisteten Steuerelemente legen.</p>



<p class="info-banner"><strong>&#x1f4a1;</strong> Hinweis: Wir werden im weiteren Verlauf noch an der PasswordBox und Co. arbeiten, sowie wichtige Sicherheitsaspekte besprechen. Dies hier ist daher noch nicht die finale Variante! Ebenso fehlt hier auch noch ein wichtiger Punkt bei der Benutzer-Textbox!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;!-- in C#, there's a project prefix before Views -->
&lt;Window x:Class="Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfCommandsVbTutorial"
        mc:Ignorable="d"
        xmlns:vm="clr-namespace:WpfCommandsVbTutorial.ViewModels"
        Title="MainWindow" Background="DarkOrange" Height="450" Width="800" WindowStartupLocation="CenterScreen">
    
    &lt;Window.DataContext>
        &lt;vm:MainViewModel />
    &lt;/Window.DataContext>

    &lt;Grid>
        &lt;StackPanel Width="200" VerticalAlignment="Center" HorizontalAlignment="Center">
            &lt;StackPanel.Resources>
                &lt;Style TargetType="TextBlock">
                    &lt;Setter Property="Foreground" Value="WhiteSmoke" />
                    &lt;Setter Property="FontWeight" Value="SemiBold" />
                    &lt;Setter Property="FontSize" Value="16" />
                    &lt;Setter Property="Margin" Value="0 0 0 4" />
                &lt;/Style>
                &lt;Style TargetType="TextBox">
                    &lt;Setter Property="FontSize" Value="16" />
                    &lt;Setter Property="Margin" Value="0 0 0 4" />
                    &lt;Setter Property="Effect">
                        &lt;Setter.Value>
                            &lt;DropShadowEffect ShadowDepth="2" Direction="330" Color="Black" Opacity="0.5" BlurRadius="1"/>
                        &lt;/Setter.Value>
                    &lt;/Setter>
                &lt;/Style>
                &lt;Style TargetType="PasswordBox">
                    &lt;Setter Property="FontSize" Value="16" />
                    &lt;Setter Property="Margin" Value="0 0 0 8" />
                    &lt;Setter Property="Effect">
                        &lt;Setter.Value>
                            &lt;DropShadowEffect ShadowDepth="2" Direction="330" Color="Black" Opacity="0.5" BlurRadius="1"/>
                        &lt;/Setter.Value>
                    &lt;/Setter>
                &lt;/Style>
            &lt;/StackPanel.Resources>
            &lt;TextBlock Text="Benutzer" />
            &lt;TextBox Text="{Binding User}" />
            &lt;TextBlock Text="Passwort" />
            &lt;!-- die Passwordbox kann nicht so einfach gebunden werden und da dies nicht
Bestandteil dieses Beitrages ist, werde ich dies auf einen anderen Beitrag verlagern.
Konzentriere Dich bitte auf die Aspekte von WPF Commands :) -->
            &lt;PasswordBox />
            &lt;Button Command="{Binding LoginCommand}" Content="Anmelden" FontSize="16" />
        &lt;/StackPanel>
    &lt;/Grid>
&lt;/Window></pre>



<h2 class="wp-block-heading">Das ViewModel zur Oberfläche</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Das-MainViewModel-640px.png"><img loading="lazy" decoding="async" width="640" height="417" src="https://robbelroot.de/wp-content/uploads/2023/01/Das-MainViewModel-640px.png" alt="Ein ViewModel zur Oberfläche" class="wp-image-13686" title="Ein ViewModel zur Oberfläche"/></a><figcaption class="wp-element-caption">Ein ViewModel zur Oberfläche</figcaption></figure>



<p>Bevor wir mit den WPF Commands im Detail fortfahren können, müssen wir noch etwas bauen, was Diese letztendlich beinhaltet. Hierbei rede ich natürlich von einer ViewModel-Klasse, Welche alles Notwendige für die Datenbindungen bereitstellt. Neben dem oben im XAML erwähnten &#8222;LoginCommand&#8220;, bezieht sich dies natürlich auch auf die Eigenschaften wie &#8222;User&#8220; und &#8222;Password&#8220;.</p>



<p>Wenn wir dies auslassen würden, könnte die grafische Oberfläche Ihre dort empfangenen Änderungen nie via Datenbindungen an die dahinter liegenden ViewModel-Eigenschaften weitergeben. Somit hätten wir auch in unserer Geschäftslogik (innerhalb des ViewModels) nie die Chance, damit tatsächlich arbeiten zu können. Wie Du Dir also vorstellen kannst, wäre eine Anmeldung ohne z. B. typische Benutzer- und Passwort-Kombination ziemlich schwierig.</p>



<h3 class="wp-block-heading">Fehlermeldungen bei fehlender / falscher Datenbindung</h3>



<p>Falls Du an dieser Stelle also eine der typischen Binding-Fehlermeldungen bekommen solltest, kannst Du Dir nun vorstellen woran das liegt. Du hast vermutlich vergessen, den DataContext mit einer passenden ViewModel-Instanz zu befüllen – wie wir es im gleich folgenden Schritt tätigen werden. Typische Fehlermeldung sehen ungefähr so aus, also hier wurde z. B. die &#8222;User&#8220;-Eigenschaft nicht auf der &#8222;MainViewModel&#8220;-Instanz gefunden:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/BindingExpression-Fehler-–-User-konnte-nicht-fuer-die-Datenbindung-auf-dem-ViewModel-gefunden-werden.png"><img loading="lazy" decoding="async" width="856" height="75" src="https://robbelroot.de/wp-content/uploads/2023/01/BindingExpression-Fehler-–-User-konnte-nicht-fuer-die-Datenbindung-auf-dem-ViewModel-gefunden-werden.png" alt="BindingExpression Fehler – User konnte nicht für die Datenbindung auf dem ViewModel gefunden werden" class="wp-image-13762" title="BindingExpression Fehler – User konnte nicht für die Datenbindung auf dem ViewModel gefunden werden"/></a><figcaption class="wp-element-caption">BindingExpression Fehler – User konnte nicht für die Datenbindung auf dem ViewModel gefunden werden</figcaption></figure>



<p>Falls Du also eine derartige Fehlermeldung bekommst, stelle sicher, dass die Eigenschaft &#8222;User&#8220; in Deinem  Viewmodel existiert. Es kann hier aber auch vorkommen, dass z. B. gar kein Fehler kommt und dennoch nichts passiert. Dies ist z. B. der Fall, wenn Du vergisst, den DataContext überhaupt zu setzen! Denke daran, dass wir das hier im View selbst, also via XAML-Code weiter oben getan haben!</p>



<h3 class="wp-block-heading">Das (erste) MainViewModel erstellen</h3>



<p>Erstelle für die weitere Vorgehensweise in Richtung WPF Commands nun einmal bitte eine Klasse namens &#8222;MainViewModel&#8220; in Deinem &#8222;ViewModels&#8220;-Ordner. Unsere ViewModel-Klasse wird hierbei von der &#8222;PropertyChangedBase&#8220;-Basisklasse erben, damit wir die dort zurechtgeschriebene Funktionalität weiterverwenden können. Somit sparen wir es uns, die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle immer und immer wieder manuell implementieren zu müssen. Kopiere Dir die Basisklasse ruhig von <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/#PropertyChangedBase-Klasse" target="_blank" rel="noreferrer noopener">diesem Beitrag hier</a></strong>.</p>



<p>Nun das MainViewModel, wundere Dich bitte nicht, dass dort schon anderer Kram drin steht, aber ich möchte Dir diesen Batzen gleich nicht noch 3x um die Ohren hauen. Wir nehmen dies daher als Basis und arbeiten uns von dort aus weiter durch den Code. Es fehlen hier auch noch die ein odere andere Funktion, sowie eine Klasse für die Commands, Welche wir natürlich noch erstellen.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="main-view-model">Imports WpfCommandsVbTutorial.Utils

Namespace ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        Private _user As String

        Public Property User As String
            Get
                Return _user
            End Get
            Set(value As String)
                If _user = value Then
                    Return
                End If
                _user = value
                NotifyOfPropertyChange()
            End Set
        End Property

        Private _password As String

        Public Property Password As String
            Get
                Return _password
            End Get
            Set(value As String)
                If _password = value Then
                    Return
                End If
                _password = value
                NotifyOfPropertyChange()
            End Set
        End Property

        Public Property LoginCommand As LoginCommand

        Sub New()
            LoginCommand = New LoginCommand()
        End Sub

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="main-view-model">using Utils;

namespace ViewModels;

public class MainViewModel : PropertyChangedBase
{
    private string _user;

    public string User
    {
        get => _user;
        set
        {
            if (_user == value)
                return;
            _user = value;
            NotifyOfPropertyChange();
        }
    }

    private string _password;

    public string Password
    {
        get => _password;
        set
        {
            if (_password == value)
                return;
            _password = value;
            NotifyOfPropertyChange();
        }
    }

    public LoginCommand LoginCommand { get; set; }

    public MainViewModel()
    {
        LoginCommand = new LoginCommand();
    }

}</pre>



<h3 class="wp-block-heading">Die üblichen Mechanismen</h3>



<p>Zuerst einmal siehst Du im obigen MainViewModel-Code die 3 wichtigsten Eigenschaften für unser Login-Fenster:</p>



<ol class="wp-block-list">
<li>Die User-Eigenschaft</li>



<li>Die Password-Eigenschaft</li>



<li>Die LoginCommand-Eigenschaft</li>
</ol>



<p>Bis auf das &#8222;LoginCommand&#8220; sind alle Eigenschaften sogenannte &#8222;voll implementierte&#8220;-Eigenschaften, Sie haben also einen von uns manuell implementierten Getter und Setter. Dies geschieht deshalb, weil wir im Setter sagen müssen: &#8222;Hey, es hat sich Code-seitig z. B. der User geändert&#8220;. Das passiert einerseits, wenn wir einen gespeicherten Zugang von der Festplatten laden, aber auch, wenn der Benutzer des Programms eine Eingabe in der Textbox macht – besonders hierzu aber gleich noch etwas! Andererseits können wir dem Command das dann auch noch explizit mitteilen, denn warum sollte man sich anmelden können, wenn, wenn z. B. gar kein Benutzername eingegeben wurde? Dazu kommen wir aber noch..</p>



<p>Etwas anders sieht es wie schon gesagt beim &#8222;LoginCommand&#8220; aus, denn Dieses wird nach der Instanziierung nicht mehr geändert. Hier reicht also eine sogenannte automatisch implementierte Eigenschaft aus.</p>



<h2 class="wp-block-heading">Der erste Command-Versuch</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/WPF-Cmmand-nach-3-Klicks-sperren.gif"><img loading="lazy" decoding="async" width="796" height="452" src="https://robbelroot.de/wp-content/uploads/2023/01/WPF-Cmmand-nach-3-Klicks-sperren.gif" alt="WPF Command nach 3 Klicks sperren" class="wp-image-13848" title="WPF Command nach 3 Klicks sperren"/></a><figcaption class="wp-element-caption">WPF Command nach 3 Klicks sperren</figcaption></figure>



<p>An dieser Stelle werden wir den ersten Versuch Richtung eigener WPF Commands starten. Wir werden dafür das sogenannte &#8222;ICommand&#8220;-Interface (manuell) implementieren – später vereinfacht, keine Sorge! Erstelle also nun bitte eine weitere Klasse (ohne Datei) namens &#8222;LoginCmd&#8220;, Diese kannst Du für den Anfang einfach innerhalb des Wurzelverzeichnisses Deines Projektes ablegen. Dies werden wir selbstverständlich später ändern, brauchen wir aber am Anfang zur reinen Demonstration.</p>



<p>Schreibe für VB.NET anschließend – wie für Schnittstellen-Implementierungen üblich – einfach &#8222;Implements INotifyPropertyChanged&#8220; unter die beginnende Klassendefinition: &#8222;Public Class LoginCommand&#8220;. Für C# kannst Du ein &#8222;: INotifyPropertyChanged&#8220; hinter den Klassennamen schreiben. In beiden Fällen kannst Du dann die Imports durch die Visual Studio-Hilfe durchführen. Alternativ kannst Du auch Strg+&#8220;.&#8220; drücken und die dann erscheinenden Vorschläge durchführen.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCmd.vb" data-enlighter-group="erstes-command-1">Imports WpfCommandsVbTutorial.Utils

Namespace ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        ' restlicher MainViewModel-Code

        Public Property LoginCommand As LoginCommand

        Sub New()
            LoginCommand = New LoginCommand()
        End Sub

        Public Class LoginCmd
            Implements ICommand

            Public Sub Execute(parameter As Object) Implements ICommand.Execute
                Throw New NotImplementedException()
            End Sub

            Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
                Throw New NotImplementedException()
            End Function

            Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

        End Class

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCmd.cs" data-enlighter-group="erstes-command-1">using System.Windows.Input;
using System;
using Utils;

namespace ViewModels;

public class MainViewModel : PropertyChangedBase
{

    // restlicher MainViewModel-Code

    public LoginCmd LoginCommand { get; set; }

    public MainViewModel()
    {
        LoginCommand = new LoginCmd();
    }

    public class LoginCmd : ICommand
    {

        public void Execute(object? parameter)
        {
            throw new NotImplementedException();
        }

        public bool CanExecute(object? parameter)
        {
            throw new NotImplementedException();
        }

        public event EventHandler? CanExecuteChanged;

    }
}</pre>



<h3 class="wp-block-heading">Was soll bei Ausführung des WPF Commands passieren?</h3>



<p>Um dem Command sagen zu können: &#8222;Hey, wenn Du ausgeführt wirst, soll xy passieren&#8220;, müssen wir nun eine bestimmte Methode implementieren. Dabei handelt es sich um die &#8222;Execute&#8220;-Methode, der &#8222;ICommand&#8220;-Schnittstelle. Dort drin können wir beispielsweise einmal einen Anmeldevorgang simulieren, mehr wird denke ich zu kompliziert sein, also etwa mit Services zu kommunizieren, usw. Das werde ich aber mit Sicherheit noch in einem anderen Beitrag vertiefen!</p>



<p>Die &#8222;Execute&#8220;-Methode sieht dann grundsätzlich erstmal wie folgt aus:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCommand.vb" data-enlighter-group="login-command-klasse">Public Class LoginCommand
    Implements ICommand

    Public Sub Execute(parameter As Object) Implements ICommand.Execute
        ' hier kommt rein, was beim Klick passieren soll
        Debug.WriteLine("Melde mich an")
    End Sub

    ' restlicher Code..

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCommand.cs" data-enlighter-group="login-command-klasse">public class LoginCommand : ICommand
{

    public void Execute(object? parameter)
    {
        // hier kommt rein, was beim Klick passieren soll
        Debug.WriteLine("Melde mich an");
    }

    // restlicher Code..

}</pre>



<h3 class="wp-block-heading">Kann das WPF Command überhaupt ausgeführt werden?</h3>



<p>Nachdem wir die eine &#8222;Execute&#8220;-Methode implementiert und somit beschrieben haben, was beim Ausführen des Commands passieren soll, geht&#8217;s nun mit dem &#8222;kann ich das ausführen&#8220; weiter. Es wäre ja z. B. blöd, wenn der Benutzer der Software den &#8222;Anmelden&#8220;-Knopf spammen könnte. Stattdessen sollte man den Button deaktivieren, während z. B. eine Art Sperre stattfindet. Dazu müssen wir eine Funktion namens &#8222;CanExecute&#8220; implementieren, Welche – wie der Name schon sagt – bestimmt, ob das jeweilige Command gerade ausgeführt werden kann.</p>



<p>Der Code dazu könnte wie folgt aussehen, also, dass z. B. das Command nach 3 fehlerhaften Login-Versuchen gesperrt ist.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCommand.vb" data-enlighter-group="gesperrtes-command">Public Class LoginCommand
    Implements ICommand

    Private _failedAttempts As Integer

    Public Sub Execute(parameter As Object) Implements ICommand.Execute
        ' simulate failed login attempt
        _failedAttempts += 1
    End Sub

    Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
        Return _failedAttempts &lt; 3
    End Function

    Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCommand.cs" data-enlighter-group="gesperrtes-command">using System;
using System.Windows.Input;

public class LoginCommand : ICommand
{
    private int _failedAttempts;

    public void Execute(object? parameter)
    {
        // simulate failed login attempt
        _failedAttempts++;
    }

    public bool CanExecute(object? parameter)
    {
        return _failedAttempts &lt; 3;
    }

    public event EventHandler? CanExecuteChanged;

}</pre>



<p>Zum Schluss bleibt hier nun noch ein letztes Problem: Das Command wird so gesehen niemals &#8222;aktualisiert&#8220;. Es wird aktuell leider nicht nach außen bekanntgemacht: &#8222;Hey, grafische Oberfläche, evaluiere bitte einmal neu, ob ich ausgeführt werden kann, denn hier hat sich was geändert!&#8220;. Somit bleibt das Command aktuell immer ausführbar. Wenn Du stattdessen z. B. initial ein Kriterium hättest – wie z. B. eine Rolle an Nutzern– die den Button nicht drücken dürfen, dann könnte es auch sein, dass der Button permanent grau hinterlegt ist. Keine Sorge, darum kümmern wir uns nun im letzten Schritt!</p>



<h3 class="wp-block-heading">Neu evaluieren, ob ausgeführt werden kann</h3>



<p>Um der grafischen Oberfläche genau dieses Signal a la &#8222;Hey, re-evaluiere hier bitte einmal die CanExecute-Funktion&#8220; zu geben, müssen wir nur eins tun: Das Ereignis auslösen, Welches bewusst von der &#8222;ICommand&#8220;-Schnittstelle dafür vorgegeben wurde, namens &#8222;CanExecuteChanged&#8220;. Aktuell ist dies noch relativ einfach, da wir alles was wir dafür benötigen, innerhalb der Command-Instanz selbst haben.</p>



<p>Was ich meine, ist die Anzahl an fehlgeschlagenen Anmelde-Versuchen, Welche wir in der Zeile 3 deklariert haben. Wir müssten also eigentlich immer das &#8222;CanExecuteChanged&#8220;-Ereignis auslösen, wenn sich diese Variable ändert. Gleich kommen wir auch noch dazu, wie wir unter Umständen agieren könnten, wenn wir z. B. auf eine &#8222;IsLoading&#8220;-Eigenschaft eines ViewModels keinen Zugriff haben.</p>



<p>Das könnte z. B. so aussehen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCommand.vb" data-enlighter-group="can-execute-changed-1">Public Class LoginCommand
    Implements ICommand

    Private _failedAttempts As Integer

    Public Sub Execute(parameter As Object) Implements ICommand.Execute
        ' simulate failed login attempt
        _failedAttempts += 1
        RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
    End Sub

    Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
        Return _failedAttempts &lt; 3
    End Function

    Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="LoginCommand.cs" data-enlighter-group="can-execute-changed-1">using System.Windows.Input;
using System;

public class LoginCommand : ICommand
{
    private int _failedAttempts;

    public void Execute(object? parameter)
    {
        // simulate failed login attempt
        _failedAttempts++;
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

    public bool CanExecute(object? parameter)
    {
        return _failedAttempts &lt; 3;
    }

    public event EventHandler? CanExecuteChanged;

}</pre>



<h3 class="wp-block-heading">Der ganze Aufwand für ein einziges Command?</h3>



<p>Ich gebe zu, dass dies alles, vor allem als Anfänger natürlich erstmal überwältigend klingen kann, besonders für bisher ein einziges Command, aber keine Sorge – &#8222;I got you&#8220;. Statt diesen Aufwand immer wieder von vorn zu betreiben, werden wir uns – als &#8222;faule&#8220; Entwickler – selbstverständlich einige Hilfsmittel schreiben, Welche uns die Arbeit vereinfachen. Im ersten Schritt wird das die gleich folgende, wiederverwendbare Klasse Namens z. B. &#8222;DelegateCommand&#8220; sein.</p>



<h2 class="wp-block-heading" id="wiederverwendbare-klasse">Eine wiederverwendbare DelegateCommand-Klasse</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Das-DelegateCommand-–-unsere-Basisklasse-fuer-WPF-Commands-640px.png"><img loading="lazy" decoding="async" width="640" height="424" src="https://robbelroot.de/wp-content/uploads/2023/01/Das-DelegateCommand-–-unsere-Basisklasse-fuer-WPF-Commands-640px.png" alt="Das DelegateCommand – unsere Basisklasse für WPF Commands" class="wp-image-13853" title="Das DelegateCommand – unsere Basisklasse für WPF Commands"/></a><figcaption class="wp-element-caption">Das DelegateCommand – unsere Basisklasse für WPF Commands</figcaption></figure>



<p>Wie Du vermutlich im letzten Schritt festgestellt hast, ist das Ganze mit den WPF Commands nicht so &#8222;mal eben&#8220; wie mit den Click-Handlern erledigt. Lasse Dich aber bitte hier dennoch nicht dazu verführen – typisch Mensch, typisch Gewohnheitstier – dennoch wieder die alten &#8222;Button1_Click&#8220; und Co. Dinger zu verwenden. Wir können uns wie hier drüber erwähnt, auch viele Hilfsmittel erschaffen, mit Denen wir uns das Handling deutlich vereinfachen können.</p>



<p>Im Endeffekt ändert sich von der Konzeption des Commands selbst, nicht viel, wir brauchen immer noch folgende Aspekte:</p>



<ol class="wp-block-list">
<li>Was soll bei Ausführung getan werden?</li>



<li>Kann es ausgeführt werden?</li>



<li>Wann ändert sich die Info, ob&#8217;s ausgeführt werden kann?</li>
</ol>



<p>Allerdings möchten wir nun nicht jedes Mal dafür eine eigene Klasse erstellen, sondern alles ein wenig mehr zentralisieren. Das können wir ganz einfach, indem wir Obiges logisch betrachten: Punkt 1 ist einfach nur ein &#8222;Packen&#8220; Arbeit, Welchen wir in die Zukunft delegieren (vielleicht rückt der Begriff &#8222;DelegateCommand&#8220; nun etwas näher &#x1f609;), nämlich bei z. B. einem Klick. Punkt 2 ist praktisch das Gleiche in bunt, nur, dass wir eine &#8222;Ja/Nein&#8220;-Info zurückbekommen: Also einen Boolean. Punkt 3 kann je nach Fall komplizierter werden, aber auch das lässt sich regeln.</p>



<h3 class="wp-block-heading">Methoden / Funktionalität dynamisch übergeben</h3>



<p>Wenn man nun zu den obigen Erkenntnissen gekommen ist, fragt man sich im nächsten Schritt das Offensichtliche: &#8222;Okay, aber wie verlagere / gruppiere ich denn überhaupt Arbeit?&#8220;. Das Stichwort, bzw. die Stichworte Deiner Begierde nennen sich &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.action-1?view=net-7.0" target="_blank" rel="noreferrer noopener">Action</a></strong>&#8222;, bzw. &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.func-2?view=net-7.0" target="_blank" rel="noreferrer noopener">Func</a></strong>&#8222;. Diese übernehmen exakt das, was wir bruachen, Sie kapseln / gruppieren unsere Anweisungen und machen Diese praktisch ansprech- und ausführbar – auch später.</p>



<h3 class="wp-block-heading">Der erste Versuch – delegierte Arbeit</h3>



<p>Schreiben wir also nun unseren aus den Erkenntnissen resultierenden, ersten Versuch. Hierbei geht es erstmal nur darum, die &#8222;Was soll getan werden&#8220;-Anweisungen als Gruppe von Anweisungen zu verlagern / zu delegieren. Das geht auch im Endeffekt ganz einfach, schaue Dir dazu diese neue Variante unserer Command-Implementierung an.</p>



<p>Wir lassen hierbei der instaziierenden Partei die Möglichkeit, den gruppierten Stapel an Arbeit von außen mitzugeben. Beachte auch, dass wir sogar die Möglichkeit haben, einen Parameter mitzuliefern. Dadurch sind wir – indem wir z. B. eine weitere Klasse erstellen – auch nicht auf einen Übergabeparameter limitiert, könnten also praktisch eine ganze &#8222;Config&#8220; mitliefern. Natürlich muss dies passend in der Execute-Methode gecastet werden..</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DelegateCommand.vb" data-enlighter-group="delegate-command-mit-aktion">Namespace Utils

    Public Class DelegateCommand
        Implements ICommand

        Private _action As Action(Of Object)

        Sub New(action As Action(Of Object))
            _action = action
        End Sub

        Public Sub Execute(parameter As Object) Implements ICommand.Execute
            ' führt unsere von außen mitgegebene Aktion aus und übergibt auch den Parameter
            _action(parameter)
        End Sub

        Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
            Return True
        End Function

        Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DelegateCommand.cs" data-enlighter-group="delegate-command-mit-aktion">using System.Windows.Input;
using System;

namespace Utils;

public class DelegateCommand : ICommand
{
    private Action&lt;object?> _action;

    public DelegateCommand(Action&lt;object?> action)
    {
        _action = action;
    }

    public void Execute(object? parameter)
    {
        // führt unsere von außen mitgegebene Aktion aus und übergibt auch den Parameter
        _action?.Invoke(parameter);
    }

    public bool CanExecute(object? parameter)
    {
        return true;
    }

    public event EventHandler? CanExecuteChanged;

}</pre>



<h3 class="wp-block-heading">Delegierte Arbeit im MainViewModel verwenden</h3>



<p>Zurück in unserem (komprimierten) MainViewModel, könnte die Erstellung des Commands nun wie gleich folgend und vollkommen dynamisch aussehen. Beachte auch, dass wir durch Polymorphie oben ein &#8222;ICommand&#8220; deklarieren, aber ein diese Schnittstelle implementierendes Objekt zuweisen können (im Konstruktor).</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="mainvm-beispiel-mit-action-command">Imports WpfCommandsVbTutorial.Utils

Namespace ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        ' restliche Eigenschaften..

        Public Property LoginCommand As ICommand

        Sub New()
            LoginCommand = New DelegateCommand(AddressOf Login)
        End Sub

        ' unsere gruppierten Anweisungen - zusammengefasst
        Private Sub Login(parameter As Object)
            ' tu dies
            ' tu jenes
            ' tu das
        End Sub

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="mainvm-beispiel-mit-action-command">using System.Windows.Input;
using Utils;

namespace ViewModels;

public class MainViewModel : PropertyChangedBase
{

    // restliche Eigenschaften..

    public ICommand LoginCommand { get; set; }

    public MainViewModel()
    {
        LoginCommand = new DelegateCommand(Login);
    }

    // unsere gruppierten Anweisungen - zusammengefasst
    private void Login(object? parameter)
    {
        // tu dies
        // tu jenes
        // tu das
    }
}</pre>



<h3 class="wp-block-heading">Was fehlt ist, ob&#8217;s geht!</h3>



<p>Was nun noch als vorletzter Punkt fehlt, ist eine dynamische Feststellung, ob das Command überhaupt ausgeführt werden kann, denn das haben wir aktuell völlig vernachlässigt. Zum Glück können wir uns auch hier ganz schnell helfen, denn dafür gab es neben der &#8222;Action&#8220; auch etwas namens &#8222;Func&#8220;. Damit können wir eine Funktion dynamisch übergeben und dessen Rückgabewert nach unserem Belieben auswerten.</p>



<p>Hierzu habe ich einen neuen Konstruktor erstellt, Welcher uns dann die optionale Möglichkeit bietet, sagen zu können: &#8222;Hey, so stellst Du fest, ob das Command ausgeführt werden kann&#8220;. Wenn dies also nicht mitgegeben wird, könnte man praktisch davon ausgehen, dass das Command immer ausgeführt werden kann.</p>



<h3 class="wp-block-heading" id="finale-delcommand-basisklasse">Die finale DelegateCommand-Basisklasse</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DelegateCommand.vb" data-enlighter-group="delegate-command-mit-aktion2">Namespace Utils

    Public Class DelegateCommand
        Implements ICommand

        Private _action As Action(Of Object)

        Private _canExecute As Func(Of Object, Boolean)

        Sub New(action As Action(Of Object))
            _action = action
        End Sub

        Sub New(action As Action(Of Object), canExecute As Func(Of Object, Boolean))
            _action = action
            _canExecute = canExecute
        End Sub

        Public Sub Execute(parameter As Object) Implements ICommand.Execute
            ' führt unsere von außen mitgegebene Aktion aus und übergibt auch den Parameter
            _action(parameter)
        End Sub

        Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
            If _canExecute Is Nothing Then
                Return True
            End If
            Return _canExecute(parameter)
        End Function

        Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DelegateCommand.cs" data-enlighter-group="delegate-command-mit-aktion2">using System.Windows.Input;
using System;

namespace Utils
{
    public class DelegateCommand : ICommand
    {
        private Action&lt;object?> _action;

        private Func&lt;object?, bool>? _canExecute;

        public DelegateCommand(Action&lt;object?> action)
        {
            _action = action;
        }

        public DelegateCommand(Action&lt;object?> action, Func&lt;object?, bool> canExecute)
        {
            _action = action;
            _canExecute = canExecute;
        }

        public void Execute(object? parameter)
        {
            _action(parameter);
        }

        public bool CanExecute(object? parameter)
        {
            if (_canExecute == null)
                return true;
            return _canExecute(parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }

        public event EventHandler? CanExecuteChanged;
    }
}</pre>



<p>Nun kann unser MainViewModel wie folgt ergänzt werden:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="mainvm-beispiel-mit-func-command">Imports WpfCommandsVbTutorial.Utils

Namespace ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        ' other properties..

        Private Property LoginAttempts As Integer

        Public Property LoginCommand As ICommand

        Sub New()
            LoginCommand = New DelegateCommand(AddressOf Login, AddressOf CanLogin)
        End Sub

        ' our grouped instructions - summed up
        Private Sub Login(parameter As Object)
            ' do this
            ' do that
            ' do something else
            LoginAttempts += 1
        End Sub

        Private Function CanLogin(parameter As Object) As Boolean
            ' determine, if the login process should be executed and return it
            Return LoginAttempts &lt; 3
        End Function

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="mainvm-beispiel-mit-func-command">using System.Windows.Input;
using Utils;

namespace ViewModels;

public class MainViewModel : PropertyChangedBase
{

    // restliche Eigenschaften..

    private int LoginAttempts { get; set; }

    public ICommand LoginCommand { get; set; }

    public MainViewModel()
    {
        LoginCommand = new DelegateCommand(Login, CanLogin);
    }
    
    // our grouped instructions - summed up
    private void Login(object? parameter)
    {
        // do this
        // do that
        // do something else
        LoginAttempts++;
    }

    private bool CanLogin(object? parameter)
    {
        // determine, if the login process should be executed and return it
        return LoginAttempts &lt; 3;
    }
}</pre>



<h3 class="wp-block-heading">Und was, wenn es sich ändert?</h3>



<p>Nun kommen wir (sorry, endlich) zum finalen Punkt: Wir müssen auch hier die Möglichkeit bieten, sagen zu können: &#8222;Hey Oberfläche, bitte neu evaluieren, ob das Command ausgeführt werden kann&#8220;. Dafür gibt es z. B. die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.windows.input.commandmanager.invalidaterequerysuggested?view=windowsdesktop-7.0" target="_blank" rel="noreferrer noopener">&#8222;CommandManager.InvalidateRequerySuggested&#8220;-Methode</a></strong>, Welche ich aber nicht ganz toll finde. Alle Commands re-evaluieren, nur weil sich ggf. Eines geändert hat? Nee..</p>



<p>Wir werden stattdessen unser eigenes kleines Helferlein dafür einbauen, ich meine, dafür haben wir ja sowieso schon unsere Basisklasse. Wir ergänzen also eine Methode, Welche es uns von außen erlaubt, das &#8222;CanExecuteChanged&#8220;-Ereignis des Commands auszulösen. Für mich fühlt sich auch das nicht ganz super an, aber es deckt genau unsere Bedürfnisse:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DelegateCommand.vb" data-enlighter-group="finales-delegate-command">Namespace Utils

    Public Class DelegateCommand
        Implements ICommand

        Private _action As Action(Of Object)

        Private _canExecute As Func(Of Object, Boolean)

        Sub New(action As Action(Of Object))
            _action = action
        End Sub

        Sub New(action As Action(Of Object), canExecute As Func(Of Object, Boolean))
            _action = action
            _canExecute = canExecute
        End Sub

        Public Sub Execute(parameter As Object) Implements ICommand.Execute
            _action(parameter)
        End Sub

        Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
            If _canExecute Is Nothing Then
                Return True
            End If
            Return _canExecute(parameter)
        End Function

        Public Sub RaiseCanExecuteChanged()
            RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
        End Sub

        Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DelegateCommand.cs" data-enlighter-group="finales-delegate-command">using System.Windows.Input;
using System;

namespace Utils;

public class DelegateCommand : ICommand
{
    private Action&lt;object?> _action;

    private Func&lt;object?, bool>? _canExecute;

    public DelegateCommand(Action&lt;object?> action)
    {
        _action = action;
    }

    public DelegateCommand(Action&lt;object?> action, Func&lt;object?, bool>? canExecute)
    {
        _action = action;
        _canExecute = canExecute;
    }

    public void Execute(object? parameter)
    {
        _action(parameter);
    }

    public bool CanExecute(object? parameter)
    {
        if (_canExecute == null)
            return true;
        return _canExecute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

    public event EventHandler? CanExecuteChanged;
}</pre>



<p>Zum Schluss nun unsere letzte MainViewModel-Anpassung (bezogen auf die fehlgeschlagene Logins Geschichte):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="final-main-vm">Imports WpfCommandsVbTutorial.Utils

Namespace ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        ' restliche Eigenschaften

        Public Property LoginCommand As CommandBase

        Private _loginAttempts As Integer

        Private Property LoginAttempts As Integer
            Get
                Return _loginAttempts
            End Get
            Set(value As Integer)
                _loginAttempts = value
                LoginCommand.RaiseCanExecuteChanged()
            End Set
        End Property

        Sub New()
            LoginCommand = New CommandBase(AddressOf Login, AddressOf CanLogin)
        End Sub

        Private Sub Login(parameter As Object)
            ' fehlgeschlagenen Login simulieren..
            LoginAttempts += 1
        End Sub

        Private Function CanLogin(parameter As Object) As Boolean
            Return _loginAttempts &lt; 3
        End Function

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="final-main-vm">using Utils;

namespace ViewModels;

public class MainViewModel : PropertyChangedBase
{

    // restliche Eigenschaften

    public DelegateCommand LoginCommand { get; set; }

    private int _loginAttempts;

    private int LoginAttempts
    {
        get => _loginAttempts;
        set
        {
            _loginAttempts = value;
            LoginCommand.RaiseCanExecuteChanged();
        }
    }

    public MainViewModel()
    {
        LoginCommand = new DelegateCommand(Login, CanLogin);
    }

    private void Login(object? parameter)
    {
        // simulate failed login
        LoginAttempts++;
    }

    private bool CanLogin(object? parameter)
    {
        return _loginAttempts &lt; 3;
    }
}</pre>



<h2 class="wp-block-heading">Wie übergibt man Command-Parameter?</h2>



<figure class="wp-block-image size-full is-resized"><a href="https://robbelroot.de/wp-content/uploads/2023/01/WPF-Command-Parameter-uebergeben_640px.png"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2023/01/WPF-Command-Parameter-uebergeben_640px.png" alt="WPF Command-Parameter übergeben" class="wp-image-14032" style="width:640px;height:360px" title="WPF Command-Parameter übergeben"/></a><figcaption class="wp-element-caption">WPF Command-Parameter übergeben</figcaption></figure>



<p>Bisher konnten wir schon viele Eindrücke in das WPF Command gewinnen, eine Sache ist jedoch noch ein wenig auf der Strecke geblieben, sorry. Es geht um das Übergeben von Parametern, da wir ja auch z. B. extra eine &#8222;Action&#8220; / &#8222;Func&#8220; mit generischem Typenparameter vom Typ &#8222;Object&#8220; deklariert haben. Bisher verwenden wir diese Möglichkeit der Parameterübergabe noch nicht.</p>



<p class="info-banner"><strong>&#x1f4a1;</strong> Hinweis: Viele Online-Code-Schnipsel verwenden die Übergabe des Parameters auch als Möglichkeit, die PasswordBox in das Command zu übergeben und so das Passwort bei Bedarf abzurufen. Es ist laut Diskussion im Netz zwar in erster Linie &#8222;sicherer&#8220; und sicherlich eine mögliche Herangehensweise, allerdings finde ich dies wenig praktikabel.</p>



<p>Jeder (und ich meine jeder) Entwickler muss hier selbst entscheiden, Welche Sicherheitsmaßnahmen für sich, Kunden und Co. angemessen sind! Dies soll die &#8222;Considerations&#8220; selbstverständlich trotzdem nicht kleinreden und man sollte sich an &#8222;best practices&#8220; halten..</p>



<h3 class="wp-block-heading">Warum, bzw. wann übergibt man Command-Parameter?</h3>



<p>Bevor man sich allerdings tiefer in diesen Dschungel wagt, macht diese kleine Überlegung ggf. auch Sinn: &#8222;Wann braucht man Command-Parameter überhaupt!?&#8220;. Grundsätzlich können wir ja auf die Eigenschaften unseres Viewmodels zugreifen, wenn wir lokale Methoden des ViewModels mit der DelegateCommand-Klasse von oben verwenden. Das stimmt soweit auch, aber wie wir schon im Absatz hier drüber gesehen haben, kann es Situationen geben, wo wir auf die Dinge ggf. keinen Zugriff haben, dann könnte so eine Übergabe helfen.</p>



<h3 class="wp-block-heading">Parameter manuell via XAML übergeben</h3>



<p>Lasse uns daher einmal im nächsten Schritt schauen, wie wir diese Möglichkeit der Parameter nun allgemein verwenden könnten. Ich denke hier zuerst an die einfachste Variante, also eine einfache &#8222;manuelle&#8220; Übergabe. Dies können wir sehr einfach im XAML-Code erledigen, indem wir einfach einen passenden Wert, an die &#8222;CommandParameter&#8220;-Eigenschaft mitliefern:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;Button Command="{Binding LoginCommand}" CommandParameter="D" /></pre>



<h3 class="wp-block-heading">WPF Command-Parameter via DataBinding übergeben</h3>



<p>Auch ganz interessant ist die eigentlich offensichtlichste Sache, also die Übergabe via normalem Databinding. Hierzu kann ich einfach eine normale &#8222;Binding-Expression&#8220;, also einen Bindungs-Ausdruck schreiben. Dieser wird dann zu passender Zeit evaluiert und als CommandParameter übergeben:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;Button Command="{Binding LoginCommand}" CommandParameter="{Binding SomeBoundPropertyToPassIn}" /></pre>



<h3 class="wp-block-heading">Mehrere Parameter via Klasse oder MultiBinding übergeben</h3>



<p>Hier kommt noch ein wichtiger Punkt, da viele Anfänger daran scheitern. Häufig stellt man folgende Überlegung an: &#8222;Okay, so übergebe ich einen einzigen Parameter an das WPF Command, aber wie kann ich Mehrere auf einmal übergeben?&#8220;.</p>



<p>Aber keine Sorge, die Antwort ist relativ einfach, die erste Variante wäre via eigener Klasse. Lege dazu einfach eine passende Klasse mit den Eigenschaften / Werten, die Du übertragen möchtest, an:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="parameter-params-class">Public Class SpecialCmdParams

    Public Property SomePropToPassOne As String

    Public Property SomePropToPassTwo As Integer

    ' ...

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="parameter-params-class">public class SpecialCmdParams
{

    public string SomePropToPassOne { get; set; }

    public int SomePropToPassTwo { get; set; }

    // ...

}</pre>



<p>Instanziiere diese Klasse nun an passender Stelle, z. B. im ViewModel-Konstruktor und befülle die Eigenschaften durch passende Bindungen an z. B. Textboxen, whatever. Im nächsten Schritt kannst Du die Instanz der Klasse natürlich einfach via Datebindung (wie hier drüber, siehe &#8222;SomeBoundPropertyToPassIn&#8220;) übergeben. Gleich schauen wir uns noch an, wie man dies dann abrufen kann.</p>



<h3 class="wp-block-heading">Parameter via Multibinding übergeben</h3>



<p>Wer keine Lust hat jedes Mal eine passende Klasse zu erstellen, kann sich die Übergabe mehrerer Command-Parameter auch durch ein sogenanntes &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.windows.data.multibinding?view=windowsdesktop-7.0" target="_blank" rel="noreferrer noopener">Multibinding</a></strong>&#8220; vereinfachen. Wie der Name schon sagt, bindet man hier nicht an ein &#8222;Ding&#8220;, sondern kann gleich mehrere Datenbindungen quasi in Einer verwenden.</p>



<p>Das Blöde ist hier nur, dass man einen &#8222;Converter&#8220; benötigt, wie soll sonst aus &#8222;mehrere Dinger&#8220; -&gt; &#8222;ein Ding&#8220; werden? Man muss hier auch noch bedenken, dass man eventuell den Weg zurück braucht, also aus einem Ding wieder Mehrere zu machen. Ein derartiges, passendes Muster finden wir bei der <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.windows.data.imultivalueconverter?view=windowsdesktop-7.0" target="_blank" rel="noreferrer noopener">&#8222;IMultiValueConverter&#8220;-Schnittstelle</a></strong>.</p>



<p>Implementieren wir dieses Interface also einmal beispielhaft, um mehrere Zahlen zu übergeben (auch wenn dies bei einem Login keinen Sinn macht), es geht wie gesagt um &#8222;Commands&#8220; selbst :)! Wir übergeben hier einfach mal stumpf die Größe des Login-Fensters. Dies könnten wir natürlich auch durch eine normale Datenbindung realisieren, aber es geht wie gesagt um ein Beispiel!</p>



<p>Beachte, dass die Implementierung hier nur beispielhaft und z. B. nicht konfigurierbar ist. Zum Thema &#8222;ValueConverter&#8220; im Allgemeinen werde ich ggf. noch einen separaten Beitrag schreiben. Erstelle also nun einmal folgende Klasse, im (z. B.) &#8222;Utils&#8220;-Ordner</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DoubleNumberConverter.vb" data-enlighter-group="double-number-converter">Imports System.Globalization

Namespace Utils

    Public Class DoubleNumberConverter
        Implements IMultiValueConverter

        Public Function Convert(values() As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IMultiValueConverter.Convert
            Return String.Join("|", values)
        End Function

        Public Function ConvertBack(value As Object, targetTypes() As Type, parameter As Object, culture As CultureInfo) As Object() Implements IMultiValueConverter.ConvertBack
            Return value.ToString.Split("|")
        End Function

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="DoubleNumberConverter.cs" data-enlighter-group="double-number-converter">using System.Globalization;

namespace Utils
{
    public class DoubleNumberConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return string.Join("|", values);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return value.ToString().Split("|");
        }
    }
}</pre>



<p>Damit können wir dann ein Multibinding realisieren, Welches dann über übliche Binding-Ausdrücke Daten mitliefert – aber eben Mehrere! Vergiss hier jedoch nicht, eine Instanz des &#8222;DoubleNumberConverters&#8220; entweder in der App.xaml / Application.xaml, oder in Deinem jeweiligen Control (UserControl, Window, etc..) zu erstellen. Ansonsten kannst Du den Konverter nicht verwenden:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;!-- The Window here is named "MyWindow" for example purposes -->

    &lt;!-- further above -->
    &lt;Window.Resources>
        &lt;ResourceDictionary>
            &lt;u:DoubleNumberConverter x:Key="MyDoubleNumberConverter" />
        &lt;/ResourceDictionary>
    &lt;/Window.Resources>

&lt;!-- usage -->
&lt;Button Command="{Binding LoginCommand">
    &lt;Button.CommandParameter>
        &lt;MultiBinding Converter="{StaticResource MyDoubleNumberConverter}">
             &lt;Binding Path="ActualWidth" ElementName="MyWindow"/>
             &lt;Binding Path="ActualHeight" ElementName="MyWindow"/>
        &lt;/MultiBinding>
    &lt;/Button.CommandParameter>
&lt;/Button></pre>



<h3 class="wp-block-heading">Übergebene Parameter abgreifen</h3>



<p>Nachdem wir die Beispiele zum Übergeben der Parameter gesehen haben, schauen wir uns nun an, wie wir Diese auch wieder abgreifen können. Je nachdem Welches der obigen Beispiele Du verwendest, sieht die Vorgehensweise eigentlich immer gleich aus (auch bei CanExecute):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="getting-params">' rest of the ViewModel-Class (like MainViewModel)

' the "parameter" contains everything, but combined in one thing
Public Sub Login(parameter As Object)
    ' if it looked like this in the XAML:
    ' CommandParameter="D"
    Dim passedLetter = Convert.ToString(parameter)
    ' now there's the letter D inside passedLetter

    '' if it was a custom class
    Dim yourParam = CType(parameter, SpecialCmdParams)

    '' if it was the DoubleNumberConverter - you now have :
    Dim numbersAsStrings = Convert.ToString(parameter).Split("|")
    ' do conversion, whatever
End Sub

' rest of the ViewModel-Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="getting-params">public void Login(object parameter)
{
    // if it looked like this in the XAML:
    // CommandParameter="D"
    var passedLetter = Convert.ToString(parameter);
    // now there's the letter D inside passedLetter

    // // if it was a custom class
    var yourParam = (SpecialCmdParams)parameter;

    // // if it was the DoubleNumberConverter - you now have :
    var numbersAsStrings = Convert.ToString(parameter).Split("|");
}</pre>



<h2 class="wp-block-heading" id="downloads">Downloads</h2>



<p>Lade Dir hier gerne das Beispielprojekt (oder einzelne Bestandteile) in der Sprache Deiner Wahl herunter, da der Beitrag natürlich anhand des Themas ein wenig riesig ist. So kannst Du Dich spielerisch an die einzelnen Punkte wagen und begleitend mit dem Beitrag arbeiten. Die einzelnen Dinge beinhalten jeweils die Dateien für beide Sprachen.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=5867f9d0-4c87-4691-b5af-edcfc9cf21e5" target="_blank" rel="noreferrer noopener">WpfCommandsExampleVbTutorial.zip</a></div>



<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=b7a9ff1d-36a0-4517-b7c9-7d3b08395f3a" target="_blank" rel="noreferrer noopener">WpfCommandsExampleCsTutorial.zip</a></div>



<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=6f4780e5-cac9-4a61-a0ef-336fdf663bfa" target="_blank" rel="noreferrer noopener">DelegateCommand.zip</a></div>



<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=9c9ac493-82cc-45f2-a2cd-34a1dbe8e78b" target="_blank" rel="noreferrer noopener">PropertyChangedBase.zip</a></div>
</div>
<p>Der Beitrag <a href="https://robbelroot.de/blog/eigene-wpf-commands-in-mvvm-vbnet-und-csharp-der-guide/">Eigene WPF Commands in MVVM VB.NET &#038; C# &#8211; der 2024 Guide</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/eigene-wpf-commands-in-mvvm-vbnet-und-csharp-der-guide/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ein WPF MVVM Projekt aufsetzen (VB.NET &#038; C#) – 2026 Guide</title>
		<link>https://robbelroot.de/blog/ein-wpf-mvvm-projekt-aufsetzen-guide/</link>
					<comments>https://robbelroot.de/blog/ein-wpf-mvvm-projekt-aufsetzen-guide/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Wed, 04 Jan 2023 20:57:49 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[aufbau]]></category>
		<category><![CDATA[aufsetzen]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[entwurfsmuster]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[konfiguration]]></category>
		<category><![CDATA[model]]></category>
		<category><![CDATA[mvvm]]></category>
		<category><![CDATA[pattern]]></category>
		<category><![CDATA[projekt]]></category>
		<category><![CDATA[projektvorlage]]></category>
		<category><![CDATA[setup]]></category>
		<category><![CDATA[starten]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vb.net]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[view]]></category>
		<category><![CDATA[viewmodel]]></category>
		<category><![CDATA[visual basic]]></category>
		<category><![CDATA[vorlage]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=13539</guid>

					<description><![CDATA[<p>Moderne Software durch WPF MVVM Projekte erstellen In diesem Guide zeige ich, wie du ein WPF MVVM Projekt aufsetzen kannst — Schritt für Schritt, von der Ordnerstruktur bis zur wiederverwendbaren Projektvorlage. Falls du von WinForms umsteigst, lies auch: Windows Forms zu WPF migrieren – Der vollständige Guide Viele mich anschreibende Zuschauer &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/ein-wpf-mvvm-projekt-aufsetzen-guide/">Ein WPF MVVM Projekt aufsetzen (VB.NET &amp; C#) – 2026 Guide</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/WPF-MVVM-Projekt-aufsetzen-beitrags-kopf-bild.png"><img loading="lazy" decoding="async" width="640" height="427" src="https://robbelroot.de/wp-content/uploads/2023/01/WPF-MVVM-Projekt-aufsetzen-beitrags-kopf-bild.png" alt="WPF MVVM Projekt in VB.NET und C# aufsetzen" class="wp-image-13557" title="WPF MVVM Projekt in VB.NET und C# aufsetzen"/></a><figcaption class="wp-element-caption">WPF MVVM Projekt in VB.NET und C# aufsetzen</figcaption></figure>






<h2 class="wp-block-heading">Moderne Software durch WPF MVVM Projekte erstellen</h2>



<p><strong>In diesem Guide zeige ich, wie du ein WPF MVVM Projekt aufsetzen kannst — Schritt für Schritt, von der Ordnerstruktur bis zur wiederverwendbaren Projektvorlage.</strong></p>



<p><em>Falls du von WinForms umsteigst, lies auch: <a href="https://robbelroot.de/blog/windows-forms-zu-wpf-migrieren/" target="_blank" rel="noreferrer noopener"><strong>Windows Forms zu WPF migrieren – Der vollständige Guide</strong></a></em></p>



<p>Viele mich anschreibende Zuschauer und Kunden haben sich gewünscht, dass ich mal einen Projektaufbau nach Model-View-ViewModel-Manier zeige – von Beginn an und ohne anderes Zeug. Also hier kommt er nun, der Beitrag, wie man ein VB.NET, bzw. ein C# WPF Projekt anhand der MVVM-Struktur aufsetzt und durch ein Projekttemplate sogar wiederverwendbar macht.</p>



<p>In erster Linie sind hier die Ordnerstruktur, die Konfiguration des Start-Prozesses und einige kleine / weitere Dinge relevant, aber genug gequasselt – let&#8217;s go &#x1f913;!</p>



<h2 class="wp-block-heading">Kurzfassung – WPF MVVM Projekt aufsetzen</h2>



<p>Grundsätzlich kann man die Schritte wie folgt zusammenfassen, allerdings ist dies natürlich nur extrem kukrz gefasst und Bedarf sicherlich einer genaueren Beschreibung. Diese findest Du selbstverständlich weiter unten, wenn Dir die groben Schritte nicht ausreichen sollten – wovon ich ausgehe, da Diese nur einer Art Leitfaden darstellen.</p>



<p>Die Schritte wären an sich wie folgt:</p>



<ul class="wp-block-list">
<li><strong>Erstelle </strong>die MVVM-typischen <strong>Ordner</strong>: <strong>Views</strong>, <strong>ViewModels</strong>, <strong>Models</strong>, <strong>Utils</strong> (optional)</li>



<li><strong>Verschiebe </strong>das <strong>MainWindow </strong>über den Visual Studio Projektmappenexplorer <strong>in </strong>den &#8222;<strong>Views</strong>&#8222;-Ordner</li>



<li><strong>Passe </strong>den Namen des <strong>MainWindows zu </strong>&#8222;<strong>MainView</strong>&#8220; <strong>an</strong></li>



<li><strong>Sorge </strong>dafür, <strong>dass </strong>das <strong>MainView in </strong>einem passenden <strong>Namespace ist </strong>– Achtung: Code behind Datei, sowie auch die XAML-Datei</li>



<li><strong>Setze</strong> im MainView einen DataContext (entweder im XAML oder Code behind)</li>



<li><strong>Passe </strong>die <strong>StartupUri </strong>in Deiner Application.xaml Datei <strong>an</strong>: &#8222;Views/MainView.xaml&#8220;</li>



<li><strong>Implementiere </strong>die <strong>INotifyPropertyChanged</strong>-Schnittstelle <strong>in </strong>einer <strong>Basisklasse</strong> – für die Vererbung an ViewModel-Klassen (in den Utils-Ordner damit)</li>



<li><strong>Erstelle </strong>eine passende <strong>MainViewModel</strong>&#8211;<strong>Klasse</strong>, Welche von Deiner &#8222;PropertyChanged&#8220;-Implementierung erbt (häufig auch ShellViewModel genannt)</li>
</ul>



<p class="info-banner">&#x1f4a1;&nbsp;<strong>Hinweis</strong>: Nutze gerne <strong><a href="#toc_container">das Inhaltsverzeichnis</a></strong>, wenn Du es eilig hast und einfach drauf los coden möchtest. Ich würde Dir allerdings empfehlen, auch die Details hinter dem Ganzen zu verstehen und ggf. ein wenig mehr Zeit zu investieren.</p>



<style>
.info-banner {
    background: #03a9f4;
    padding: 2em;
    border-radius: 0.5em;
    color: whitesmoke;
}
.info-banner a {
    color: #FED766;
}
</style>



<h2 class="wp-block-heading" id="propertychanged-schnittstelle">INotifyPropertyChanged in WPF — Warum und wie</h2>



<p>Beachte, dass ich nicht genauer auf das &#8222;INotifyPropertyChanged&#8220;-Interface eingehen werden, dies habe ich im Beitrag &#8222;<strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/#PropertyChangedBase-Klasse" target="_blank" rel="noreferrer noopener">Der ultimative INotifyPropertyChanged Guide</a></strong>&#8220; getan. Klicke auf den Link, falls Du die Basisklasse oder auch detaillierte Informationen zum Thema benötigst – der Link schickt Dich direkt zur Basisklasse.</p>



<p>Kurzgesagt ist <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-7.0" target="_blank" rel="noreferrer noopener">diese Schnittstelle</a></strong> eine Art Vertrag (eine Schnittstelle eben..), damit die &#8222;WPF-Engine&#8220; – so nenne ich Sie jetzt einfach mal – weiß: &#8222;Hey, wann und wie werden mir Änderungen kommuniziert?&#8220;. Es ist daher unsere Aufgabe als Entwickler, Sie korrekt zu implementieren und an den notwendigen Stellen das &#8222;PropertyChanged&#8220;-Ereignis auszulösen. Somit weiß die Oberfläche dann: &#8222;Aha, da ist was passiert, also muss ich aktiv werden&#8220;.</p>



<h2 class="wp-block-heading">Mehr über das MVVM-Entwurfsmuster selbst</h2>



<p><em>Das MVVM Pattern in WPF ist der Standard für saubere, testbare Desktop-Anwendungen</em>.</p>



<p>Falls Du eventuell völlig neu im Themenbereich MVVM bist, kann ich Dir hier auch meinen <strong><a href="https://robbelroot.de/blog/mvvm-csharp/" target="_blank" rel="noreferrer noopener">Beitrag über das &#8222;Model-View-ViewModel-Entwurfsmuster&#8220;</a></strong> empfehlen. Lass Dich hier bitte nicht von der &#8222;Spezialisierung&#8220; auf C# verwirren, letztendlich ist hier alles analog und die Erkenntnisse helfen Dir in beiden .NET-Sprachen gleichermaßen.</p>



<h2 class="wp-block-heading">Schritt 1: MVVM-Ordner anlegen</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Projekt-und-Ordnerstruktur-–-VB.NET-WPF-Commands-in-MVVM_640px.png"><img loading="lazy" decoding="async" width="640" height="427" src="https://robbelroot.de/wp-content/uploads/2023/01/Projekt-und-Ordnerstruktur-–-VB.NET-WPF-Commands-in-MVVM_640px.png" alt="Projekt und Ordnerstruktur – WPF MVVM Projekt aufsetzen" class="wp-image-13421" title="Projekt und Ordnerstruktur – WPF MVVM Projekt aufsetzen"/></a><figcaption class="wp-element-caption">Projekt und Ordnerstruktur – WPF MVVM Projekt aufsetzen</figcaption></figure>



<p>Wenn Du noch keine passenden Ordner für die üblichen MVVM-Bestandteile erstellt hast, machen wir dies nun zuerst. Verwende dafür die Werkzeuge aus Visual Studio, damit die Ordner auch von Visual Studio selbst korrekt erfasst werden. Führe dazu einfach z. B. einen Rechtsklick auf Deine Projektmappe (nicht auf die Lösung / Solution) aus und wähle &#8222;Hinzufügen-&gt;Ordner&#8220;.</p>



<p>Erstelle nun innerhalb Deiner Projektmappe unter anderem einen <strong>Ordner </strong>namens &#8222;<strong>ViewModels</strong>&#8222;, hier kommen – wie der Name schon sagt – unsere ViewModels rein. Füge dann bitte noch einen weiteren <strong>Ordner </strong>namens &#8222;<strong>Views</strong>&#8220; hinzu, dort werden wir die grafischen Gegenstücke – also unsere visuellen Bedien-Oberflächen – zu unseren ViewModels ablegen. Dazu könnten übrigens auch kleinere Komponenten, wie z. B. eine Navigationsleiste gehören (ergo NavigationBarView).</p>



<h3 class="wp-block-heading">Die richtige WPF Projektstruktur — Ordner und Namespaces</h3>



<p>Achtung: Wenn Du MVVM-Hilfs-Frameworks wie &#8222;Caliburn Micro&#8220; verwendest, könntest Du allerdings ein wenig an <strong><a href="https://de.wikipedia.org/wiki/Namenskonvention_(Datenverarbeitung)" target="_blank" rel="noreferrer noopener">Namenskonventionen</a></strong> gebunden sein. Hierüber jedoch auch noch zu berichten, würde bekanntlich den Rahmen des Beitrages sprengen, schätze ich. Verlasse Dich daher am Anfang auf die in diesem Beitrag angepeilte Routine.</p>



<p>Im <strong>letzten Schritt</strong> benötigen wir noch einen weiteren kleinen &#8222;<strong>Hilfs-Ordner</strong>&#8222;, dort packen wir nützliche Helferlein rein, Welche uns den Alltag in MVVM versüßen. Nenne den Ordner daher einfach &#8222;<strong>Utils</strong>&#8220; (Utilities/Hilfsmittel), später kommt dort dann die wiederverwendbare &#8222;PropertyChangedBase&#8220;-Klasse hinein. Wenn Du auch den <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/#PropertyChangedBase-Klasse" target="_blank" rel="noreferrer noopener">Beitrag zur &#8222;INotifyPropertyChanged&#8220;-Schnittstelle</a></strong> besucht hast, hast Du hier auch schon die notwendige &#8222;PropertyChangedBase&#8220;-Basisklasse drin. Besuche ansonsten einfach den Link, ziehe Dir den Code (wo Du direkt auskommen solltest) und packe die Klasse in den &#8222;Utils&#8220;-Ordner.</p>



<p>Unsere finale Struktur sollte also nun wie folgt aussehen:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Finale-Ordnerstruktur-im-Projekt-–-VB.NET-WPF-Commands-in-MVVM.png"><img loading="lazy" decoding="async" width="279" height="191" src="https://robbelroot.de/wp-content/uploads/2023/01/Finale-Ordnerstruktur-im-Projekt-–-VB.NET-WPF-Commands-in-MVVM.png" alt="Finale Ordnerstruktur im Projekt – WPF MVVM Projekt aufsetzen" class="wp-image-13428" title="Finale Ordnerstruktur im Projekt – WPF MVVM Projekt aufsetzen"/></a><figcaption class="wp-element-caption">Finale Ordnerstruktur im Projekt – WPF MVVM Projekt aufsetzen</figcaption></figure>



<p class="info-banner">&#x1f4a1;&nbsp;<strong>Hinweis</strong>: Wir werden in diesem Beitrag die &#8222;PropertyChangedBase&#8220;-Basisklasse aus meinem Beitrag zum Thema &#8222;<strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/#PropertyChangedBase-Klasse" target="_blank" rel="noreferrer noopener">Die INotifyPropertyChanged-Schnittstelle – Änderungen kommunizieren</a></strong>&#8220; verwenden. Stelle sicher, dass Du die Klasse also im &#8222;Utils&#8220;-Ordner und dem passenden Namespace ablegst! Klicke einfach auf den Link und Du kommst sofort zur passenden Klasse im Beitrag (VB.NET &amp; C# Code).</p>



<style>
.info-banner {
    background: #03a9f4;
    padding: 2em;
    border-radius: 0.5em;
    color: whitesmoke;
}
.info-banner a {
    color: #FED766;
}
</style>



<h2 class="wp-block-heading">Schritt 2: MainWindow verschieben &amp; umbenennen</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/MainWindow-verschieben-etc-640px..png"><img loading="lazy" decoding="async" width="640" height="406" src="https://robbelroot.de/wp-content/uploads/2023/01/MainWindow-verschieben-etc-640px..png" alt="MainWindow verschieben &amp; umbenennen – WPF MVVM Projekt aufsetzen" class="wp-image-13656" title="MainWindow verschieben &amp; umbenennen – WPF MVVM Projekt aufsetzen"/></a><figcaption class="wp-element-caption">MainWindow verschieben &amp; umbenennen – WPF MVVM Projekt aufsetzen</figcaption></figure>



<p>Im folgenden Schritt müssen wir auch noch daran denken, das &#8222;MainWindow&#8220; MVVM-gemäß umzubenennen und zu verschieben. Dies führt natürlich aufgrund der Standard-Konfiguration des Projektes zu Fehlern. Weder weiß die &#8222;Application.xaml&#8220;-Datei nach dem Umzug, wo unser Hauptfenster ist, noch, dass es nicht mehr im Root-Namespace ist (im Namensraum unseres Projektes).</p>



<h3 class="wp-block-heading">Ab in den richtigen Ordner</h3>



<p>Schiebe das MainWindow nun in den Ordner namens &#8222;Views&#8220;, verwende dazu am besten den Drag &amp; Drop-Vorgang im Visual Studio selbst. Wenn Du dies außerhalb machst, kann es zu Problemen führen, da Visual Studio es ggf. nicht korrekt erkennt. Nun befindet sich unsere grafische Oberfläche (potenziell Eine von Vielen) im richtigen Ordner, allerdings fehlt noch was.</p>



<h3 class="wp-block-heading">Einen MVVM entsprechenden Namen &amp; Namespace</h3>



<p>Benenne im nächsten Schritt das &#8222;MainWindow&#8220; in &#8222;MainView&#8220; um und achte darauf, dass dies auch anschließend korrekt in der ersten Zeile des XAML-Codes widergespiegelt wird. Verwende dazu einfach die F2-Taste, während Du Dein MainWindow im Projektmappen-Explorer ausgewählt (und den Fokus darauf) hast. Passe – wie hier drunter dargestellt – auch gleich den Namespace an, also schreibe das &#8222;Views.&#8220;-Prefix vor die eigentliche Bezeichnung.</p>



<h3 class="wp-block-heading">DataContext in WPF setzen — via XAML oder Code-Behind</h3>



<p>Beachte hier, dass Du diese beiden Zeilen (mit x:Class &amp; xmlns:vm) separat in Dein Projekt kopierst / Sie abänderst, sonst zerstörst Du ggf. Dein bestehendes Projekt. Dort stehen ja auch noch andere Dinge, wie z. B. die Fenstergröße, usw. Diese habe ich hier bewusst ausgelassen! Den ViewModels Namespace kannst Du aktuell außer acht lassen, bzw. als Fehler drin lassen – <strong><a href="#Das-MainViewModel">darum kümmern wir uns später</a></strong>. Durch die später erstellte Klasse wird der Namespace hier verfügbar – ggf. musst Du hierfür einmal das Projekt debuggen/builder, damit &#8222;er&#8220; es kapiert.</p>



<p>Dann setzen wir noch im letzten Schritt den Fenster-Datenkontext via XAML, wir erstellen also eine Instanz der MainViewModel-Klasse via XAML-Code. Dies könntest Du auch – wie gleich angemerkt – via &#8222;Code behind&#8220;-Datei durchführen.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;Window x:Class="Views.MainView"
        xmlns:vm="clr-namespace:WpfCommandsVbTutorial.ViewModels">

    &lt;Window.DataContext>
        &lt;vm:MainViewModel />
    &lt;/Window.DataContext>

    &lt;!-- restlicher XAML-Code -->
&lt;/Window></pre>



<p>Diese Änderungen werden wir nun auch im sogenannten &#8222;Code behind&#8220;-File übernehmen / sicherstellen. Gehe dazu einfach in diese Datei, indem Du die F7-Taste drückst, während Du Dich im XAML-Designer des MainViews (ehemals MainWindows) befindest. Danach springt die Visual Studio IDE via Hotkey in die &#8222;Code behind&#8220;-Datei und Du solltest folgenden Code einfügen &#8211; also das MainView mit einem Namespace umgeben.</p>



<h3 class="wp-block-heading">Anmerkung zum DataContext</h3>



<p>Optional könntest Du die DataContext-Eigenschaft hier festlegen, wir werden dies allerdings im  XAML-Code machen, daher entfällt es hier weg, bzw. ist es hier optional. Es geht also an dieser Stelle hier erstmal um den Namespace und die Klasse selbst, sieh&#8216; Dir aber auch gern an, wie Du auch hier den Datenkontext festlegen könntest.</p>



<p>Mach&#8216; Dir hier übrigens keine Sorgen über das &#8222;MainViewModel&#8220;, dabei handelt es sich um eine Klasse, Welche wir gleich noch erstellen werden!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainView.xaml.vb" data-enlighter-group="mainview-namespace">Namespace Views

    Class MainView
        
        '' entfällt durch View-seitige DataContext-Konfiguration
        'Sub New()
            'InitializeComponent()
            '' Import bei Bedarf nicht vergessen!!
            'DataContext = New MainViewModel()
        'End Sub

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainView.xaml.cs" data-enlighter-group="mainview-namespace">namespace Views
{
    class MainView
    {

        //// entfällt durch View-seitige DataContext-Konfiguration
        //public MainView()
        //{
            //InitializeComponent();
            //// Import bei Bedarf nicht vergessen!!
            //DataContext = new MainViewModel();
        //}

    }
}</pre>



<h2 class="wp-block-heading">Schritt 3: Start-Prozess der Software anpassen (Bootstrapping)</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Start-Prozess-anpassen-640px.png"><img loading="lazy" decoding="async" width="640" height="427" src="https://robbelroot.de/wp-content/uploads/2023/01/Start-Prozess-anpassen-640px.png" alt="Start-Prozess anpassen – WPF MVVM Projekt aufsetzen" class="wp-image-13658" title="Start-Prozess anpassen – WPF MVVM Projekt aufsetzen"/></a><figcaption class="wp-element-caption">Start-Prozess anpassen – WPF MVVM Projekt aufsetzen</figcaption></figure>



<p>Nun müssen wir ein wenig Feintuning betreiben, da das Programm nach unseren Änderungen mehr oder weniger ins Leere laufen würde. Das liegt daran, dass wir das MainWindow erstens umbenannt und zweitens verschoben haben (und denke an die Namespace-Änderung). Nun muss die Zeile mit der &#8222;StartupUri&#8220; in der &#8222;Application.xaml / App.xaml&#8220;-Datei angepasst werden.</p>



<p>Achte bitte darauf, dass ich mein Projekt &#8222;WpfCommandsVbTutorial&#8220; genannt hatte und Du dies durch Deinen eigenen Projektnamen ersetzen musst. Dadurch gibst Du praktisch Bescheid: &#8222;Hey, hier ist nun das Start-Fenster/View, um die Anwendung zu starten&#8220;.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;Application x:Class="Application"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfCommandsVbTutorial"
             StartupUri="Views/MainView.xaml">
    &lt;Application.Resources>
         
    &lt;/Application.Resources>
&lt;/Application></pre>



<h2 class="wp-block-heading">Schritt 4: ViewModel in WPF erstellen — Basisklasse und MainViewModel</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Das-MainViewModel-640px.png"><img loading="lazy" decoding="async" width="640" height="417" src="https://robbelroot.de/wp-content/uploads/2023/01/Das-MainViewModel-640px.png" alt="Ein passendes ViewModel erstellen" class="wp-image-13686" title="Ein passendes ViewModel erstellen"/></a><figcaption class="wp-element-caption">Ein passendes ViewModel erstellen</figcaption></figure>



<p>Im nächsten und letzten Schritt müssen wir für das oben konfigurierte View nun ein passendes ViewModel erstellen. Das View ist ja wie Du vermutlich weißt (oder gelernt hast) das grafische Gegenstück (ohne Geschäftslogik) zum View. Es mag also im wahrsten Sinne des Wortes &#8222;cool&#8220; aussehen, aber letztendlich nichts &#8222;können&#8220;.</p>



<h3 class="wp-block-heading">Eine wiederverwendbare Basisklasse</h3>



<p>Um uns nicht immer wiederholen zu müssen, machen wir hier Gebrauch von der schon mehrfach erwähnten &#8222;PropertyChangedBase&#8220;-Basisklasse aus meinem anderen Beitrag. Gehe daher also bitte hin und erstelle diese Klassen-Datei im oben angelegten &#8222;Utils&#8220;-Ordner. Einen detaillierten Guide findest Du dazu im <strong><a href="#propertychanged-schnittstelle">oben verlinkten Beitrag</a></strong>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="PropertyChangedBase.vb" data-enlighter-group="property-changed-base-klasse">Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Namespace Utils
    Public MustInherit Class PropertyChangedBase
        Implements INotifyPropertyChanged
        Protected Sub NotifyOfPropertyChange(&lt;CallerMemberName> Optional propertyName As String = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    End Class
End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="PropertyChangedBase.cs" data-enlighter-group="property-changed-base-klasse">using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace Utils;
public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    protected void NotifyOfPropertyChange([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}</pre>



<h3 class="wp-block-heading">Das MainViewModel</h3>



<p>Nun erstellen wir unser MainViewModel, Welches die aufbereiteten Elemente für das View beinhaltet – darunter auch passende Logik, wie z. B. für einen &#8222;Button1_Click&#8220;. Warum diese typische Ereignishandler aus Winforms (Windows Forms) Zeiten schon lange nicht mehr modern ist und nicht ins MVVM-Entwurfsmuster passt, erfährst Du in einem passenden, baldigen Beitrag.</p>



<p>Da wir dieses beispielhaft aufgesetzte Projekt hier wie initial erwähnt gleich noch als Projektvorlage anlegen möchten, werde ich hier auch keine großen Eigenschaften realisieren. Das MainViewModel bleibt daher also eher karg und leer. Achte auch hier bitte wieder daran, dass Du oben den korrekten Projektnamen für Deine Anwendung verwendest:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="main-view-model">Imports WpfCommandsVbTutorial.Utils

Namespace ViewModels

    Public Class NewMainViewModel
        Inherits PropertyChangedBase

        Sub New()
            
        End Sub

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="main-view-model">using WpfCommandsVbTutorial.Utils;

namespace ViewModels
{

    public class NewMainViewModel : PropertyChangedBase
    {

        public NewMainViewModel()
        {
            
        }

    }

}</pre>



<h2 class="wp-block-heading">Bonus: WPF MVVM Projektvorlage erstellen</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/NET-MVVM-Projektvorlage-640px.png"><img loading="lazy" decoding="async" width="640" height="430" src="https://robbelroot.de/wp-content/uploads/2023/01/NET-MVVM-Projektvorlage-640px.png" alt="Bonus: NET MVVM Projektvorlage" class="wp-image-13693" title="Bonus: NET MVVM Projektvorlage"/></a><figcaption class="wp-element-caption">Bonus: NET MVVM Projektvorlage</figcaption></figure>



<p>Als kleines – wie sagt man so schön – Schmankerl, erstellen wir uns aus der gemachten Arbeit nun eine Projektvorlage. Wir möchten ja schließlich vermeiden, diesen ganzen Aufwand für jedes einzelne MVVM-Projekt zu betreiben. Im Endeffekt geht dies nach getaner Arbeit sehr einfach und schnell, mit Hilfe der Visual Studio IDE.</p>



<p>Wenn nun alles nach Deinen Vorstellungen ist, gehe oben im Visual Studio Menü auf &#8222;Projekt-&gt;Vorlage exportieren&#8220;. Dann zeigt sich folgender Dialog und Du kannst ein paar weitere Einstellungen treffen. Wähle im ersten Schritt allerdings einfach nur &#8222;Projektvorlage&#8220; &#8211; das ist schließlich, was wir wollen, right!?</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Projektvorlagen-Assistent-Schritt-1.png"><img loading="lazy" decoding="async" width="717" height="549" src="https://robbelroot.de/wp-content/uploads/2023/01/Projektvorlagen-Assistent-Schritt-1.png" alt="Projektvorlagen-Assistent Schritt 1" class="wp-image-13695" title="Projektvorlagen-Assistent Schritt 1"/></a><figcaption class="wp-element-caption">Projektvorlagen-Assistent Schritt 1</figcaption></figure>



<p>Wenn Du auf weiter geklickt hast, kommt jetzt der nächste Schritt. Dort kannst Du wohl die beiden essenziellsten Informationen festlegen: Den Namen &amp; die Beschreibung für das Template – wähle hier daher vor allem einen sinnvollen Namen, wie: &#8222;&lt;C#&gt; / &lt;VB.NET&gt; MVVM Projekttemplate&#8220;, je nachdem für welche Sprache Du Dich ggf. entscheidest. Lege Dir doch auch alternativ für beide Sprachen ein entsprechendes Template an?</p>



<p>&#x1f4a1;&nbsp;<strong>Hinweis</strong>: Hake hier am besten das Kästchen &#8222;Vorlage automatisch in Visual Studio importieren&#8220; an, dann brauchst Du Dich darum nicht manuell kümmern.</p>



<figure class="wp-block-image size-full" id="Projektvorlagen-Assistent-Schritt-2"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Projektvorlagen-Assistent-Schritt-2.png"><img loading="lazy" decoding="async" width="720" height="551" src="https://robbelroot.de/wp-content/uploads/2023/01/Projektvorlagen-Assistent-Schritt-2.png" alt="Projektvorlagen-Assistent Schritt 2" class="wp-image-13701"/></a><figcaption class="wp-element-caption">Projektvorlagen-Assistent Schritt 2</figcaption></figure>



<p>Wenn Du nun ein neues Projekt anlegst, findest Du Dein Template ganz einfach wieder:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2023/01/Projektvorlagen-Assistent-Schritt-3.png"><img loading="lazy" decoding="async" width="941" height="628" src="https://robbelroot.de/wp-content/uploads/2023/01/Projektvorlagen-Assistent-Schritt-3.png" alt="Projektvorlagen-Assistent Schritt 3" class="wp-image-13705" title="Projektvorlagen-Assistent Schritt 3"/></a><figcaption class="wp-element-caption">Projektvorlagen-Assistent Schritt 3</figcaption></figure>
<p>Der Beitrag <a href="https://robbelroot.de/blog/ein-wpf-mvvm-projekt-aufsetzen-guide/">Ein WPF MVVM Projekt aufsetzen (VB.NET &amp; C#) – 2026 Guide</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/ein-wpf-mvvm-projekt-aufsetzen-guide/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>VB.NET Autostart für Dein eigenes Programm erstellen – 2024 Version</title>
		<link>https://robbelroot.de/blog/vb-net-autostart-fuer-dein-eigenes-programm-erstellen/</link>
					<comments>https://robbelroot.de/blog/vb-net-autostart-fuer-dein-eigenes-programm-erstellen/#respond</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Tue, 27 Dec 2022 05:40:27 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET Problemlösungen]]></category>
		<category><![CDATA[automatisch]]></category>
		<category><![CDATA[autostart]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[dotnet]]></category>
		<category><![CDATA[erstellen]]></category>
		<category><![CDATA[programm]]></category>
		<category><![CDATA[programmieren]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[shortcut]]></category>
		<category><![CDATA[start]]></category>
		<category><![CDATA[starten]]></category>
		<category><![CDATA[startup]]></category>
		<category><![CDATA[systemstart]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[verknüpfung]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=13290</guid>

					<description><![CDATA[<p>Dein VB.NET Programm automatisch bei Systemstart starten Hast Du Deine nächste tolle Anwendung entwickelt, Welche Du nun via VB.NET in den Autostart packen möchtest? Im heutigen Beitrag schauen wir uns genau das und mehr, wie z. B. das Erstellen von Verknüpfungen via der &#8222;WshShell&#8220; an. Bleib also dran, es wird &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vb-net-autostart-fuer-dein-eigenes-programm-erstellen/">VB.NET Autostart für Dein eigenes Programm erstellen – 2024 Version</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Autostart-fuer-Dein-eigenes-Programm-erstellen-Programm-mit-Windows-starten-–-2023-Remake-Beitrags-Header.png"><img loading="lazy" decoding="async" width="1280" height="720" src="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Autostart-fuer-Dein-eigenes-Programm-erstellen-Programm-mit-Windows-starten-–-2023-Remake-Beitrags-Header.png" alt="VB.NET Autostart für Dein eigenes Programm erstellen - Programm mit Windows starten" class="wp-image-13329" title="VB.NET Autostart für Dein eigenes Programm erstellen - Programm mit Windows starten"/></a><figcaption class="wp-element-caption">VB.NET Autostart für Dein eigenes Programm erstellen &#8211; Programm mit Windows starten</figcaption></figure>






<h2 class="wp-block-heading">Dein VB.NET Programm automatisch bei Systemstart starten</h2>



<p>Hast Du Deine nächste tolle Anwendung entwickelt, Welche Du nun via VB.NET in den Autostart packen möchtest? Im heutigen Beitrag schauen wir uns genau das und mehr, wie z. B. das Erstellen von Verknüpfungen via der &#8222;WshShell&#8220; an. Bleib also dran, es wird ziemlich interessant &#x1f609;!</p>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Wenn Du es eilig hast, kannst Du auch direkt zum Code weiter unten scrollen, bzw. das <strong><a href="#toc_container">Inhaltsverzeichnis</a></strong> für das Springen zu verschiedenen Eckpunkten verwenden. Auch wenn der Beitrag seinen Fokus auf VB.NET hat, sind alle Code-Beispiele natürlich auch für C# verfügbar! Springe für den <strong><a href="#kompletter-code">kompletten Code</a></strong>, oder für dessen <strong><a href="#verwendungsbeispiele">Verwendungsbeispiele</a></strong> nach unten.</p>



<style>
.info-banner {
    background: #03a9f4;
    padding: 2em;
    border-radius: 0.5em;
    color: whitesmoke;
}
.info-banner a {
    color: #FED766;
}
</style>



<p>Wie Du vielleicht weißt hatte ich auch schonmal ein Video zum Thema gemacht &#8211; vor unfassbaren 11 Jahren, oh man, ich werde alt.. Ein Abonnent hatte mich darauf aufmerksam gemacht, dass es in die Jahre gekommen ist und er sich ein Update wünscht. Dies war mir Befehl und nun sind wir hier &#x1f913;!</p>



<p>Wenn Du moderne Anwendungen entwickeln möchtest – Welche dann passenderweise in den Autostart können –, solltest Du Dir unbedingt diese beiden Beiträge anschauen:</p>



<ul class="wp-block-list">
<li><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/" target="_blank" rel="noreferrer noopener"><strong>Der ultimative INotifyPropertyChanged Guide – für C# &amp; VB.NET</strong></a></li>



<li><strong><a href="https://robbelroot.de/blog/der-ultimative-wpf-datagrid-mvvm-guide-csharp-und-vb-net/" target="_blank" rel="noreferrer noopener">Der ultimative WPF DataGrid MVVM Guide – C# &amp; VB.NET</a></strong></li>
</ul>



<h2 class="wp-block-heading">Als Video ansehen – &#8222;VB.NET Autostart von Programmen&#8220;</h2>



<p>Wenn Du es eiliger, bzw. eventuell keine Lust auf Text hast, kannst Du natürlich auch gerne zum Videoformat greifen und Dich berieseln lassen. Nutze auch im Video gerne die Kapitel, um zu den Stellen von besonderem Interesse zu springen.</p>


<div class="async-youtube" data-embed="ESRgqNvvfKg" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading">Wie startet man Software überhaupt via Autostart?</h2>



<p>Bevor wir uns an die VB.NET-spezifische Thematik wagen, ist es ggf. vorerst ganz sinnvoll, sich mit dieser Frage auseinanderzusetzen: Wie starte ich ein Programm überhaupt bei Systemstart? Grundsätzlich ist die Antwort darauf ziemlich einfach, denn Windows bietet eigentlich alle Bordmittel, Die man als Administrator oder auch Entwickler, braucht.</p>



<p>Dazu gibt es den sogenannten Autostart-Ordner, Welcher ungefähr so wie gleich folgend aussieht. Bei mir ist – zugegebenermaßen – nicht viel dort zu sehen, denn ich versuche den immer &#8222;clean&#8220; zu halten.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Autostart-fuer-das-eigene-Programm-erstellen.png"><img loading="lazy" decoding="async" width="851" height="505" src="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Autostart-fuer-das-eigene-Programm-erstellen.png" alt="VB.NET Autostart für das eigene Programm erstellen" class="wp-image-13305" title="VB.NET Autostart für das eigene Programm erstellen"/></a><figcaption class="wp-element-caption">VB.NET Autostart für das eigene Programm erstellen</figcaption></figure>



<h3 class="wp-block-heading">Vorgehensweise</h3>



<p>Um ein Programm beim Start des Windows-Systems automatisch mit-starten zu lassen, muss man Dieses, bzw. eine Verknüpfung dazu in den Autostart-Ordner ablegen. Das Schöne ist hier wirklich die Möglichkeit der Verknüpfung, denn wer hat Lust, jedes Mal x Megabyte zu kopieren und eventuell dann doppelt zu haben. Ich persönlich hatte bei dieser Vorgehensweise auch nie Probleme, im Gegensatz zu einigen recherchierten Suchergebnissen auf Google. Kommentiere gerne, wenn auch Du auf derartige Probleme stößt, würde mich interessieren!</p>



<h3 class="wp-block-heading">Vorsicht geboten</h3>



<p>Wie erwähnt wird jedes Programm, bzw. jede Verknüpfung im genannten Ordner automatisch bei Systemstart gestartet. Das stattet uns Entwickler &amp; Administratoren natürlich auch mit einer großen Bürde aus. Erstens – so finde ich – sollte man dem Nutzer bei Systemstart nicht direkt 20 GUIs an den Kopf schmeißen und zweitens sollte man ja auch noch an die Startzeit denken. Wer hat schon Lust, 5 Minuten länger zu warten, nur weil ein Programm bei Start schon viel Auslastung zieht!?</p>



<h2 class="wp-block-heading">Welche Möglichkeiten habe ich beim Autostart?</h2>



<p>Egal ob mit VB.NET (oder C#..), oder ohne, wir haben verschiedene Möglichkeiten, den Autostart zu verwenden. Einerseits können wir benutzerspezifische Startmöglichkeiten verwenden, oder auch global, also für alle Nutzer einen automatischen Programmstart anlegen. In diesem Beitrag gehe ich jedoch in erster Linie auf den benutzerspezifischen Autostart ein.</p>



<h2 class="wp-block-heading">Wo, bzw. wie finde ich die Autostart-Ordner?</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/75jxwu.jpg"><img loading="lazy" decoding="async" width="888" height="499" src="https://robbelroot.de/wp-content/uploads/2022/12/75jxwu.jpg" alt="Wo, bzw. wie finde ich die Autostart-Ordner? VB.NET Autostart erstellen" class="wp-image-13331" title="Wo, bzw. wie finde ich die Autostart-Ordner? VB.NET Autostart erstellen"/></a><figcaption class="wp-element-caption">Wo, bzw. wie finde ich die Autostart-Ordner? VB.NET Autostart erstellen</figcaption></figure>



<p>In erster Linie kommt das natürlich darauf an, für wen Du einen Autostart erstellen möchtest. Möchtest Du Dein Programm z. B. für den aktuellen Nutzer in den Autostart legen, dann kommt folgendes Verzeichnis in Frage. Beachte hierbei, dass Du den Platzhalter natürlich durch den jeweiligen Nutzernamen ersetzen müsstest:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">C:\Users\&lt;DeinNutzername>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup</pre>



<p>Du kannst den Ordner auch vereinfacht finden, indem Du die Windows-Taste+R drückst, was den &#8222;Ausführen&#8220;-Dialog startet. Und im nächsten Schritt dann:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">shell:startup</pre>



<p>eingeben, was Dich dann in dem für Dich passenden Autostart-Ordner auskommen lässt. Cool, oder?</p>



<h3 class="wp-block-heading">Globaler Autostart-Ordner</h3>



<p>Möchtest Du hingegen den &#8222;globalen&#8220; Ordner finden, kannst Du stattdessen in folgendem Verzeichnis</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp</pre>



<p>und mit diesem Befehl hier fündig werden (denke an Windows-Taste+R):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">shell:common startup</pre>



<p>Hier findest Du dann die Programme, Welche für alle Nutzer automatisch bei Systemstart gestartet werden sollen.</p>



<h2 class="wp-block-heading">Wie sieht der VB.NET Autostart nun aus?</h2>



<p>Um nun die oben erklärte Logik, bzw. Vorgehensweise mit VB.NET nachzubilden, müssen wir ja praktisch nichts Anderes tun. Wir müssen (via Code) eine Verknüpfung unserer Anwendung erschaffen und Diese nach Möglichkeit im Autostart-Ordner unserer Wahl unterbringen. Besonders die Erstellung einer Verknüpfung kann aber tricky sein, wenn man nicht weiß, wie.</p>



<p>Zusätzlich könnte man weitere Hilfsmittel schreiben, um z. B. überprüfen zu können, ob sich unsere Anwendung aktuell schon im Autostart befindet. Dies alles werden wir selbstverständlich nach Regeln der OOP (Objektorientierten Programmierung) in eine Klasse auslagern. So bleibt alles schön wiederverwendbar und portierbar.</p>



<p>Lege also im nächsten Schritt einen Ordner namens &#8222;Utils&#8220; in Deinem Projekt an und erstelle dort drin eine &#8222;Autostart&#8220;-Klasse.</p>



<h2 class="wp-block-heading">Mit VB.NET den Autostart-Ordner ermitteln</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/Autostart-Ordner-mit-VB.NET-ermitteln.png"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2022/12/Autostart-Ordner-mit-VB.NET-ermitteln.png" alt="Autostart-Ordner mit VB.NET ermitteln" class="wp-image-13386" title="Autostart-Ordner-mit-VB.NET-ermitteln"/></a><figcaption class="wp-element-caption">Autostart-Ordner mit VB.NET ermitteln</figcaption></figure>



<p>Bevor wir uns jedoch Gedanken über Dinge wie &#8222;Wie kann ich die Verknüpfung erstellen&#8220;, usw. machen, starten wir easy. Zuerst einmal müssen wir wissen, wie wir an den Autostart-Ordner kommen, ist ja schließlich Betriebssystem-Sache. Zufälligerweise benutzen wir eine optimale Sprache für dieses Betriebssystem vom selben Hersteller – kann ja dann nicht so schwer sein, richtig? Richtig!</p>



<p>Um an den Pfad zum Autostart-Ordner (auch Startup-Folder genannt) zu kommen, können wir vorgefertigte Funktionen nutzen. Die Methode unseres Vertrauens befindet sich in der <strong><a href="https://learn.microsoft.com/en-us/dotnet/api/system.environment?view=net-7.0" target="_blank" rel="noreferrer noopener">&#8222;Environment&#8220;-Klasse</a></strong> und heißt <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.environment.getfolderpath?view=net-7.0" target="_blank" rel="noreferrer noopener">&#8222;GetFolderPath&#8220;</a></strong>. Diese erwartet einen Wert der <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.environment.specialfolder?view=net-7.0" target="_blank" rel="noreferrer noopener">&#8222;SpecialFolder&#8220;-Enumeration</a></strong> aus selbiger Klasse.</p>



<p>Implementieren wir nun eine Art &#8222;Singleton&#8220;-, bzw. eine &#8222;Lazy&#8220;-Property (Eigenschaft), Welche uns den Autostart-Ordner serviert. So müssen wir der Environment nicht öfter als nötig auf die Nerven gehen &#8211; ergo, die Funktion nur einmalig aufrufen und den Rückgabewert zwischenspeichern:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="startup-directory-property">        Private Shared _startupDirectory As String

        Public Shared ReadOnly Property StartupDirectory As String
            Get
                If String.IsNullOrWhiteSpace(_startupDirectory) Then
                    _startupDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Startup)
                End If
                Return _startupDirectory
            End Get
        End Property</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="startup-directory-property">    private static string _startupDirectory;

    public static string StartupDirectory
    {
        get
        {
            if (string.IsNullOrWhiteSpace(_startupDirectory))
                _startupDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
            return _startupDirectory;
        }
    }</pre>



<h2 class="wp-block-heading">Einen Eintrag in den Autostart mit VB.NET hinzufügen</h2>



<p>Im nächsten Schritt schauen wir uns an, wie wir unsere VB.NET-Anwendung, bzw. eine Verknüpfung davon in den Autostart-Ordner gelegt bekommen. Dazu bauen wir uns eine kleine &#8222;AddEntry&#8220; Methode, Welcher es egal ist, ob ein Pfad zu unserer Anwendung mitkommt, denn Diesen, kann Sie auch selbst ermitteln. So bleibt uns die Möglichkeit übrig, fremde Anwendungen, sowie die Eigene gemütlich hinzuzufügen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="add-entry-method">        ''' &lt;summary>
        ''' Adds an entry to the autostart folder for the given Application
        ''' &lt;/summary>
        ''' &lt;param name="applicationFilePath">The given application filepath, or empty to create an autostart for the current executing .exe&lt;/param>
        Public Shared Sub AddEntry(Optional applicationFilePath As String = "")
            Dim noAppPathProvided = applicationFilePath = ""
            If noAppPathProvided Then
                DefaultToExecutingExe(applicationFilePath)
            End If
            CreateShortcut(applicationFilePath)
        End Sub</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="add-entry-method">/// &lt;summary>
/// Adds an entry to the autostart folder for the given Application
/// &lt;/summary>
/// &lt;param name="applicationFilePath">The given application filepath, or empty to create an autostart for the currently executing .exe&lt;/param>
public static void AddEntry(string applicationFilePath = "")
{
    var noAppPathProvided = applicationFilePath == "";
    if (noAppPathProvided)
        DefaultToExecutingExe(applicationFilePath);
    CreateShortcut(applicationFilePath);
}</pre>



<p>Wie Du hier schon sehen kannst, schauen wir (wie erwähnt), ob ein passender &#8222;applicationFilePath&#8220; mitgeliefert wurde. Wenn nicht &#8222;Defaulten&#8220; wir zur ausführenden Exe-Datei, also dem aktuellen Programm. So oder so erstellen wir am Ende der Methode eine Verknüpfung, was wir im nächsten Schritt besprechen werden.</p>



<h2 class="wp-block-heading">Die ausgeführte .Exe-Datei erkennen</h2>



<p>Nachdem wir die eigentliche Methode schon einmal im letzten Schritt vorbereitet haben, fehlen nun noch die 2 Sub-Routinen. In erster Linie schauen wir hier uns hier schnell die &#8222;DefaultToExecutingExe&#8220;-Methode an. Diese bekommt einen &#8222;ByRef&#8220;-Parameter übergeben &#8211; empfand ich hier irgendwie leichter lesbar &#8211; aber ist nur meine Meinung. Diese setzt dann den &#8222;applicationFilePath&#8220; via &#8222;Process&#8220;-Klasse, bzw. mittels einer verschachtelten Sub-Eigenschaft des Prozesses. Diesen ermitteln wir mit der statischen &#8222;GetCurrentProcess&#8220;-Funktion der &#8222;Process&#8220;-Klasse. Hier gibt es ab .NET 6 auch eine neue Variante, allerdings habe ich hier Die genommen, Welche vermutlich am geläufigsten ist.</p>



<h2 class="wp-block-heading">Unsere VB.NET Anwendung als Verknüpfung in den Autostart legen</h2>



<p>Nun fehlt nur noch ein letztes Werkzeug, die Funktionalität, für unsere (und auch andere Anwendungen) eine Verknüpfung erzeugen zu können. Dies kann – wie oben bereits erwähnt – ein wenig tricky werden, wenn man nicht weiß, wie. Wir verwenden dazu das &#8222;WshShell&#8220;-Interface, Welches komischerweise instanziiert werden kann, Du glaubst mir nicht? Guck in den Code!</p>



<h3 class="wp-block-heading">COM-Verweis hinzufügen</h3>



<p>Bevor wir damit jedoch starten können, müssen wir zuerst noch den benötigten COM-Verweis auf das &#8222;Windows Script Host Object Model&#8220; hinzufügen. Das kannst Du ganz einfach machen, indem Du einen Rechtsklick auf Dein Projekt durchführst und anschließend &#8222;Hinzufügen-&gt;COM-Verweis&#8220; auswählst.</p>



<p>Danach erscheint dieses folgende Fenster, wo Du dann oben rechts dementsprechend z. B. nach &#8222;host&#8220; suchen kannst, dann dürftest Du – siehe Bild – schnell fündig werden:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Windows-Script-Object-Model-COM-Verweis.png"><img loading="lazy" decoding="async" width="789" height="506" src="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Windows-Script-Object-Model-COM-Verweis.png" alt="VB.NET Windows Script Object Model COM-Verweis" class="wp-image-13370" title="VB.NET Windows Script Object Model COM-Verweis"/></a><figcaption class="wp-element-caption">VB.NET Windows Script Object Model COM-Verweis</figcaption></figure>



<h3 class="wp-block-heading">Ein instanziierbares Interface – what the f**?</h3>



<p>Um ehrlich zu sein – wie ich nunmal bin.. – habe ich mich selbst damit auch nicht auseinandergesetzt, warum man diese Schnittstelle instanziieren kann (auch in C#). Und da ich COM-Dinge eigentlich nie benötige, ist mir das hier auch relativ egal, solange es funktioniert. Ich gehe mal ohne Recherche davon aus, dass das an der Deklaration des Interfaces liegt, dort sehen wir ein &#8222;CoClass&#8220;-Attribut.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Verknuepfung-erstellen-WshShell-Interface.png"><img loading="lazy" decoding="async" width="444" height="194" src="https://robbelroot.de/wp-content/uploads/2022/12/VB.NET-Verknuepfung-erstellen-WshShell-Interface.png" alt="VB.NET Verknüpfung erstellen WshShell-Interface" class="wp-image-13364" title="VB.NET Verknüpfung erstellen WshShell-Interface"/></a><figcaption class="wp-element-caption">VB.NET Verknüpfung erstellen WshShell-Interface</figcaption></figure>



<p>Vermutlich wird &#8222;der Code&#8220; dann sehen: &#8222;Ah, da ist ein CoClass Ding, also muss ich das wohl instaziieren&#8220; – oder so ähnlich. EDIT: Habe kurz nachgeguckt, liegt wie vermutet an dem &#8222;CoClass&#8220;-Attribut – Erfahrung &#8222;strikes again&#8220; &#x1f609;!</p>



<h3 class="wp-block-heading">Eine Verknüpfung erstellen</h3>



<p>Nachdem wir das Interface nun instanziieren können – ew, klingt immer noch ekelig, na egal – tun wir gleich genau das. Vorher erstellen wir noch ein &#8222;FileInfo&#8220;-Objekt, Welches uns bei der Arbeit mit Datei-Infos wie den Namen der Datei, usw. unterstützen wird. Und dann gehen wir schonmal hin und erstellen den letztendlichen Pfad der Verknüpfung mit String-Interpolation.</p>



<p>Hier verwenden wir ein weiteres kleines Helferlein, was – wie der Name schon sagt – den Dateinamen ohne Dateiendung für uns herausfiltert. Dazu übergeben wir der statischen &#8222;Path&#8220;-Klassen-Funktion &#8222;GetFileNameWithoutExtension&#8220;, den Namen des &#8222;FileInfo&#8220;-Objektes.</p>



<p>Im letzten Schritt erzeugen wir die Verknüpfung auf Objekt-Ebene und casten Sie in ein für unsere Zwecke passendes Interface – &#8222;IWshShortcut&#8220;. Anschließend kannst Du weitere Eigenschaften wie z. B. die Beschreibung und den Icon-Pfad angeben, dies lasse ich hier aber aus. Zuletzt kommen die beiden wichtigsten Schritte: Den Ziel-Pfad, also den Pfad zur eigentlichen Anwendung festzulegen und dann final auch &#8222;Save&#8220; aufzurufen, damit die Verknüpfung tatsächlich erstellt wird.</p>



<h2 class="wp-block-heading">Prüfen ob ein Autostart-Eintrag existiert</h2>



<p>Nun haben wir die Funktionalität von oben vorliegen und würden direkt mit der Nächsten starten. Es wäre schließlich ganz nützlich, wenn man auch prüfen könnte, ob bereits eine Verknüpfung im Autostart ist. Dazu erstellen wir im nächsten Schritt eine &#8222;EntryExists&#8220;-Funktion, Welche dies über einen Boolean kommuniziert.</p>



<p>In den ersten Zeilen verfahren wir hier wir in der &#8222;AddEntry&#8220;-Methode, also bezogen auf die Pfad-Sache. Wenn kein Pfad angegeben wurde, nehmen wir den Pfad der aktuellen Exe-Datei, usw. Danach bauen wir uns den Pfad zur Verknüpfung (im Autostart-Ordner!) zusammen. Wenn eine derartige Datei (dort) existiert, dann haben wir das Programm bereits im Autostart – yay!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="entry-exists-method">        Public Shared Function EntryExists(Optional applicationFilePath As String = "") As Boolean
            Dim noAppPathProvided = applicationFilePath = ""
            If noAppPathProvided Then
                DefaultToExecutingExe(applicationFilePath)
            End If
            Dim applicationFile = New FileInfo(applicationFilePath)
            Dim shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk")
            Return IO.File.Exists(shortcutPath)
        End Function</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="entry-exists-method">public static bool EntryExists(string applicationFilePath = "")
{
    var noAppPathProvided = applicationFilePath == "";
    if (noAppPathProvided)
        DefaultToExecutingExe(applicationFilePath);
    var applicationFile = new FileInfo(applicationFilePath);
    var shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk");
    return System.IO.File.Exists(shortcutPath);
}</pre>



<h2 class="wp-block-heading">Mit VB.NET den Autostart wieder entfernen</h2>



<p>Im letzten Abschnitt dieses Beitrages werden wir uns ansehen, wie wir erzeugte Einträge auch wieder entfernen können. Dies kennt man natürlich bereits aus größeren Tools. Wir schreiben also eine &#8222;RemoveEntry&#8220;-Methode, Welche sehr ähnlich zu den Vorherigen funktioniert. Zuerst prüfen wir wieder die Pfad-Übergabe, danach erstellen wir der Einfachheit halber eine &#8222;FileInfo&#8220; und danach generieren wir den Verknüpfungs-Pfad.</p>



<p>Wenn Dieser Pfad existent ist, wird er gelöscht, somit wird auch unser VB.NET Autostart entfernt. Beachte hierbei, dass ich bewusst kein Try-Catch drum gemacht habe. Ich möchte aktiv auf Fehler reagieren und diese separat und sauber verarbeiten können.</p>



<h2 class="wp-block-heading" id="kompletter-code">Kompletter Code</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/Kompletter-Code-VB.NET-Autostart.png"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2022/12/Kompletter-Code-VB.NET-Autostart.png" alt="Kompletter Code - VB.NET Autostart" class="wp-image-13384" title="Kompletter Code - VB.NET Autostart"/></a><figcaption class="wp-element-caption">Kompletter Code &#8211; VB.NET Autostart</figcaption></figure>



<p>Hier findest Du den wichtigsten Code für diesen Beitrag. In diesem Beispiel handelt es sich um die kleine von mir gebaute Hilfs-Klasse. Diese vereinfacht Dir die Erstellung der Programm-Verknüpfung innerhalb des Autostart-Ordners via VB.NET (und C#!):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="Autostart.vb" data-enlighter-group="complete-code">Imports System.IO
Imports IWshRuntimeLibrary

Namespace Utils

    Public Class Autostart

        Private Shared _startupDirectory As String

        Public Shared ReadOnly Property StartupDirectory As String
            Get
                If String.IsNullOrWhiteSpace(_startupDirectory) Then
                    _startupDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Startup)
                End If
                Return _startupDirectory
            End Get
        End Property

        ''' &lt;summary>
        ''' Adds an entry to the autostart folder for the given Application
        ''' &lt;/summary>
        ''' &lt;param name="applicationFilePath">The given application filepath, or empty to create an autostart for the current executing .exe&lt;/param>
        Public Shared Sub AddEntry(Optional applicationFilePath As String = "")
            Dim noAppPathProvided = applicationFilePath = ""
            If noAppPathProvided Then
                DefaultToExecutingExe(applicationFilePath)
            End If
            CreateShortcut(applicationFilePath)
        End Sub

        Private Shared Sub DefaultToExecutingExe(ByRef applicationFilePath As String)
            applicationFilePath = Process.GetCurrentProcess().MainModule.FileName
        End Sub

        Private Shared Sub CreateShortcut(applicationFilePath As String)
            Dim applicationFile = New FileInfo(applicationFilePath)
            Dim shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk")
            Dim shell = New WshShell()
            Dim shortcut = CType(shell.CreateShortcut(shortcutPath), IWshShortcut)
            With shortcut
                ' .Description = ""
                ' .IconLocation = ""
                .TargetPath = applicationFile.FullName
                .Save()
            End With
        End Sub

        Public Shared Function EntryExists(Optional applicationFilePath As String = "") As Boolean
            Dim noAppPathProvided = applicationFilePath = ""
            If noAppPathProvided Then
                DefaultToExecutingExe(applicationFilePath)
            End If
            Dim applicationFile = New FileInfo(applicationFilePath)
            Dim shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk")
            Return IO.File.Exists(shortcutPath)
        End Function

        Public Shared Sub RemoveEntry(Optional applicationFilePath As String = "")
            Dim noAppPathProvided = applicationFilePath = ""
            If noAppPathProvided Then
                DefaultToExecutingExe(applicationFilePath)
            End If
            Dim applicationFile = New FileInfo(applicationFilePath)
            Dim shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk")
            IO.File.Delete(shortcutPath)
        End Sub

    End Class

End Namespace
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="Autostart.cs" data-enlighter-group="complete-code">using System.IO;
using IWshRuntimeLibrary;

namespace Utils
{
    public class Autostart
    {
        private static string _startupDirectory;

        public static string StartupDirectory
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_startupDirectory))
                    _startupDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
                return _startupDirectory;
            }
        }

        /// &lt;summary>
        ///         ''' Adds an entry to the autostart folder for the given Application
        ///         ''' &lt;/summary>
        ///         ''' &lt;param name="applicationFilePath">The given application filepath, or empty to create an autostart for the current executing .exe&lt;/param>
        public static void AddEntry(string applicationFilePath = "")
        {
            var noAppPathProvided = applicationFilePath == "";
            if (noAppPathProvided)
                DefaultToExecutingExe(ref applicationFilePath);
            CreateShortcut(applicationFilePath);
        }

        private static void DefaultToExecutingExe(ref string applicationFilePath)
        {
            applicationFilePath = Process.GetCurrentProcess().MainModule.FileName;
        }

        private static void CreateShortcut(string applicationFilePath)
        {
            var applicationFile = new FileInfo(applicationFilePath);
            var shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk");
            var shell = new WshShell();
            var shortcut = (IWshShortcut)shell.CreateShortcut(shortcutPath);
            {
                var withBlock = shortcut;
                // .Description = ""
                // .IconLocation = ""
                withBlock.TargetPath = applicationFile.FullName;
                withBlock.Save();
            }
        }

        public static bool EntryExists(string applicationFilePath = "")
        {
            var noAppPathProvided = applicationFilePath == "";
            if (noAppPathProvided)
                DefaultToExecutingExe(ref applicationFilePath);
            var applicationFile = new FileInfo(applicationFilePath);
            var shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk");
            return System.IO.File.Exists(shortcutPath);
        }

        public static void RemoveEntry(string applicationFilePath = "")
        {
            var noAppPathProvided = applicationFilePath == "";
            if (noAppPathProvided)
                DefaultToExecutingExe(ref applicationFilePath);
            var applicationFile = new FileInfo(applicationFilePath);
            var shortcutPath = Path.Combine(StartupDirectory, $"{Path.GetFileNameWithoutExtension(applicationFile.Name)}.lnk");
            System.IO.File.Delete(shortcutPath);
        }
    }
}</pre>



<h2 class="wp-block-heading" id="verwendungsbeispiele">Verwendung des Codes</h2>



<p>Um den obigen Code verwenden zu können, kannst Du z. B. 2 Knöpfe und eine CheckBox in Deine Form setzen. Beim Start des Formulares wird die Checkbox je nachdem ob das VB.NET Programm im Autostart liegt gefüllt. Wenn Du dann auf die jeweiligen Buttons drückst, kannst Du das Programm wieder aus dem Autostart entfernen, bzw. natürlich auch hinzufügen. Die Namen sind denke ich selbsterklärend..</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="Form1.vb" data-enlighter-group="form-example">Imports AutoStartExampleVb.Utils

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        chbAutostartActivated.Checked = Autostart.EntryExists()
    End Sub

    Private Sub btnActivateAutostart_Click(sender As Object, e As EventArgs) Handles btnActivateAutostart.Click
        Autostart.AddEntry()
        chbAutostartActivated.Checked = True
    End Sub

    Private Sub btnDeactivateAutostart_Click(sender As Object, e As EventArgs) Handles btnDeactivateAutostart.Click
        Autostart.RemoveEntry()
        chbAutostartActivated.Checked = False
    End Sub

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="Form1.cs" data-enlighter-group="form-example">using System;
using AutoStartExampleVb.Utils;

public class Form1
{
    private void Form1_Load(object sender, EventArgs e)
    {
        chbAutostartActivated.Checked = Autostart.EntryExists();
    }

    private void btnActivateAutostart_Click(object sender, EventArgs e)
    {
        Autostart.AddEntry();
        chbAutostartActivated.Checked = true;
    }

    private void btnDeactivateAutostart_Click(object sender, EventArgs e)
    {
        Autostart.RemoveEntry();
        chbAutostartActivated.Checked = false;
    }
}</pre>



<h2 class="wp-block-heading">Downloads</h2>



<p>Keine Lust alles zu kopieren und es manuell zusammen zu frickeln? Dann lade Dir hier einfach den Beispielcode herunter, Dieser beinhaltet die komplette und fertig testbare Projektmappe.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/datei-download/?dlid=676ab19b-1548-4e62-891b-5a79d57bcf7f" target="_blank" rel="noreferrer noopener">AutoStartExampleVb.zip</a></div>


</div>
<p>Der Beitrag <a href="https://robbelroot.de/blog/vb-net-autostart-fuer-dein-eigenes-programm-erstellen/">VB.NET Autostart für Dein eigenes Programm erstellen – 2024 Version</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/vb-net-autostart-fuer-dein-eigenes-programm-erstellen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Der ultimative WPF DataGrid MVVM Guide in 2026 – C# &#038; VB.NET</title>
		<link>https://robbelroot.de/blog/der-ultimative-wpf-datagrid-mvvm-guide-csharp-und-vb-net/</link>
					<comments>https://robbelroot.de/blog/der-ultimative-wpf-datagrid-mvvm-guide-csharp-und-vb-net/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Sat, 24 Dec 2022 12:40:22 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[C# Problemlösungen]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET Problemlösungen]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[WPF Problemlösungen]]></category>
		<category><![CDATA[auswahl]]></category>
		<category><![CDATA[bindung]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[databinding]]></category>
		<category><![CDATA[datagrid]]></category>
		<category><![CDATA[datagridview]]></category>
		<category><![CDATA[mainwindow]]></category>
		<category><![CDATA[mvvm]]></category>
		<category><![CDATA[selecteditem]]></category>
		<category><![CDATA[tabelle]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[view]]></category>
		<category><![CDATA[viewmodel]]></category>
		<category><![CDATA[wpf]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=13143</guid>

					<description><![CDATA[<p>Daten im MVVM tabellarisch darstellen – mit WPF DataGrid Ich glaube jeder Entwickler wird regelmäßig eine Art Tabellen-Steuerelement, also z. B. das WPF DataGrid benutzen müssen. Unter Windows Forms hieß es damals noch &#8222;DataGridView&#8220;, aber letztendlich hat sich vom offensichtlichen Zweck nichts geändert: Wir möchten dem Nutzer Daten anhand einer &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/der-ultimative-wpf-datagrid-mvvm-guide-csharp-und-vb-net/">Der ultimative WPF DataGrid MVVM Guide in 2026 – C# &amp; VB.NET</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/WPF-Datagrid-MVVM-Der-ultimative-Guide.png"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2022/12/WPF-Datagrid-MVVM-Der-ultimative-Guide.png" alt="WPF Datagrid MVVM - Der ultimative Guide" class="wp-image-13288" title="WPF Datagrid MVVM - Der ultimative Guide"/></a><figcaption class="wp-element-caption">WPF Datagrid MVVM &#8211; Der ultimative Guide</figcaption></figure>






<h2 class="wp-block-heading">Daten im MVVM tabellarisch darstellen – mit WPF DataGrid</h2>



<p>Ich glaube jeder Entwickler wird regelmäßig eine Art Tabellen-Steuerelement, also z. B. das WPF DataGrid benutzen müssen. Unter Windows Forms hieß es damals noch &#8222;DataGridView&#8220;, aber letztendlich hat sich vom offensichtlichen Zweck nichts geändert: Wir möchten dem Nutzer Daten anhand einer Tabellenstruktur darstellen. Diese Daten werden anhand von Reihen, Welche aus verschiedenen Spalten bestehen gegliedert dargestellt.</p>



<p>Daher beschäftigen wir uns im heutigen Beitrag damit, was sich seit den Windows Forms (DataGridView) geändert hat und wie man im heutigen MVVM- (Model-View-Viewmodel-) Entwurfsmuster mit Tabellen arbeitet. Auch wenn wir hier natürlich nicht zu tief in die Details von MVVM selbst abtauchen können, werde ich einen Teil wiederholen.</p>



<p>Gerne kannst Du in meinem <strong><a href="https://robbelroot.de/blog/mvvm-csharp/" target="_blank" rel="noreferrer noopener">Beitrag über das Model-View-ViewModel-Entwurfsmuster</a></strong> mehr Details erfahren. Hierbei spielt es auch keine Rolle, ob man VB.NET oder C# verwendet, denn die Prinzipien sind die Gleichen! Für diesen Beitrag ist auch das <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/" target="_blank" rel="noreferrer noopener">Verständnis über die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle </a></strong>essenziell, also schau auch einmal dort vorbei!</p>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Beachte bitte, dass es nicht &#8222;den einen Weg&#8220; gibt. Im heutigen Beitrag schauen wir uns eine Möglichkeit an, alles mehr oder weniger selfmade zu machen, da ich das Grundverständnis wichtig finde. In einem zukünftigen Beitrag, bzw. in einem Update für diesen Beitrag, werde ich dann noch auf die Möglichkeiten mit einem Werkzeug wie Caliburn Micro eingehen. Derartige Frameworks helfen uns Entwicklern bei der Umsetzung und sparen uns Einiges an Arbeit.</p>



<style>
.info-banner {
    background: #03a9f4;
    padding: 2em;
    border-radius: 0.5em;
    color: whitesmoke;
}
.info-banner a {
    color: #FED766;
}
</style>



<h2 class="wp-block-heading">Im Videoformat ansehen</h2>



<p>Falls Du keine Lust auf Text hast, kannst Du Dir auch gerne mein passendes Video ansehen. Ich würde Dir jedoch trotzdem empfehlen, den ein oder anderen Blick auf diesen Beitrag hier zu werfen, da ich hier noch mehr im Detail auf das Thema eingehen kann.</p>


<div class="async-youtube" data-embed="n7U4eQkTsyM" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading">Ordnerstruktur vorbereiten</h2>



<p>Die meisten Entwickler lieben es, alles sauber zu strukturieren, es gehört ja auch irgendwie zum Beruf, oder? Daher gehen wir im nächsten Schritt hin und beginnen mit unserer Projekt-Strukturierung. Achte darauf, dass Du die Ordner nicht aus Versehen in der Projektmappe, sondern tatsächlich im Projektordner erstellst!</p>



<p>Erstelle dazu im ersten Schritt den wichtigsten Ordner in Deinem Projekt, namens &#8222;ViewModels&#8220;. Danach kannst Du noch den Ordner &#8222;Models&#8220; und einen Weiteren namens &#8222;Utils&#8220; erstellen. Danach sind wir startklar und können passende ViewModels dort ablegen und darauf zugreifen. Deine Struktur sollte zu diesem Zeitpunkt wie folgt aussehen:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/Ordnerstruktur-des-Projekts-–-WPF-DataGrid-MVVM-Beispiel.png"><img loading="lazy" decoding="async" width="290" height="318" src="https://robbelroot.de/wp-content/uploads/2022/12/Ordnerstruktur-des-Projekts-–-WPF-DataGrid-MVVM-Beispiel.png" alt="Ordnerstruktur des Projekts – WPF DataGrid MVVM Beispiel" class="wp-image-13171" title="Ordnerstruktur des Projekts – WPF DataGrid MVVM Beispiel"/></a><figcaption class="wp-element-caption">Ordnerstruktur des Projekts – WPF DataGrid MVVM Beispiel</figcaption></figure>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Beachte hier, dass wir uns nicht an alles halten, sprich, wir werden keinen &#8222;Views&#8220;-Ordner anlegen, da hier die Verwendung des DataGrids im Vordergrund steht! Ich möchte Dich hier nicht unnötig damit verwirren, wie man dann das MainWindow inkl. der Application-Datei auch noch umgestalten müsste, etc.</p>



<p>Allerdings ist dies auch nicht schlimm, da es praktisch im Kern darum geht, wo man die Daten her bekommt und wie man Sie anschließend durch Datenbindungen darstellt.</p>



<h2 class="wp-block-heading">Das MainViewModel für die WPF DataGrid Bindung</h2>



<p>Damit wir gleich irgendwann mit sogenannten Datenbindungen im WPF-Bereich arbeiten können, müssen wir vorerst noch andere Vorbereitungen treffen. Da im MVVM-Entwurfsmuster mit ViewModels und dazugehörigen Views gearbeitet wird, legen wir im nächsten Schritt unser erstes ViewModel an.</p>



<p>Dies wird praktisch das &#8222;Haupt-Ding&#8220; unserer Anwendung, daher nennen wir es passenderweise auch &#8222;MainViewModel&#8220;. Weil wir natürlich sauber arbeiten, achte bitte darauf, dass Du die Klassen-Datei nicht nur im korrekten Ordner (ViewModels) anlegst, sondern auch mit einem korrekten Namespace versiehst! </p>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Für das C#-Snippet verwende ich hier die neue Namespace-Variante, also ohne die &#8222;{}&#8220; und einen zusätzlichen Einschub (ich liebe dieses Feature). Wenn Du noch mit der alten Syntax unterwegs bist, kannst Du Diese natürlich genauso gut verwenden. Umschließe dazu einfach das &#8222;MainViewModel&#8220; – wie üblich – mit dem &#8222;namespace&#8220;-Schlüsselwort und &#8222;{}&#8220; und setze die Klasse dort rein.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="erstes-mainviewmodel">namespace DataGridBindingBeispiel.ViewModels;

public class MainViewModel
{

    public MainViewModel()
    {

    }

}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="erstes-mainviewmodel">Namespace ViewModels

    Public Class MainViewModel
    
        Sub New()

        End Sub

    End Class

End Namespace</pre>



<h2 class="wp-block-heading">Den DataContext des MainWindows setzen</h2>



<p>Nachdem Du nun das &#8222;MainViewModel&#8220; erstellt hast, müssen wir Dieses, bzw. eine Instanz davon im nächsten Schritt verwenden. Ansonsten wüsste das &#8222;MainWindow&#8220; nicht, wo es die zugrundeliegenden Daten für die Datenbindungs-Anweisungen findet. Lass uns den &#8222;DataContext&#8220; des &#8222;MainWindows&#8220; nun im Code-Behind-File des Views setzen.</p>



<p>Dorthin gelangst Du, indem Du einfach die F7-Taste drückst, während Du im XAML-Designer des &#8222;MainWindows&#8220; bist:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainWindow.xaml.cs" data-enlighter-group="datacontext-setzen">using DataGridBindingBeispiel.ViewModels;

class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainWindow.xaml.vb" data-enlighter-group="datacontext-setzen">Imports DataGridBindingBeispiel.ViewModels

Class MainWindow

    Sub New()
        InitializeComponent()
        DataContext = New MainViewModel()
    End Sub

End Class</pre>



<p>Grundsätzlich weiß unser Hauptfenster also nun, wo es seine Daten finden kann, wenn wir eine Datenbindung verwenden. Das nächste Problem ist aktuell natürlich noch, dass im Datenkontext noch gar keine Daten vorhanden sind, an Welche gebunden werden könnte. Lass uns das also gleich erledigen, vorerst müssen wir leider noch etwas &#8222;erfinden&#8220; &#8211; dazu im nächsten Abschnitt mehr.</p>



<h2 class="wp-block-heading">Unser Model &#8211; womit arbeiten wir überhaupt?</h2>



<p>Bevor wir gleich überhaupt &#8222;etwas&#8220; anzeigen können, müssen wir uns über dieses &#8222;Etwas&#8220; erst einmal Gedanken machen. In unserem heutigen Beispiel möchten wir z. B. Rechnungen im DataGrid darstellen. Dazu müssen wir unserem Programm jedoch zuerst beibringen, was eine Rechnung überhaupt ist – woher soll es das sonst wissen..</p>



<p>Bringen wir also im nächsten Schritt unserem Programm bei, was eine Rechnung ist. Erstelle dazu im Ordner &#8222;Models&#8220; eine Klassen-Datei namens &#8222;Bill&#8220; und schreibe den folgenden Code hinein. Achte auch hier selbstverständlich auf den korrekten Namespace!</p>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Natürlich werden wir hier keine vollständige Rechnung nachbauen, sondern nur eine für das Beispiel komprimierte Klasse/Variante. Beachte auch, dass wir beim VB.NET-Beispiel eckige Klammern brauchen, da &#8222;Date&#8220; ein reserviertes Schlüsselwort, bzw. ein &#8222;DateTime&#8220;-Alias ist.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="Bill.cs" data-enlighter-group="bill-model">using System;

namespace Models
{
    public class Bill
    {
        public int Id { get; set; }

        public DateTime Date { get; set; }

        public Bill(int id, DateTime date)
        {
            Id = id;
            Date = date;
        }
    }
}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="Bill.vb" data-enlighter-group="bill-model">Namespace Models

    Public Class Bill

        Public Property Id As Integer

        Public Property [Date] As Date

        Sub New(id As Integer, [date] As Date)
            Me.Id = id
            Me.Date = [date]
        End Sub

    End Class

End Namespace</pre>



<p>So, nun weiß unser Programm, was eine Rechnung ist und der nächste Schritt wäre dann, Rechnungen darzustellen. Dies besteht natürlich wieder aus einzelnen Schritten.</p>



<h2 class="wp-block-heading">Eine erste Datenbindung</h2>



<p>Gehe nun zurück in die &#8222;MainWindow.xaml&#8220;-Datei, dort werden wir jetzt die Tabelle, also das WPF DataGrid erstellen. Natürlich werden wir hier jetzt keine ausgefeilte UI bauen, sondern uns auf das DataGrid allein fokussieren. Schreibe wie für XAML üblich also nun ein öffnendes und schließendes DataGrid-Tag in den XAML Code.</p>



<p>Zusätzlich geben wir auch direkt die erste Datenbindung an:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;!-- MainWindow Rest ... -->

&lt;DataGrid ItemsSource="{Binding Bills}">

&lt;/DataGrid>

&lt;!-- ... --></pre>



<p>Damit sagen wir dem XAML-Code erstmal: &#8222;Hey, stelle hier bitte ein DataGrid dar und die dazugehörigen Einträge/Reihen, findest Du in der Bills-Eigenschaft&#8220;.</p>



<p>Der Code hätte aktuell außer einer Fehlermeldung noch nichts zur Folge. Wo schauen die Datenbindungs-Anweisungen nochmal nach? Richtig! Im DataContext, sprich unserer &#8222;MainViewModel&#8220;-Instanz! Dort gibt es bisher noch keine Art &#8222;Liste&#8220; von Rechnungen. Aktuell kommt so nur ein Fehler namens &#8222;Kann Bills auf blabla nicht finden&#8220;.</p>



<h3 class="wp-block-heading">Automatisch generierte Spalten im WPF DataGrid</h3>



<p>Da wir gerade aber beim Thema XAML sind, stell&#8216; Dir einfach mal vor, wir hätten diese &#8222;Bills&#8220; schon drin (dazu kommen wir gleich selbstverständlich auch noch).  Aktuell würde das &#8222;DataGrid&#8220; alle Spalten anhand der Eigenschaften der zugrundeliegenden Objekte automatisch generieren. Dies sähe erstmal so aus:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/DataGrid-mit-automatischen-Spalten-–-WPF-DataGrid-MVVM-Beispiel.png"><img loading="lazy" decoding="async" width="254" height="151" src="https://robbelroot.de/wp-content/uploads/2022/12/DataGrid-mit-automatischen-Spalten-–-WPF-DataGrid-MVVM-Beispiel.png" alt="DataGrid mit automatischen Spalten – WPF DataGrid MVVM Beispiel" class="wp-image-13215" title="DataGrid mit automatischen Spalten – WPF DataGrid MVVM Beispiel"/></a><figcaption class="wp-element-caption">DataGrid mit automatischen Spalten – WPF DataGrid MVVM Beispiel</figcaption></figure>



<p>Ich sag mal so: Cool, aber auch irgendwie uncool.. Die meisten deutschen Nutzer würden sich direkt beschweren, was denn bitte ein &#8222;Date&#8220; sein soll (bitte arg deutsch aussprechen &#8211; dann kommt der Effekt besser rüber). Ebenso ist die Darstellung, bzw. das Format des Datums nicht wirklich toll &#8211; aber um beides kümmern wir uns nun!</p>



<h3 class="wp-block-heading">Spalten &amp; Formate selbst bestimmen</h3>



<p>Bevor wir uns über die Darstellungs-Formate der jeweiligen Spalten Gedanken machen, müssen wir erstmal dafür sorgen, dass manuelle Spalten vorhanden sind. Das geht im WPF DataGrid ganz einfach, indem wir – eben XAML-typisch – eine Eigenschaft des DataGrids festlegen: &#8222;DataGrid.ColumnDefinitions&#8220;.</p>



<p>Gehe also wieder in den XAML-Code und zwischen die beiden DataGrid-Tags, dort erstellst Du ein öffnendes und schließendes Tag für die &#8222;ColumnDefinitions&#8220;-Eigenschaft. Dort drin fügen wir unsere Spalten-Definitionen hinzu. Für unser Beispiel haben wir erstmal eine simple Text-Spalte und eine Weitere mit einer Format-Angabe.</p>



<h3 class="wp-block-heading">Der Kopf-Text der Spalte</h3>



<p>Wir geben der jeweiligen Spalte einen &#8222;Header&#8220;-Wert, damit wir eine Spaltenüberschrift bekommen und danach noch die Datenbindungs-Anweisung. Bei der Id ist es relativ simple, da wir hier nur sagen: &#8222;Stelle hier einfach die Zahl der Id als Text dar&#8220;. Beim Datum sieht es anders aus, hier sagen wir erstmal, dass er das Datum nehmen, aber speziell formatieren soll. Dies erreichen wir innerhalb der Binding-Anweisung mit der Eigenschaft &#8222;StringFormat&#8220;.</p>



<h3 class="wp-block-heading">Formatierung</h3>



<p>Dort schreibst Du einfach zwei geschweifte Klammern-Paare, pass jedoch dabei auf, die Autovervollständigung grätscht hier öfters mal dazwischen. Im zweiten Paar geben wir zuerst den zu verwendenden Wert an (in diesem Fall den ersten Wert = Index 0), Welchen wir dann mit &#8222;dd.MM.yy HH:mm&#8220; formatieren möchten.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">        &lt;DataGrid ItemsSource="{Binding Bills}">
            &lt;DataGrid.Columns>
                &lt;DataGridTextColumn Header="Nr." Binding="{Binding Id}" />
                &lt;DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat={}{0:dd.MM.yy HH:mm}}" />
            &lt;/DataGrid.Columns>
        &lt;/DataGrid></pre>



<h3 class="wp-block-heading">Automatische Spalten-Generierung deaktivieren</h3>



<p>Das Ganze sieht dann nun wie gleich folgend aus. Wie Du sicherlich sehen wirst, haben wir das Problem, dass nun 4 Spalten sichtbar sind. Das liegt daran, dass das DataGrid nach wie vor die Spalten generiert, dies müssen wir deaktivieren!</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/DataGrid-mit-manuellen-Spalten-–-WPF-DataGrid-MVVM-Beispiel.png"><img loading="lazy" decoding="async" width="333" height="135" src="https://robbelroot.de/wp-content/uploads/2022/12/DataGrid-mit-manuellen-Spalten-–-WPF-DataGrid-MVVM-Beispiel.png" alt="DataGrid mit manuellen Spalten – WPF DataGrid MVVM Beispiel" class="wp-image-13228" title="DataGrid mit manuellen Spalten – WPF DataGrid MVVM Beispiel"/></a><figcaption class="wp-element-caption">DataGrid mit manuellen Spalten – WPF DataGrid MVVM Beispiel</figcaption></figure>



<p>Die automatische Generierung der Spalten deaktivierst Du mit der <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.windows.controls.datagrid.autogeneratecolumns?view=windowsdesktop-7.0" target="_blank" rel="noreferrer noopener">&#8222;AutoGenerateColumns&#8220;-Eigenschaft</a></strong> des WPF DataGrids. Mit ausgeschalteter Spalten-Generierung sieht es dann final so aus:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/Ausgeschaltete-Spaltengenerierung.png"><img loading="lazy" decoding="async" width="168" height="124" src="https://robbelroot.de/wp-content/uploads/2022/12/Ausgeschaltete-Spaltengenerierung.png" alt="Ausgeschaltete Spaltengenerierung" class="wp-image-13232" title="Ausgeschaltete Spaltengenerierung"/></a><figcaption class="wp-element-caption">Ausgeschaltete Spaltengenerierung</figcaption></figure>



<p>Selbstverständlich hält Dich auch hier nichts davon ab, die &#8222;Header&#8220;-Eigenschaft zu binden und dynamischen Inhalt wie z. B. Übersetzungen darzustellen.</p>



<h2 class="wp-block-heading">Die Datengebundene Liste befüllen</h2>



<p>Nachdem wir nun alle Vorbereitungen soweit abgeschlossen haben, kümmern wir uns um die eigentlichen Rechnungen. Einerseits werden wir hierfür eine passende Art von Liste erstellen und zusätzlich befüllen. Leider reicht eine normale Liste, also eine gängige &#8222;List(Of T) &amp; List&lt;T&gt;&#8220; nicht aus, da Diese keine Änderungen kommunizieren kann.</p>



<h3 class="wp-block-heading">Kommunizieren von Listen-Änderungen</h3>



<p>Für gewöhnlich nimmt man hier die &#8222;ObservableCollection&#8220;, bzw. einen Listen-Typ, Welcher die <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.collections.specialized.inotifycollectionchanged?view=net-7.0" target="_blank" rel="noreferrer noopener">&#8222;INotifyCollectionChanged&#8220;-Schnittstelle</a></strong> implementiert. Elemente, Welche Listen-Änderungen an das gebundene Steuerelement kommunizieren möchten, können diese Schnittstelle implementieren. Um dies zu tun, lösen die implementierenden Klassen das &#8222;CollectionChanged&#8220;-Ereignis aus.</p>



<h3 class="wp-block-heading">MainViewModel erweitern</h3>



<p>Nun fügen wir dem &#8222;MainViewModel&#8220; die passende Eigenschaft hinzu. Beachte, dass ich hier nur einen &#8222;Getter&#8220; definiere, da wir die Liste nicht mehrfach setzen werden. Wir instanziieren Diese nur einmal im Konstruktor und befüllen Sie anschließend bei Bedarf mit Daten. Natürlich können wir die Liste auch zu passenden Zeitpunkten leeren.</p>



<p>Nach der Instanziierung der Liste fügen wir noch ein paar Einträge (natürlich in Form von Rechnungen) hinzu.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="collectionchanged-liste">using System.Collections.ObjectModel;
using DataGridBindingBeispiel.Models;

namespace DataGridBindingBeispiel.ViewModels;

public class MainViewModel
{

    public ObservableCollection&lt;Bill> Bills { get; }

    public MainViewModel()
    {
        Bills = new ObservableCollection&lt;Bill>();
        Bills.Add(new Bill(1, DateTime.Now));
        Bills.Add(new Bill(2, DateTime.Now));
    }

}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="collectionchanged-liste">Imports System.Collections.ObjectModel
Imports DataGridBindingBeispiel.Models

Namespace DataGridBindingBeispiel.ViewModels

    Public Class MainViewModel

        Public ReadOnly Property Bills As ObservableCollection(Of Bill)

        Public Sub New()
            Bills = New ObservableCollection(Of Bill)()
            Bills.Add(new Bill(1, Date.Now))
            Bills.Add(new Bill(2, Date.Now))
        End Sub

    End Class

End Namespace</pre>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Wir benutzen die aufeinanderfolgenden &#8222;Add&#8220;-Aufrufe nur, weil es hier mehr oder weniger die Basis ist. In einem realen Projekt sollte man eine bessere Implementierung einer <strong>INotifyCollectionChanged</strong>-Schnittstelle verwenden, da jeder &#8222;Add&#8220;-Aufruf hier einmal &#8222;Hey, es hat sich was geändert&#8220; schreit. Dies ist natürlich völliger Quatsch! Schaue für eine beispielhafte Implementierung in den nächsten Abschnitt.</p>



<p>Im besten Fall sammelt man vorher einmal alle Items und fügt Diese in einem Schritt hinzu. Das hat den offensichtlichen Vorteil, dass die eigentliche (datengebundene) Liste nur ein einziges Mal &#8222;Hey, ich hab mich geändert&#8220; auslöst &#8211; statt 200x für 200 Einträge.. Dies könnte im ersten Schritt z. B. durch eine Art Service passieren, oder im Testfall wie folgt:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="no-change-trigger-list">var list = new List&lt;Bill>();
// triggert kein CollectionChanged!
list.Add(new Bill(1, DateTime.Now));
// triggert auch kein CollectionChanged!
list.Add(new Bill(2, DateTime.Now));

// triggert CollectionChanged!!
// Achtung: Gibt es leider nicht standardmäßig!
Bills.AddRange(list);</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="no-change-trigger-list">Dim list = New List(Of Bill)()
' triggert kein CollectionChanged!
list.Add(New Bill(1, Date.Now))
' triggert auch kein CollectionChanged!
list.Add(New Bill(2, Date.Now))

' triggert CollectionChanged!!
' Achtung: Gibt es leider nicht standardmäßig!
Bills.AddRange(list)</pre>



<h2 class="wp-block-heading">Eine ObservableRangeCollection – AddRange zur Hand!</h2>



<p>Wie im vorherigen Abschnitt erwähnt, gibt es Performance und – naja es fühlt sich halt nicht richtig an – Sauberkeits-Probleme, wenn wir die normale &#8222;ObservableCollection&#8220; verwenden. Sie triggert für jeden hinzugefügten oder gelöschten Eintrag das &#8222;CollectionChanged&#8220;-Ereignis, was blöd ist, wenn das 200x in wenigen Millisekunden passiert.</p>



<p>Daher kannst Du Dich an folgender Klasse orientieren, Welche eine &#8222;AddRange&#8220;-Methode anbietet. Diese triggert das &#8222;CollectionChanged&#8220;-Ereignis bei der Verwendung der Methode nur ein einziges Mal! Google dazu am besten einfach mal nach &#8222;ObservableRangeCollection&#8220;.</p>



<h2 class="wp-block-heading">Mit ausgewähltem Item arbeiten</h2>



<p>Jetzt, wo die Liste an sich erstmal funktioniert, kümmern wir uns im letzten Schritt um eine weitere wichtige Funktionalität: Die Auswahl eines Eintrages. Hierbei spielt es natürlich auch eine Rolle, ob wir den Eintrag aus dem Code, bzw. durch die UI heraus setzen. Also, grundsätzlich zumindest, wir können es uns aber ziemlich einfach machen, da wir mit Zweiwege-Datenbindungen arbeiten.</p>



<p>Bevor wir das allerdings ans Laufen bekommen, fehlt vorher noch eine weitere Sache. Auch hierbei wäre es wieder ratsam, wenn Du Dir zum tieferen Verständnis <strong><a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/" target="_blank" rel="noreferrer noopener">meinen Beitrag über die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle</a> </strong>anschaust.</p>



<h3 class="wp-block-heading">Das PropertyChangedBase-Helferlein</h3>



<p>Der Einfachheit halber, werden wir von der dort gezeigten &#8222;PropertyChangedBase&#8220;-Klasse erben. Diese Klasse vereinfacht uns das Auslösen des &#8222;PropertyChanged&#8220;-Ereignisses, Welches der grafischen Oberfläche alles Notwendige signalisiert. Kopiere Dir die Klasse daher gerne in einen &#8222;Utils&#8220;-Ordner und packe Sie in den entsprechenden Namespace:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="PropertyChangedBase.cs" data-enlighter-group="property-changed-base">using System.Runtime.CompilerServices;
using System.ComponentModel;

namespace Utils
{
    public abstract class PropertyChangedBase : INotifyPropertyChanged
    {
        protected void NotifyOfPropertyChange([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="PropertyChangedBase.vb" data-enlighter-group="property-changed-base">Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Namespace Utils

    Public MustInherit Class PropertyChangedBase
        Implements INotifyPropertyChanged

        Protected Sub NotifyOfPropertyChange(&lt;CallerMemberName> Optional propertyName As String? = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub

        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    End Class

End Namespace</pre>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Lasse nun Dein &#8222;MainViewModel&#8220; von dieser Basis-Klasse erben, oder implementiere das &#8222;INotifyPropertyChanged&#8220;-Interface manuell!</p>



<h3 class="wp-block-heading">Eine SelectedBill-Eigenschaft</h3>



<p>Füge im nächsten Schritt nun eine &#8222;SelectedBill&#8220;-Eigenschaft im ViewModel hinzu. Diese erlaubt uns die Datenbindung der ausgewählten Rechnung aus dem Datagrid. Das schöne ist, es funktioniert in beide Richtungen! Bedenke hier, dass Du das oben im blauen Hinweis angegebene Interface implementierst, oder von meine Beispiel-Basisklasse erbst!</p>



<p>Die &#8222;SelectedBill&#8220;-Eigenschaft hat hier einen speziellen &#8222;Setter&#8220;. Falls der sich hinter dem Feld befindliche Wert nicht von dem via &#8222;Setter&#8220; übermittelte Wert unterscheiden sollte, springen wir heraus. Warum sollten wir auch &#8222;es hat sich was geändert&#8220; auslösen, wenn dem nicht so ist. Wenn sich allerdings was geändert hat, kommunizieren wir dies via &#8222;PropertyChanged&#8220;-Ereignis nach außen.</p>



<p>Somit können alle interessierten Objekte auf ihre Art – wie das DataGridView – darauf reagieren.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="selected-bill-eigenschaft">using System.Collections.ObjectModel;
using DataGridBindingBeispiel.Models;
using DataGridBindingBeispiel.Utils;

namespace ViewModels
{
    public class MainViewModel : PropertyChangedBase
    {
        public ObservableCollection&lt;Bill> Bills { get; set; }

        private Bill _selectedBill;

        public Bill SelectedBill
        {
            get
            {
                return _selectedBill;
            }
            set
            {
                if (_selectedBill == value)
                    return;
                _selectedBill = value;
                NotifyOfPropertyChange();
            }
        }

        public MainViewModel()
        {
            Bills = new ObservableCollection&lt;Bill>();
            Bills.Add(new Bill(1, DateTime.Now));
            Bills.Add(new Bill(2, DateTime.Now));
        }
    }
}</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="selected-bill-eigenschaft">Imports System.Collections.ObjectModel
Imports DataGridBindingBeispiel.Models
Imports DataGridBindingBeispiel.Utils

Namespace ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        Public Property Bills As ObservableCollection(Of Bill)

        Private _selectedBill As Bill

        Public Property SelectedBill As Bill
            Get
                Return _selectedBill
            End Get
            Set(value As Bill)
                If _selectedBill Is value Then
                    Return
                End If
                _selectedBill = value
                NotifyOfPropertyChange()
            End Set
        End Property

        Sub New()
            Bills = New ObservableCollection(Of Bill)()
            Bills.Add(New Bill(1, Date.Now))
            Bills.Add(New Bill(2, Date.Now))
        End Sub

    End Class

End Namespace</pre>



<p class="info-banner">&#x1f4a1; <strong>Hinweis</strong>: Wenn Du eine Standard-Auswahl, bzw. eine Auswahl aus dem Code heraus treffen möchtest, kannst Du der Eigenschaft einfach einen Wert zuweisen.</p>



<p>Das Setzen der ausgewählten Rechnung aus dem Code heraus könnte so aussehen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="select-default-bill">// zweite Rechnung standardmäßig auswählen
SelectedBill = Bills[1];</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="select-default-bill">' zweite Rechnung standardmäßig auswählen
SelectedBill = Bills(1)</pre>



<h3 class="wp-block-heading">Datenbindung anpassen</h3>



<p>Final können wir nun noch einmal in den XAML-Code gehen, um die bisherige Datenbindungen des DataGrids zu verändern. Bisher weiß das WPF DataGrid natürlich nichts von der &#8222;SelectedBill&#8220;-Eigenschaft in unserem Viewmodel. Dies ändern wir nun mit der folgenden Bindung an die &#8222;SelectedItem&#8220;-Eigenschaft des Datagrids:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainWindow.xaml" data-enlighter-group="">&lt;!-- restlicher MainWindow.xaml Code.. -->

&lt;DataGrid ItemsSource="{Binding Bills}" SelectedItem="{Binding SelectedBill}" AutoGenerateColumns="False">
    &lt;DataGrid.Columns>
        &lt;DataGridTextColumn Header="Nr." Binding="{Binding Id}" />
        &lt;DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat={}{0:dd.MM.yy HH:mm}}" />
    &lt;/DataGrid.Columns>
&lt;/DataGrid>

&lt;!-- restlicher Code.. --></pre>
<p>Der Beitrag <a href="https://robbelroot.de/blog/der-ultimative-wpf-datagrid-mvvm-guide-csharp-und-vb-net/">Der ultimative WPF DataGrid MVVM Guide in 2026 – C# &amp; VB.NET</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/der-ultimative-wpf-datagrid-mvvm-guide-csharp-und-vb-net/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Der ultimative INotifyPropertyChanged Guide in 2026 – für C# &#038; VB.NET</title>
		<link>https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/</link>
					<comments>https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Fri, 23 Dec 2022 11:38:47 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[C# (C Sharp)]]></category>
		<category><![CDATA[C# Problemlösungen]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET Problemlösungen]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[databinding]]></category>
		<category><![CDATA[daten]]></category>
		<category><![CDATA[datenbindung]]></category>
		<category><![CDATA[dotnet]]></category>
		<category><![CDATA[eigenschaften]]></category>
		<category><![CDATA[inotifypropertychanged]]></category>
		<category><![CDATA[mvvm]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[viewmodel]]></category>
		<category><![CDATA[winforms]]></category>
		<category><![CDATA[wpf]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=13002</guid>

					<description><![CDATA[<p>Datenbindungs-Änderungen einfach ans UI weiterleiten Kennst Du auch das Problem, die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle immer wieder implementieren zu müssen? Egal ob in VB.NET oder C#, jedes ViewModel benötigt in guter alter &#8222;WPF MVVM&#8220;-Manier dieses implementierte Interface, um Änderungen der Daten an die Oberfläche bekanntzugeben. Nervig wird es dann nur, wenn man das &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/">Der ultimative INotifyPropertyChanged Guide in 2026 – für C# &amp; VB.NET</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/Aenderungen-einfach-via-PropertyChanged-Basisklasse-kommunizieren.png"><img loading="lazy" decoding="async" width="1280" height="720" src="https://robbelroot.de/wp-content/uploads/2022/12/Aenderungen-einfach-via-PropertyChanged-Basisklasse-kommunizieren.png" alt="Änderungen einfach via PropertyChanged Basisklasse kommunizieren" class="wp-image-13018"/></a><figcaption class="wp-element-caption">Änderungen einfach via PropertyChanged Basisklasse kommunizieren</figcaption></figure>






<h2 class="wp-block-heading">Datenbindungs-Änderungen einfach ans UI weiterleiten</h2>



<p>Kennst Du auch das Problem, die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle immer wieder implementieren zu müssen? Egal ob in VB.NET oder C#, jedes ViewModel benötigt in guter alter &#8222;WPF MVVM&#8220;-Manier dieses implementierte Interface, um Änderungen der Daten an die Oberfläche bekanntzugeben. Nervig wird es dann nur, wenn man das Gefühl hat, endlose Monkey-Work zu schreiben, aber zum Glück gibt es die Vererbung, gell!?</p>



<p class="info-banner">&#x1f4a1; Du hast es eilig?: Kein Problem, navigiere einfach mithilfe des&nbsp;<strong><a href="#toc_container">Inhaltsverzeichnisses</a></strong>&nbsp;an für Dich wichtige Punkte. Natürlich kannst Du Dich auch durch <strong><a href="#video">das Video</a></strong> berieseln lassen. Im Beitrag gehe ich allerdings mehr auf die Details und mehr Hintergründe ein &#8211; ggf. nimmst Du Ihn daher als Ergänzung.</p>



<style>
.info-banner {
    background: #03a9f4;
    padding: 2em;
    border-radius: 0.5em;
    color: whitesmoke;
}
.info-banner a {
    color: #FED766;
}
</style>



<div class="wp-block-group quick-reference-block has-background" style="border-left-color:#F7941D;border-left-width:4px;background-color:#f9f9f9;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1.5rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow">
<h3 class="wp-block-heading">&#x26a1; Schnellübersicht – vollständige Implementierung auf einen Blick</h3>



<p style="font-size:0.9rem">Für alle, die direkt den fertigen Code brauchen. Die ausführliche Erklärung mit allen Hintergründen folgt weiter unten.</p>



<h4 class="wp-block-heading">1. Basisklasse (einmalig im Projekt anlegen)</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="qr-base">using System.ComponentModel;
using System.Runtime.CompilerServices;

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    protected void NotifyOfPropertyChange([CallerMemberName] string? propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="qr-base">Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public MustInherit Class PropertyChangedBase
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub NotifyOfPropertyChange( Optional propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class
</pre>



<h4 class="wp-block-heading">2. ViewModel davon erben lassen</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="qr-vm">public class MainViewModel : PropertyChangedBase
{
    private string _name = string.Empty;

    public string Name
    {
        get => _name;
        set
        {
            if (_name == value) return;
            _name = value;
            NotifyOfPropertyChange();
        }
    }
}
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="qr-vm">Public Class MainViewModel
    Inherits PropertyChangedBase

    Private _name As String = String.Empty

    Public Property Name As String
        Get
            Return _name
        End Get
        Set(value As String)
            If _name = value Then Return
            _name = value
            NotifyOfPropertyChange()
        End Set
    End Property
End Class
</pre>



<h4 class="wp-block-heading">3. XAML-Binding im View</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainWindow.xaml" data-enlighter-group="qr-xaml">&lt;TextBlock Text="{Binding Name}" />
&lt;TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</pre>



<h4 class="wp-block-heading">4. DataContext setzen (Code-Behind)</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="qr-dc">public MainWindow()
{
    InitializeComponent();
    DataContext = new MainViewModel();
}
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="qr-dc">Sub New()
    InitializeComponent()
    DataContext = New MainViewModel()
End Sub
</pre>



<p style="font-size:0.85rem">&#x1f447; <strong>Warum das so funktioniert, was <code>CallerMemberName</code> bedeutet und wie man Schritt für Schritt dahin kommt</strong> – dafür ist der Rest dieses Artikels da.</p>
</div></div>



<p></p>



<p>Mithilfe von Vererbung werden wir uns heute ein kleines Helferlein, Welches einem die tägliche Arbeit im Bereich Datenbindungen vereinfachen kann, erstellen. In einem folgenden Beitrag werde ich hier auch noch einen Schritt weiter optimieren, das darfst Du auf keinen Fall verpassen &#8211; dort reduzieren wir alles auf eine einzige Zeile &#x1f609;. Ich meine, wer hat schon Bock alles 50x zu schreiben!?</p>



<p>Bevor wir damit jedoch starten, machen wir erst einmal eine Reise zurück, also zur Basis von <strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-7.0" target="_blank" rel="noreferrer noopener">INotifyPropertyChanged</a></strong>.</p>



<h2 class="wp-block-heading">Zu viel Text? Schaue das Video!</h2>



<p>Wenn Du keine Lust hast, diesen Beitrag in schriftlicher Form durchzuarbeiten, kannst Du Dir natürlich auch das Video zu Gemüte führen.</p>


<div class="async-youtube" data-embed="xQMZSmJMIME" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading">Was ist INotifyPropertyChanged?</h2>



<p>Wie oben bereits erwähnt, gehen wir erst einmal einen Schritt zurück, bevor wir weiter nach vorn gehen. Man muss ja schließlich erstmal die Basics dahinter verstehen, bevor man dann auf Diesen aufbauen kann.  Wie man zumindest schonmal an dem für .NET Schnittstellen typischen &#8222;I&#8220;-Prefix erkennen kann, handelt es sich bei &#8222;INotifyPropertyChanged&#8220; um eine Schnittstelle (engl. Interface).</p>



<h3 class="wp-block-heading">Exkurs Schnittstellen</h3>



<p>Ohne zu tief in die Thematik Schnittstellen einzutauchen, könnte man sagen: &#8222;Schnittstellen definieren grobes Verhalten, Eigenschaften und auch Ereignisse, ohne Diese konkret zu implementieren&#8220;. Wenn man einen Menschen mit Interfaces beschreiben wollen würde, könnte man vereinfacht z. B. wie folgt beginnen:</p>



<ul class="wp-block-list">
<li>&#8222;ICanWalk&#8220;</li>



<li>&#8222;IHaveBladder&#8220;</li>



<li>&#8222;ICanSpeak&#8220;</li>
</ul>



<p>Damit würden wir genau das tun, was Schnittstellen ausmacht: Beschreiben, was das jeweilige Objekt können wird (sogar muss). Wir wissen nun, dass ein Objekt, Welches diese Schnittstellen implementieren würde, laufen, sprechen und auf Toilette gehen können muss (eine Blase hat..). Im nächsten Schritt, könnte ich mir also bei der Verwendung eines dieser Objekte sicher sein, dass es sprechen kann. Ich weiß zwar nicht wie dieses &#8222;Ding&#8220; spricht, aber ich kann mir sicher sein, dass es dies anbietet.</p>



<h3 class="wp-block-heading">Reine Abstraktion &#8211; Nichts Konkretes</h3>



<p>Wo wir beim nächsten wichtigen Punkt wären: Wir wissen nicht, wie der oder diejenige spricht! Die eine Person könnte eventuell nuscheln oder ggf. stottern. Der nächste könnte zum Beispiel in jedem zweiten Satz &#8222;Hm..&#8220; sagen &#8211; wobei mich das an meine Videos erinnert (Spaß beiseite).</p>



<p>Okay, ich meine, wir könnten dies natürlich auch anders designen- wir sind ja schließlich die Programmierer. Wir könnten z. B. das &#8222;ICanSpeak&#8220; nochmal genauer konkretisieren &#8211; aber dies ist eine Design-, Erfahrungs- und Anforderungssache! Unser &#8222;ICanSpeak&#8220; könnte dann z. B. &#8222;ISpeakStutterly&#8220; o. ä. heißen. Somit würde vielleicht eine andere Person, die auf stotternde Menschen spezialisiert ist, schauen: &#8222;Okay, wer von allen Menschen die ich hier vorliegen habe, stottern? Wem kann ich mit meiner speziellen Expertise helfen?&#8220;.</p>



<h3 class="wp-block-heading">Quasi unendlich Möglichkeiten</h3>



<p>Wenn man dies nun weiter spinnt, könnte man auch über eine dritte Instanz nachdenken, die &#8222;ISpeakStutterly &#8211; Stotterer&#8220; und &#8222;IHelpStutterer&#8220; gezielt zusammenbringt. Aber ich denke das reicht an dieser Stelle, sonst tauchen wir viel zu tief in die Interface-Thematik ab.</p>



<h2 class="wp-block-heading">Wofür ist INotifyPropertyChanged?</h2>



<p>Nachdem wir oben nun eine kleine Erinnerung an die Thematik &#8222;Schnittstellen&#8220; ins Gedächtnis gerufen haben, nun zum &#8222;INotifyPropertyChanged&#8220;-Interface selbst. Grundsätzlich ist die &#8222;INotifyPropertyChanged&#8220;-Schnittstelle dazu da, der Oberfläche signalisieren zu können: &#8222;Hey, hier hat sich etwas geändert, bitte reagiere entsprechend darauf!&#8220;. Dies kann man grob und einfach definieren, WPF guckt bei einer Datenbindung: &#8222;Okay, immer wenn xy sich ändert, muss ich die Oberfläche neu zeichnen, bzw. die Darstellung aktualisieren&#8220;.</p>



<h3 class="wp-block-heading">Das Problem &#8211; keine Kristallkugel!</h3>



<p>Da weder Du noch ich z. B. voraussagen können, was der jeweils Andere in Zukunft realisieren wird, wird Dir auffallen, dass auch das .NET-Team vor diesem Problem stand. Sie konnten weder wissen, dass ich eine Transportsoftware baue (und vor allem nicht, wie ich Diese objekt-logisch-technisch gestalten würde), noch, dass Du z. B. eine rein hypothetische Lern-Software baust.</p>



<h3 class="wp-block-heading">Doch kein Problem?</h3>



<p>Das Gute ist allerdings, dass das .NET-Team das auch gar nicht wissen, oder gar hellsehen musste. Alles was wir zusammenfassen können, könnten wir mit einem groben &#8222;Verhalten&#8220; festhalten: &#8222;Wenn sich was ändert, muss gesagt werden, was sich geändert hat&#8220; &#8211; mehr nicht. Genau dort greift letztendlich das &#8222;INotifyPropertyChanged&#8220;-Interface und füllt die Lücke!</p>



<h2 class="wp-block-heading">Wie verwendet man INotifyPropertyChanged dann?</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/INotifyPropertyChanged-wie-Bescheid-geben.png"><img loading="lazy" decoding="async" width="640" height="360" src="https://robbelroot.de/wp-content/uploads/2022/12/INotifyPropertyChanged-wie-Bescheid-geben.png" alt="INotifyPropertyChanged - wie Bescheid geben" class="wp-image-13042" title="INotifyPropertyChanged - wie Bescheid geben"/></a><figcaption class="wp-element-caption">INotifyPropertyChanged &#8211; wie Bescheid geben?</figcaption></figure>



<p>Um dies zu erklären, packe ich mal wieder meine überaus heftigen (#ironieaus) Paint-Skills aus. Schaue Dir einmal die Parteien/Teilnehmer im Bild hier drüber an, dort habe ich es vereinfacht dargestellt.</p>



<h3 class="wp-block-heading">Aus dem realen Leben</h3>



<p>Stell Dir vor, Du bietest über einen bekannten Online-Marktplatz einen Artikel zum Verkauf an. Nun findet ein Nutzer Deinen Artikel und er kauft Diesen auch. Aber was nun? Du musst den Artikel natürlich verschicken und hierzu gibst Du die Sendungsverfolgung an &#8211; Du beobachtest also anschließend (mehr oder weniger), was der Versanddienstleister Dir mitteilt.</p>



<p>Auf unser Beispiel bezogen, bist Du jetzt praktisch die agierende und die zuhörende Partei in Einem &#8211; in der Praxis wäre dies getrennt, aber aktuell egal. Du bekommst von den modernen Marktplätzen das verdiente Geld heutzutage nur ausgezahlt, wenn der Versanddienstleister die Zustellung des Paketes bestätigt. Ohne den signalisierenden Dienstleister würde der Marktplatz also nie Wind davon bekommen, dass das Paket angekommen ist.</p>



<h3 class="wp-block-heading">Nochmal auf MVVM bezogen</h3>



<p>Wie oben erwähnt, vermischten sich zumindest in diesem Beispiel verschiedene Rollen. Daher werde ich dies nun auf das Model-View-ViewModel-Entwurfsmuster übertragen. Also das, was man gängigerweise in der Entwicklung mit WPF und XAML verwendet.</p>



<h2 class="wp-block-heading">INotifyPropertyChanged in WPF – vollständiges MVVM Beispiel</h2>



<p>Hierzu steigen wir nun in den Code ein und erstellen ein reelles Beispiel. Im allerersten Schritt kannst Du dafür einfach ein WPF-Projekt (egal ob C# oder VB.NET) aufsetzen. Wenn Du dies getan hast, kannst Du nun einen Ordner namens &#8222;ViewModels&#8220; im Projekt anlegen.</p>



<h3 class="wp-block-heading">Das MainViewModel erstellen</h3>



<p>Erstelle im angelegten Ordner eine Datei namens &#8222;MainViewModel&#8220; und befülle Sie mit dem gleichen folgenden Code. Beachte hier, dass wir nicht ganz MVVM-konform sind, da ich hier keine Art Container namens &#8222;ShellView&#8220; erzeuge und auch das &#8222;MainWindow&#8220; nicht wirklich umbenenne (z. B. in MainView). Ich möchte den Fokus hier wirklich nur auf das Notwendigste halten.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="VB.NET" data-enlighter-group="first-vm">Namespace ViewModels

    Public Class MainViewModel

        Public Property FirstName As String

        Sub New()
            FirstName = "Robert"
        End Sub

    End Class

End Namespace
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="C#" data-enlighter-group="first-vm">namespace ViewModels
{
    public class MainViewModel
    {
        public string FirstName { get; set; }

        public MainViewModel()
        {
            FirstName = "Robert";
        }
    }
}</pre>



<h3 class="wp-block-heading">Den DataContext des Views setzen</h3>



<p>Wir arbeiten an dieser Stelle wirklich nur mit den Bordmitteln und verzichten daher auf Zusatz-Frameworks wie &#8222;Caliburn.Micro&#8220;. Daher müssen wir uns auch selbst darum kümmern, dass das View ein passendenes ViewModel gesetzt bekommt. Dies erledigen wir in diesem Beispiel (sonst nach Präferenz) in der &#8222;Code behind&#8220;-Datei. Öffne dazu einfach das &#8222;MainWindow&#8220;, sodass Du den XAML-Code siehst und drücke F7. Dies sollte dann der (einzige) Code im View-Code sein:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainWindow.xaml.vb" data-enlighter-group="view-code">' HIER DEINEN PROJEKTNAMEN/BASE-NAMESPACE EINTRAGEN!
Imports &lt;YOURPROJECTNAME>.ViewModels

Class MainWindow

    Sub New()
        InitializeComponent()
        DataContext = New MainViewModel()
    End Sub

End Class</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainWindow.xaml.cs" data-enlighter-group="view-code">using &lt;YOURPROJECTNAME>.ViewModels;

class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}</pre>



<h3 class="wp-block-heading">Eine erste Darstellung</h3>



<p>Um nun die Eigenschaft namens &#8222;FirstName&#8220; aus dem &#8222;MainViewModel&#8220; tatsächlich im &#8222;MainWindow&#8220; darstellen zu können, müssen wir einfach eine für WPF übliche Datenbindung verwenden. Erstelle im MainWindow also gerne einen Textblock und wende die Datenbindung wie folgt an. Beachte, dass Du in der Zeile mit dem &#8222;:local&#8220; Deinen Projektnamen verwendest und das &#8222;YOURPROJECTNAME&#8220; ersetzt!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:YOURPROJECTNAME"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
    &lt;Grid>

        &lt;StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
            &lt;TextBlock Text="{Binding FirstName}" />
        &lt;/StackPanel>

    &lt;/Grid>
&lt;/Window>
</pre>



<p>Das daraus resultierende Ergebnis kannst Du hier sehen (ich weiß, noch nicht wirklich interessant, aber hier geht es ja nicht um&#8217;s Design):</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/WPF-Datenbindung-von-ViewModel-zu-View-INotifyPropertyChanged.png"><img loading="lazy" decoding="async" width="788" height="444" src="https://robbelroot.de/wp-content/uploads/2022/12/WPF-Datenbindung-von-ViewModel-zu-View-INotifyPropertyChanged.png" alt="WPF Datenbindung von ViewModel zu View - INotifyPropertyChanged" class="wp-image-13075" title="WPF Datenbindung von ViewModel zu View - INotifyPropertyChanged"/></a><figcaption class="wp-element-caption">WPF Datenbindung von ViewModel zu View &#8211; INotifyPropertyChanged</figcaption></figure>



<h2 class="wp-block-heading">Was, wenn sich etwas ändert?</h2>



<p>Nachdem Du im letzten Schritt das Basic-MVVM-Beispiel erstellt hast, können wir uns nun weiter an das &#8222;INotifyPropertyChanged&#8220;-Interface selbst herantasten. Dazu zeige ich Dir eine kleine Demonstration, Welche alles noch weiter verdeutlichen wird. Außerdem ändern wir im nächsten Schritt den Inhalt der &#8222;FirstName&#8220;-Eigenschaft im ViewModel.</p>



<p>Lass&#8216; uns nun einmal folgende Methode im MainViewModel definieren, beachte jedoch, dass ich dies hier nur als Demonstration des eigentlichen Anliegens mache. Man sollte keine Async Subs/Voids verwenden, da man damit Probleme mit der sogenannten &#8222;Statemachine&#8220;, bzw. dem &#8222;Exception-Stack&#8220; erzeugt &#8211; aber.. anderes Thema.</p>



<p>Natürlich müssen wir die Methode auch aufrufen, sonst passiert schließlich nicht viel – dies tue ich im Konstruktor. Ca. 2 Sekunden nach der Ausführung des Programmes sollte sich nun die &#8222;FirstName&#8220;-Eigenschaft im ViewModel ändern, tut Sie aber scheinbar nicht &#8211; doch, tut Sie wohl &#x1f609;! Setze gerne einen Haltepunkt, wenn Du mir nicht glaubst, die Zeile wird ausgeführt!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="changeafterseconds-vm">' .... MainViewModel

Sub New()
    FirstName = "Robert"
    ChangeAfterSeconds()
End Sub

Private Async Sub ChangeAfterSeconds()
    Await Task.Delay(2000)
    FirstName = "Hans"
End Sub

' ....</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="changeafterseconds-vm">// .... MainViewModel

public MainViewModel()
{
    FirstName = "Robert";
    ChangeAfterSeconds();
}

private async void ChangeAfterSeconds()
{
    await Task.Delay(2000);
    FirstName = "Hans";
}

// ....</pre>



<h3 class="wp-block-heading">Warum wird die Änderung nicht angezeigt?</h3>



<p>Nun – woah – nachdem wir nun einige Basics wie die groben Zwecke von Interfaces und vielen anderen Dingen wiederholt haben, kommen wir (endlich, sorry) zum Punkt! Das View kennt zwar nun das zu sich passende ViewModel – genauer gesagt, den gesetzten DataContext, Welcher eine Instanz des MainViewModels ist. Es bekommt allerdings ohne weitere Hilfe nichts von den Änderungen an einer jeweiligen Eigenschaft mit.</p>



<p>Dies lösen wir nun mit dem INotifyPropertyChanged Interface!</p>



<h2 class="wp-block-heading">Eine INotifyPropertyChanged Implementierung</h2>



<p>Wie Du im letzten Abschnitt gesehen hast, hat sich die &#8222;FirstName&#8220;-Eigenschaft trotz Ausführung der Zeile augenscheinlich nicht geändert. Wir haben aber durch z. B. Einzelschritt-Debugging feststellen können, dass Diese sich sehr wohl ändert, jedoch eine Benachrichtigung darüber an die UI fehlt. Diese werden wir nun mit einer &#8222;INotifyPropertyChanged&#8220;-Implementierung realisieren.</p>



<h3 class="wp-block-heading">Das PropertyChanged-Ereignis</h3>



<p>Schreibe dazu einfach unter den Namen Deiner Klasse &#8222;Implements INotifyPropertyChanged&#8220; und bestätige die Importierung des &#8222;System.ComponentModel&#8220;-Namespaces. Dann sollte auch direkt das &#8222;PropertyChanged&#8220;-Ereignis eingebunden werden. Und ja, was soll ich sagen, dies ist es, worum es im Kern geht!</p>



<p>Nach Ausführung der Auto-Vervollständigung sieht unser MainViewModel nun ungefähr so aus:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="mainvm-fail-implement">Imports System.ComponentModel

Namespace ViewModels

    Public Class MainViewModel
        Implements INotifyPropertyChanged

        Public Property FirstName As String

        Sub New()
            FirstName = "Robert"
            ChangeAfterSeconds()
        End Sub

        Private Async Sub ChangeAfterSeconds()
            Await Task.Delay(2000)
            FirstName = "Hans"
        End Sub

        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="mainvm-fail-implement">using System.Threading.Tasks;
using System.ComponentModel;

namespace ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public string FirstName { get; set; }

        public DummyViewModel()
        {
            FirstName = "Robert";
            ChangeAfterSeconds();
        }

        private async void ChangeAfterSeconds()
        {
            await Task.Delay(2000);
            FirstName = "Hans";
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}</pre>



<p>Aber dennoch passiert irgendwie nichts? Ich kann mir schon Deine Gedanken vorstellen: &#8222;Aber Robert, Du hast doch gesagt, dass das dann geht.. :/&#8220;. Geduld junger Padawan, es bringt natürlich nichts, das Ereignis nur zu deklarieren, wir müssen es ja schließlich auch zur passenden Zeit auslösen!</p>



<h3 class="wp-block-heading">Das Ereignis auslösen</h3>



<p>Um nun eine Änderung von &#8222;FirstName&#8220; an die Datenbindung der grafischen Oberfläche zu kommunizieren, müssen wir das &#8222;PropertyChanged&#8220;-Ereignis mit entsprechenden Daten auslösen. Wir haben mit dem Ereignis die Möglichkeit mitzuteilen, welche Eigenschaft sich geändert hat. Um nun zu sagen, dass sich &#8222;FirstName&#8220; geändert hat, können wir es an passender Stelle so machen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="mainvm-propchanged-ausloesen">' ... MainViewModel
FirstName = "Hans"
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(FirstName)))
' ...</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="mainvm-propchanged-ausloesen">// ... MainViewModel
FirstName = "Hans";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FirstName)));
// ...</pre>



<p class="info-banner">&#x1f4a1;<strong>Hinweis</strong>: Beachte, dass ich hier den NameOf-Ausdruck verwende, Dieser hilft mir mit Compiler-Unterstützung, die korrekten namen der Variablen/Eigenschaften zu verwenden. So kann ich nicht aus Versehen &#8222;FirstNamee&#8220; schreiben, ohne das ich es merke!</p>



<p>Nun können wir die Änderung tatsächlich (endlich) im View sehen. Bedenke hierbei auch, dass Du diese Vorgehensweise für sämtliche Eigenschaften verwenden kann! Booleans, Strings, oder komplexe Objekte, Du sagst hier schließlich nur &#8222;xy hat sich geändert&#8220; – wer das dann wie rendert (z. B. mit einem DataTemplate), ist eine andere Sache!</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/12/Eigenschaft-geaendert-INotifyPropertyChanged.gif"><img loading="lazy" decoding="async" width="796" height="452" src="https://robbelroot.de/wp-content/uploads/2022/12/Eigenschaft-geaendert-INotifyPropertyChanged.gif" alt="Eigenschaft geändert - INotifyPropertyChanged" class="wp-image-13100" title="Eigenschaft geändert - INotifyPropertyChanged"/></a><figcaption class="wp-element-caption">Eigenschaft geändert &#8211; INotifyPropertyChanged</figcaption></figure>



<h2 class="wp-block-heading">Verbesserung 1 – Zentralisierung</h2>



<p>Sicherlich kannst Du Dir jetzt schon denken, dass es ziemlich anstrengend sein kann, wenn Du nun überall, wo Du die &#8222;FirstName&#8220;-Eigenschaft änderst, das Ereignis auslösen musst. Keine Sorge, musst Du nicht, Du kannst es schließlich durch eine manuell implementierte Eigenschaft (statt der Automatischen) zentralisieren. Wir können also folgendes formulieren: &#8222;Immer wenn die Eigenschaft gesetzt wird *hust, Setter, hust*, dann müssen wir PropertyChanged auslösen&#8220;.</p>



<p class="info-banner">&#x1f4a1;<strong>Hinweis</strong>: In einem der nächsten Beiträge werde ich erklären, wie man auch dies wesentlich einfacher gestalten kann, allerdings bleibt einem ohne weitere Hilfe erstmal nichts Anderes übrig!</p>



<p>Dies könnte so aussehen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.vb" data-enlighter-group="propchanged-zentralisierung">' ... MainViewModel
Private _firstName As String

Public Property FirstName As String
    Get
        Return _firstName
    End Get
    Set (value as String)
        ' nur wenn sich wirklich was geändert hat..
        If _firstName = value Then
            Return
        End If
        ' .. sagen, dass sich was geändert hat!
        _firstName = value
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(FirstName)))
    End Set
End Property
' ...</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="MainViewModel.cs" data-enlighter-group="propchanged-zentralisierung">// ... MainViewModel
    private string _firstName;

    public string FirstName
    {
        get
        {
            return _firstName;
        }
        set
        {
            // nur wenn sich wirklich was geändert hat..
            if (_firstName == value)
                return;
            // .. sagen, dass sich was geändert hat!
            _firstName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FirstName)));
        }
    }
// ...</pre>



<p>Nun kann die Eigenschaft ruhig aus verschiedenen Quellen gesetzt werden und trotzdem haben wir nur an einer zentralen Stelle die Auslösung des &#8222;PropertyChanged&#8220;-Ereignisses.</p>



<h2 class="wp-block-heading">Verbesserung 2 – Vererbung der Funktionalität</h2>



<p>Du kannst Dir nun sicherlich vorstellen, Welchen Aufwand wir haben, wenn wir (wie für die meisten Applikationen üblich) mehrere ViewModels haben. In jedem der ViewModels müssten wir das INotifyPropertyChanged implementieren und den Code immer und immer wieder schreiben – bäh!</p>



<h3 class="wp-block-heading">PropertyChangedBase-Klasse</h3>



<p>Deshalb kommen wir zu einer weiteren Verbesserung, wir vererben die erstellte Funktionalität einfach weiter und bauen uns sogar noch eine zentrale Methode zum Auslösen des &#8222;PropertyChanged&#8220;-Ereignisses. Erstelle also nun die folgende Klasse, in einem &#8222;Utils&#8220; Unterordner Deines Projekts, von der Du dann anschließend in folgenden Projekten und ViewModels erben kannst. Beachte, dass Du Diese natürlich in den passenden Namespace packen solltest!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="PropertyChangedBase.vb" data-enlighter-group="propertychangedbase">Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Namespace Utils

    Public MustInherit Class PropertyChangedBase
        Implements INotifyPropertyChanged

        Protected Sub NotifyOfPropertyChange(&lt;CallerMemberName> Optional propertyName As String = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub

        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="PropertyChangedBase.cs" data-enlighter-group="propertychangedbase">using System.Runtime.CompilerServices;
using System.ComponentModel;

namespace Utils;

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    protected void NotifyOfPropertyChange([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}</pre>



<p>Die &#8222;NotifyOfPropertyChange&#8220;-Methode bietet uns hier neben dem einfachen Auslösen des Ereignisses auch noch weitere Vorteile. Wir verwenden das &#8222;CallerMemberName&#8220;-Attribut für die Erfassung des &#8222;Property&#8220;-Namens. Wir müssen dann nicht zwangsweise den Namen der Eigenschaft angeben, er erkennt die aufrufende Property automatisch. Nur wenn wir bei einer Änderung des Vornamens, z. B. die  Änderung der darauf basierenden &#8222;FullName Readonly Property&#8220; kommunizieren wollen würden, müssen wir den Namen explizit angeben.</p>



<h3 class="wp-block-heading">Ein ViewModel mit der Basis-Klasse</h3>



<p>Deine folgenden ViewModels könntest Du nun also wie folgt erstellen. Passe hierbei Dein View ggf. noch an, indem Du zwei weitere Textblöcke mit passenden Bindungen hinzufügst. Ich habe testweise 1 weitere Eigenschaften, namens &#8222;LastName&#8220; und eine auf beiden vorherigen basierende Eigenschaft namens &#8222;FullName&#8220; hinzugefügt.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="NewMainViewModel.vb" data-enlighter-group="propertychanged-viewmodel">Imports &lt;YOURPROJECTNAME>.Utils

Namespace ViewModels

    Public Class NewMainViewModel
        Inherits PropertyChangedBase

        Private _firstName As String

        Public Property FirstName As String
            Get
                Return _firstName
            End Get
            Set(value As String)
                If _firstName = value Then
                    Return
                End If
                _firstName = value
                NotifyOfPropertyChange()
                NotifyOfPropertyChange(NameOf(FullName))
            End Set
        End Property

        Private _lastName As String

        Public Property LastName As String
            Get
                Return _lastName
            End Get
            Set(value As String)
                If _lastName = value Then
                    Return
                End If
                _lastName = value
                NotifyOfPropertyChange()
                NotifyOfPropertyChange(NameOf(FullName))
            End Set
        End Property

        Public ReadOnly Property FullName As String
            Get
                Return $"{FirstName} {LastName}"
            End Get
        End Property

        Sub New()
            FirstName = "Robert"
            LastName = "Skibbe"
            ChangeAfterSeconds()
        End Sub

        Private Async Sub ChangeAfterSeconds()
            Await Task.Delay(2000)
            FirstName = "Hans"
            LastName = "Mustermann"
        End Sub

    End Class

End Namespace</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="NewMainViewModel.cs" data-enlighter-group="propertychanged-viewmodel">using &lt;YOURPROJECTNAME>.Utils;

namespace ViewModels
{
    public class NewMainViewModel : PropertyChangedBase
    {
        private string _firstName;

        public string FirstName
        {
            get
            {
                return _firstName;
            }
            set
            {
                if (_firstName == value)
                    return;
                _firstName = value;
                NotifyOfPropertyChange();
                NotifyOfPropertyChange(nameof(FullName));
            }
        }

        private string _lastName;

        public string LastName
        {
            get
            {
                return _lastName;
            }
            set
            {
                if (_lastName == value)
                    return;
                _lastName = value;
                NotifyOfPropertyChange();
                NotifyOfPropertyChange(nameof(FullName));
            }
        }

        public string FullName
        {
            get
            {
                return $"{FirstName} {LastName}";
            }
        }

        public NewMainViewModel()
        {
            FirstName = "Robert";
            LastName = "Skibbe";
            ChangeAfterSeconds();
        }

        private async void ChangeAfterSeconds()
        {
            await Task.Delay(2000);
            FirstName = "Hans";
            LastName = "Mustermann";
        }
    }
}</pre>



<h2 class="wp-block-heading">Weiterführende Links</h2>



<ul class="wp-block-list">
<li><strong><a href="https://robbelroot.de/blog/was-ist-configureawait-false-und-warum-sollte-es-der-standard-sein/" target="_blank" rel="noreferrer noopener">Was ist ConfigureAwait und warum sollte „False“ der Standard sein?</a></strong></li>



<li><strong><a href="https://robbelroot.de/blog/backgroundworker-beispiel-in-vb-net/" target="_blank" rel="noreferrer noopener">Die ultimative VB.NET Background Worker Anleitung</a></strong></li>



<li><strong><a href="https://robbelroot.de/blog/vb-net-dictionary-ein-kompletter-guide/" target="_blank" rel="noreferrer noopener">VB NET Dictionary – ein kompletter Guide</a></strong></li>
</ul>
<p>Der Beitrag <a href="https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/">Der ultimative INotifyPropertyChanged Guide in 2026 – für C# &amp; VB.NET</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/der-ultimative-inotifypropertychanged-guide/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
	</channel>
</rss>
