Laden...

Forenbeiträge von Palladin007 Ingesamt 2.079 Beiträge

29.07.2021 - 09:56 Uhr

Microsoft.Extensions.Configuration

Das macht das ganze mit JSON, XML, INI, what ever.
Gibt's glaub auch für die DB, dann aber nicht von Mircosoft, ein eigener DB-Provider wäre aber nicht so riesig schwer.
Damit arbeitest Du dann mit IConfiguration und das arbeitet mit Strings.

Zusätzlich gibt's noch Microsoft.Extensions.Configuration.Binder und Microsoft.Extensions.Options (mit Microsoft.Extensions.Options.ConfigurationExtensions)
Ersteres bietet die Möglichkeit, die Konfiguration auf Objekte zu mappen und Letzteres liefert eine Options-Pattern-Implementierung mit automatische Anbindung an die Konfiguration..

Die Konfiguration solltest Du in Form einer simplen Key/Value-Tabelle speicherst, zusätzlich so Infos wie eine Beschreibung oder der Typ. Bedenke aber, dass der Key auch zum Configuration-Prinzip aus den Frameworks passen muss, schwer ist das aber nicht.
Das kannst Du dann leicht anzeigen und bearbeiten, das Configuration-Framework liefert dann den Zugriff innerhalb des Programms und kann ggf. auf Objekte gemappt werden.

PS:
JimStark war wohl schneller

28.07.2021 - 13:57 Uhr

Alle Links in einem neuen Tab halte ich für eine ganz schlechte Lösung.
Aber nur externe Links in einem neuen Tab sind denke ich ein guter Zwischenlösung.

27.07.2021 - 20:34 Uhr

Gewonnen hat die Argumentation, dass wir nicht immer im neuen Fenster öffnen, sondern das dem Benutzer überlassen (zB durch mittlere Maustaste).

Ich hätte es auch lieber anders 🙂

Ich schließe mich deinen Kollegen an: Lieber dem Nutzer überlassen. 🙂
Warum ... keine Ahnung, vielleicht Gewohnheit.
Allerdings arbeiten sehr viele andere Websites auch so, ich halte es für klüger, ungefähr bei dem zu bleiben, was verbreitet ist.

Ich persönlich nutze aber so gut wie immer die Mitteltaste und öffne einfach alles in einem neuen Tab.
Es würde mich also nicht wirklich stören, wenn Du das änderst - ich habe so oder so nach ca. 10 Minuten rund 500 Tabs 😁

25.07.2021 - 14:07 Uhr

Oder eine gemeinsame Server-Anwendung, bei der jeder Prozess regelmäßig seinen Status hinterlegt.
So könnte jeder Prozess dort den Status anderer PCs abfragen und wenn später auch Datenaustausch nötig wird, wäre das einfacher.

14.07.2021 - 01:47 Uhr

Bei einem Catch, das einfach nur irgendeinen Default zurückgibt, bekomme ich Bauchschmerzen.

Gerade beim Thema Reflection sind die Namen fest definiert, da ist nichts dynamisch. (Naja, außer man lädt/generiert zur Laufzeit, aber das ist hier nicht der Fall)
Ist ein Name falsch, dann ist er nun mal falsch und definitiv ein Fehler zur Entwicklungszeit. So ein Fehler sollte nicht geschluckt werden!
Ich persönlich arbeite daher möglichst mit nameof (Docs), dann passt der Compiler für mich auf.

Viel eher könnte man eine "TryGetValue"-Methode daraus machen und dann entsprechend reagieren, wenn der Wert nicht gefunden wird. Dann aber bitte prüfen, ob's die Property gibt und nicht mit Exceptions für den Kontrollfluss arbeiten.
Oder man lässt die Exception fliegen (kapselt sie ggf. in einer Exception mit sprechendem Namen und mehr Infos) und erfährt dann auch auf Anhieb, was das Problem war.
Konkrete Exceptions an der konkreten Position sind sehr viel leichter zu behandeln, als ein Fehler wie "irgendwo ist irgendwann ein null entstanden".

Außerdem habe ich die Erfahrung gemacht, dass solche allgemeinen ExtensionMethods früher oder später zu Chaos führen.
Bedenke, dass sie für alles gelten (object) und Du sie daher auch überall aufrufen kannst, was einerseits nervig werden und andererseits zu ungünstigen Abhängigkeiten führen kann.
Abgesehen davon ist das Ding auch nichts anderes als eine ganz normale statische Methode mit ein bisschen Compiler-Zucker und den gleichen Nachteilen, die jede statische Methode hat. Bei diesem Beispiel ist das kein Problem, sobald aber etwas spezifischere Logik dazu kommt, wird es zu einem Problem, das Du nicht mehr so einfach weg bekommst.

Das heißt, wenn es auf eine solche Reflection-Lösung hinauslaufen sollte:
Einfach eine normale private Methode in die Klasse, in der sie gebraucht wird.
Und ein sinnvolles Fehlerhandling indem z.B. geprüft wird, ob es die Property auch wirklich gibt und ein Setter da ist.

10.07.2021 - 02:31 Uhr

var value = GetDisplayVersionOrNull("...")
    ?? GetDisplayVersionOrNull("...");

if (value == null)
{
    // ...
}
else
{
    // ...
}

Muss keine eigene Methode sein, macht das aber cleaner.

Ach und guck dir das an:
Registry.GetValue(String, String, Object) Methode (Microsoft.Win32)
Erspart dir vermutlich die eigene GetDisplayVersion-Methode.

Und Thema NullReference:

Packs in eine Variable und prüf drauf, in der eigenen Methode kein Problem.
Oder so:


var value = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey("...")?.GetValue("DisplayVersion");

Beachte das Fragezeichen vor "GetValue".

03.07.2021 - 15:33 Uhr

Kommt noch zusammen mit dem Feature, dass man überhaupt sieht, was man gelesen hat 😉

Hast Du eine Lösung für das Performance-Problem gefunden?

02.07.2021 - 22:29 Uhr

Super Idee, gefällt mir gut 😁

Aber einen kleinen Kritikpunkt habe ich:

Aus diesem Stück Text:


\=====
[type]FileInfo[/type]
\=====
[msdocs]FileInfo[/msdocs]
\=====
[nuget]MediatR[/nuget]
\=====

wird dieses Ergebnis:

=====
FileInfo (Api-Docs)
=====
FileInfo (Docs)
=====
MediatR
=====

Sieht aus, als würde da ein paar Zeilenumbrüche verschluckt werden?

30.06.2021 - 11:13 Uhr

Das sind halt alles Dinge, die mit den Frameworks ASP.NET und Refit quasi keine Arbeit mehr sind 🙂
Für ASP.NET Core gibt's in VisualStudio entsprechende Templates, da kannst Du eines erstellen, was dann auch direkt einen Test-Endpunkt hat.

Und für Refit gibt's ne gute kleine Doku - leider nur mit async.
Du kannst natürlich auch ohne Refit arbeiten, musst den ganzen Client-Kram dann aber selber machen.

30.06.2021 - 10:12 Uhr

C#-Version != .NET-Version
Du kannst auch auf steinalten .NET-Versionen mit neuer C#-Version arbeiten, es funktionieren bloß nicht alle Funktionen - zumindest nicht ohne etwas Vorbereitung.

Und die Top-Level-Anweisungen finde ich ziemlich unnötig.
Auch viele andere Features in den letzten Versionen halte ich für fragwürdig (z.B. ref return). Sie mögen ihre Daseinsberechtigung haben, gerade für einen Einsteiger oder jemanden, der nicht immer jedes Detail einer Funktion nachliest, können sie sogar gefährlich sein.

29.06.2021 - 12:59 Uhr

Naja, die Probleme, wie man C# lernt, gibt's ja auch nicht erst sei kurzem und die Lösungen sind immer die gleichen 😁
Ja, Bücher und Artikel sind nicht immer top-aktuell, aber wie gesagt: Die Grundlagen sind identisch.
Wenn Du top aktuell magst, dann lies die MSDN-Doku, die haben auch eine Sparte für Einsteiger.

Ach ja, hab ich letztens hier kennengelernt:
https://www.youtube.com/watch?v=pyN7JTQM7sU
Ist die einzige Tutorial-Reihe, die ich nicht pauschal ablehnen würde - zumindest sieht sie auf den ersten Blick sehr gut aus und Microsoft guckt drauf.
Wie umfangreich und detailliert das ist, kann ich aber nicht sagen und mMn. sind Bücher immer noch die besste Option.
Oder sein eigener Kanal - bleibt immer noch ein Microsoft-Mitarbeiter und ich glaube nicht, dass sie ihn Mist veröffentlichen lassen.

Ich weiß nicht mal direkt für was ich diese Sachen brauche oder was ich damit machen.

Da ist C#.NET eine gute Lösung, damit lässt sich sehr viel machen und Du legst dich nicht auf irgendeine Nische fest.

29.06.2021 - 12:37 Uhr

Habt ihr Empfehlungen wie ich vorangehen sollte..?

Beispiele ausprobieren, umschreiben, kaputt machen, wieder reparieren, etc. - solange, bis begreifst, warum welcher Fehler auftritt und die Korrektur funktioniert.
Du sollst das alles nicht auswendig lernen, sondern verstehen, dann kommt der Rest von alleine.
Wenn irgendetwas im Kurs nicht (genug) erklärt wird, gibt's noch das Internet, andere Bücher oder dieses Forum.

Entscheidend ist, dass Du das Gelernte mit anderen Dingen kombinieren und anwenden kannst, denn Du wirst nie genau diese Situation nochmal haben.

Ich hab hier schon auf der nach Kursen gesucht oder Büchern bis jetzt vergeblich

Dann haben wir den nächsten Punkt, den Du lernen musst: Recherchieren, denn Bücher werden ohne Ende empfohlen - auch hier.
Ich glaube aber nicht, dass es da eine Checkliste gibt, das lernst Du mit der Zeit. Also nicht einfach aufgeben, in den allermeisten Fällen wurde deine Frage schon beantwortet und deine Suche war einfach nicht gut genug.
In manchen Foren (auch hier) gibt's auch immer wieder Beispiele, wie man ewas effizient suchen kann, weil irgendwer etwas gefragt hat, was man mit einer klugen Suche nach wenigen Sekunden hätte lösen können 😁

By the way: Such auf Englisch, ist x-fach besser.

PS:
[FAQ] Wie finde ich den Einstieg in C#?
Das dort verlinkte Buch von Andreas Kühnel ist ziemlich gut (hab ich auch hinter mir), hat aber ein paar Jahre auf dem Buckel. Den Nachfolger ist von 2019, kostet aber Geld.
Wenn Du das Geld nicht hast, kannst Du auch beim Buch von 2012 bleiben. Die absoluten Grundlagen sind aber identisch, es sind nur neue Features dazu gekommen und die kann man sich leicht in der Doku anlesen. Bei den Frameworks (auch .NET) hat sich aber einiges getan, doch die Beispiele funktionieren trotzdem.

28.06.2021 - 19:27 Uhr

Dann zähle, wie oft es nicht funktioniert hat und ab einer bestimmten Anzahl zeigst Du einen Fehler an.

Dass sich alles aufhängt, kannst Du nur (sinnvolle) mit dem Task-based asynchronous pattern lösen.
Das solltest Du aber nicht "irgendwie" nutzen, die Lernhürde ist hoch und um es richtig nutzen zu können, musst Du es auch verstanden haben.

Ich persönlich würde es aber ganz anders machen.
Ich würde eher Verbindungsfehler entsprechend behandeln und anzeigen.
Der Nutzer kann weiterhin das Programm nutzen, bekommt bloß immer angezeigt, dass keine Verbindung notwendig ist.
Oder Du machst es wie z.B. Outlook und zeigst irgendwo im Programm an, dass es keine Verbindung gibt.

17.06.2021 - 10:44 Uhr

Ich schließe mich dem an: Blöde Idee 😁
Ihr baut euch nur Probleme, wie das dann aussieht, habe ich das letzte Jahr live und in Farbe bewundern dürfen/müssen.

Früher hatte ich eine Anwendung mit ClickOnce verteilt, klappt gut, hat bei uns aber aus irgendwelchen Gründen immer öfter für Probleme gesorgt, warum, das habe ich nie herausgefunden.
Stattdessen habe ich dann eine eigene Alternative basierend auf AutoUpdater.NET gebaut, da hatte ich dann auch mehr Möglichkeiten, was gerade für intern genutzte Anwendungen praktisch ist.

In beiden Fällen liegen die Versionen dann auf dem Server und die Clients laden sich dann immer den neusten Stand runter, geprüft wurde z.B. beim ersten Start des Tages
Bei ClickOnce konnte man dann auswählen, ob aktualisiert werden soll, was manche Mitarbeiter kategorisch abgelehnt haben.
Der Selbst-Bau war da flexibler, da konnte ich dann einstellen, ob's ein Pflicht-Update ist, oder nicht.

14.06.2021 - 13:54 Uhr

Führe die Events doch direkt in der Methode aus, oder gibt es einen konkreten Grund, warum Du das nicht machst?
Die Methode ist nicht nur wegen des awaits automatisch in einem neuen Thread, für die Methode gilt intern das gleiche, wie für das Event.
Den neuen Thread hast Du erst dann, wenn Du auch tatsächlich einen neuen Task auf machst, der dann einen neuen Thread bekommt.

Mach dir Mal in VisualStudio eine Watch auf auf die aktuelle ThreadID oder schreib dir ThreadID ins Debug-Output und verfolge sie.

Task mit Progress verwenden

Das Progress-System ist nur ein Konzept, Status-Änderungen abstrahiert an die UI weiterzugeben.
Du musst kein Progress nutzen, aber Du kannst. Nutzen würde ich es nur dann, wenn ich es auch brauche.

dass async void wie fire-and-forget zu betrachten ist

Der Aufruf von einer Methode mit async void ist wie fire-and-forget, die Methode selber nicht.


// EventHandler sind seltene Ausnahmen, wo async void in Ordnung ist:
async void OnMyEvent(object sender, EventArgs e)
{
    // UI-Thread
    await DoWorkAsync();
    // DoWorkAsync ist zuende
    // UI-Thread
}

async Task DoWorkAsync()
{
    // UI-Thread
    await Task.Delay(100);
    // Task.Delay ist zuende
    // UI-Thread
}

// Fast immer schlecht:
async void DoAsyncVoidWork()
{
    // UI-Thread
    await DoWorkAsync();
    // DoWorkAsync ist zuende
    // UI-Thread
}

void DoOtherWork()
{
    // UI-Thread
    DoAsyncVoidWork(); // Fire & Forget
    // await DoAsyncVoidWork(); // Compile-fehler
    // DoWorkAsync ist NICHT zuende
    // UI-Thread
}

Du solltest dich ausführlich mit Tasks beschäftigen.
Für kleine Projekte ok, solange Fehler nicht so schlimm sein, aber im produktiven Umfeld können die schnell zu schlimmen Problemen werden.
Die Einstiegshürde ist leider ziemlich groß, dafür machen sie einige Probleme bedeutend leichter, wenn man weiß, was man tut.

14.06.2021 - 13:21 Uhr

Das await ist im Hintergrund etwas anders, als Du denkst.
In deinem Fall wird das await vermutlich alles darauf Folgende in den UI-Thread zur Bearbeitung geben.
Das await kehrt dabei nicht fire-and-forget zurück! Dass kein Task zurückgegeben wird, heißt nur, dass der Methodenaufrufer nicht auf den Task warten kann, aber das ist bei Events legitim.

Das heißt, Du kannst nach dem Await normal ein Event aufrufen.

12.06.2021 - 18:20 Uhr

Für den Client kenne ich noch Refit - macht das alles sehr viel einfacher

Und als kleiner Hinweise:
ASP.NET != ASP.NET Core
Letzteres ist der Nachfolger
Viele alte Blogs oder Foren-Fragen gehen nur auf ASP.NET ein, daher ist es nützlich, den Unterschied zu kennen.
Der Link von T-Virus ist aber korrekt, obwohl da nur ASP.NET steht.

09.06.2021 - 21:32 Uhr

[...] da diese Experten nicht schreiben, was in die .xaml.cs und was in die .cs Datei kommt.

Niemand sagt dir, was Du in welche Datei schreiben sollst - wenn Du das willst, musst Du dir einen Mentor suchen, dem dein Lernerfolg egal ist und dir alles diktiert.
Es geht um Prinzipien und Konzept, die musst Du verstehen und wenn Du dich konsequent weigerst, dich darauf einzulassen, wirst Du nicht weiter kommen.
Wenn Du das verstehst, wird auch klar, was bei deinem Projekt wo hin muss.

Vergiss, was Du über WPF zu wissen glaubst und bau dir kleine Beispiel-Projekte, mit denen Du dann die Konzepte ausprobieren kannst.
Oder ignoriere weiterhin alle Ratschläge, hänge weiter in der Luft und verzweifle - das ist dann nämlich hoffnungslos.

Denn wie Abt schon sagt:
Alles, was Du suchst und vermisst, wird durch die Dinge gelöst, die Du dir nicht anschauen willst.
Und ja, ich schreibe bewusst "nicht anschauen willst", weil das mein Eindruck ist.
Diese Konzepte sind zugegeben nicht immer ganz einfach, aber wenn Du nach einem Tag und "Blick ich nicht durch" schon aufgibst, ist das mein Eindruck.

PS @Abt:
Sicher, dass die Links von Xamarin kommen sollten?

07.06.2021 - 20:00 Uhr

auch nicht auf dem pc?

[...] It is a self-contained, serverless, [...]

Nein, auch nicht auf dem PC.

aber warum steht da c# dahinter

Weil es erklärt, wie man es mit C# nutzt.

07.06.2021 - 19:54 Uhr

doch mit einem Create WPF anzufangen und die dll dann als Referenz hinzuzufügen.

Es werden dann bestimmt eine Menge Folgeprobleme entstehen. Das wichtigste dabei, dass mein auftraggebendes System diese Projekt dann auch noch als Script erkennt.

daher meine endlosen Eigenversuche

Es hilft manchmal, wenn Du nicht weiter kommst, nochmal von Vorne anzufangen.

Daher nochmal:
Lass WPF weg, fang mit einer ganz billigen DLL an, in der eine Klasse ist:

public static class MyTestClass
{
    public static string GetText()
    {
        return "Hello World!";
    }
}

Das rufst Du dann in deinem Skript auf - das ist das Minimum, das funktionieren muss.
Wenn das geht, lässt sich (da bin ich zuversichtlich) auch WPF zum Laufen bringen, oder WinForms, wenn dir das besser liegt.

PS:
Wobei dein Skript WPF verwendet, das ist also vermutlich eine Voraussetzung, damit das Skript funktioniert und WinForms wäre wieder eine zusätzliche Hürde, denn beides zusammen ...
Ich musste so ein WinForms-WPF-Misch-Projekt mal aufräumen - ich würde es nicht meinem schlimmsten Feind wünschen.

07.06.2021 - 19:22 Uhr

Ich bin eben kein Informatiker

Was macht dich denn zu einem Informatiker?
Bin ich ein Informatiker, weil ich das gelernt habe, oder war ich das schon in der Schule, weil ich C# gelernt habe?
Bist Du ein Informatiker, sobald Du mit C# arbeitest, oder brauchst Du dafür erst eine Ausbildung?

Meine Meinung:
Das ist völlig egal.
Jeder fängt bei 0 an, Informatiker hin oder her.
Die Ausrede zieht daher nicht 😉

Wenn Du Probleme hast, in die .NET-Welt einzusteigen, dann ist das oft ein Zeichen dafür, dass Du zu weit hinten anfängst und WPF ist ein hervorragendes Beispiel für "zu weit hinten".
Also tu dir einen Gefallen, arbeite dich in die Grundlagen ein und arbeite dich dann Stück für Stück zu WPF vor.
Oder Du beharrst auf deinem Vorgehen, versuchst weterhin irgendwelche Frickellösungen zu bauen und wirst weiterhin so Probleme haben, wie jetzt.

Deshalb bin ich doch kein "schlechter" Mensch!

Nein, hat auch niemand behauptet, Abt hat bloß ein Talent dafür, besonders hart ehrlich zu sein (sorry 😄)
Aber auch wenn Du kein "schlechter" Mensch bist - diese Sichtweise ist (und das geht nicht weniger hart) einfach dumm.
Du vergeudest sehr viel Zeit für haubtsächlich selbst gebaute Probleme und bemerkst dabei nicht, dass der auf den ersten Blick längere Weg (vorne anfangen und lernen) sehr viel kürzer, leichter und vor allem nervenschonender ist. Also der blumenbewachsene Weg, der dann plötzlich über ein Hochgebirge führt, während der Andere mit einem steilen Hügel beginnt und danach durch schöne Graslandschaften führt.

Also nimm dir das zu Herzen, vergiss, was Du bisher gelernt hast und fange vorne an.
Das dauert etwas, aber mit der Zeit verstehst Du, warum wir so sehr darauf beharren und vielleicht auch, wie Du viele Dinge besser lösen kannst.
Und wenn Du einen konkreten Punkt z.B. in deinem Buch nicht verstehst, kannst Du dich nochmal hier melden und ich denke, dann ist Abt auch etwas gnädiger 😉

und wie bekomme ich es "an meine cs-Datei angekoppelt"!

Das hat nichts mit WPF zu tun, sondern hauptsächlich mit diesen Skript-Kram. Eine Referenz in VisualStudio hinzuzufügen ist super einfach, in deinem Fall musst Du vermutlich das Programm dazu bringen, das zu machen. Geht das nicht, gibt's noch andere Wege, aber die sind komplexer und meist ziemlich unschön, das sollte also nur die allerletzte Lösung sein.
Was Du also brauchst, ist, wie Du eine selbst entwickelte DLL als Referenz hinzufügen kannst. Wie das geht, kann dir aber niemand sagen, das muss das Programm tun. Fang mit etwas ganz kleinem an (Methode gibt "Hello World" zurück - reicht) und versuche das irgendwie ins Script zu bekommen.
Danach sehen wir weiter.

07.06.2021 - 18:54 Uhr

Hast Du eine Projekt-Datei, oder nur die cs-Datei?
Oder kannst Du eigene DLLs als Referenzen hinzufügen?

Dann kannst Du nämlich ein ganz normales C#-WPF-Projekt (DLL, nicht zum Starten) erstellen, dort das implementieren, was Du brauchst, das Ergebnis als Referenz hinzufügen und das komische Skript fügt das nur noch zum Window hinzu.

Der letzte Teil (das zum Window hinzufügen) ist ziemlich einfach:


window.Content = new MyScriptControl();
window.DataContext = new MyScriptViewModel(context);
window.ShowDialog(); // Wenn das nicht automatisch passiert

Hängt natürlich noch davon ab, was ich von dem Skript-Kram jetzt nicht weiß, aber ziemlich sicher lässt sich der Großteil sehr leicht davon trennen und als klassisches WPF-Projekt aufbauen.
Wenn Du die Referenzen nicht in das WPF-Projekt rüber bekommst, musst Du eine Abstraktion schaffen, könnte dann so aussehen:


// Im WPF-Projekt
public interface IScriptContext
{
    // ...
}

// Im Script-Projekt
internal class ScriptContextFacade : IScriptContext
{
    private readonly ScriptContext _realContext;

    public ScriptContextFacade(ScriptContext realContext)
    {
        _realContext = realContext;
    }

    // ...
}

public class Script
{
    public void Execute(ScriptContext context, Window window)
    {
        window.Content = new MyScriptControl();
        window.DataContext = new MyScriptViewModel(ScriptContextFacade(context));
        window.ShowDialog(); // Wenn das nicht automatisch passiert
    }
}

Wie gut das funktioniert, hängt natürlich auch davon ab, was das da ist, aber ich wette mit dir: Das geht 😉

Und als Tipp für die Zukunft:
Erwähne direkt am Anfang den Elefanten namens "Skript-Gefrickel", was Du da nutzen musst und reduziere dein Beispiel auf das eigentliche Problem.
So wirkt das eben sehr danach, dass Du noch einer der vielen "Ich will in zwei Tagen WPF-Apps programmieren, bitte erklärt mir alles"-Leute, auch wenn Du das gar nicht bist.

In der Vergangenheit habe ich sogar ein Projekt gemacht, wo ich eine Grafik als Window über einen HTML String mit Java Einfügung programmiert habe, nur um das für mich obscure WPF zu umgehen

Und das klingt sehr danach, dass Du scheinbar genau die Probleme hattest, die Abt prophezeit hat:
Du hast falsch angefangen, irgendwie versucht, damit klarzukommen und bist (nachvollziehbar) daran verzweifelt.
Dabei kann WPF sehr viel Spaß machen, aber man braucht den Einstieg und mit der Einstellung wird dir das nie gelingen - und dein Projekt auch nicht.

07.06.2021 - 18:04 Uhr

Ja.

Wie genau, das hängt von ungefähr allem ab, von dem Du uns aber nichts mitteilst.

07.06.2021 - 10:35 Uhr

Das geht nicht dynamisch, irgendwo brauchst Du einen gemeinsamen Stand, anhand dessen Du weißt, wie Du XY aufrufen kannst.
Hast Du diesen gemeinsamen Stand nicht, musst Du wohl oder übel alles einzeln machen.
Oder Du rätst, was es für Parameter gibt, wirst dann aber ziemlich sicher irgendwann den Fall haben, dass Du die richtigen Parameter nicht errätst.

Deshalb sollte man das auch umdrehen:
Nicht dein Programm muss die externe DLL und die Parameter der Methoden kennen, sondern die externe DLL kennt dein Programm.
Dein Programm liefert dafür dann eine Schnittstelle, die die externe DLL nutzt - das ist dein gemeinsamer Stand.

Du könntest also ein Interface definieren, wo die Methode, die Du brauchst, definiert ist und als Parameter wird alles übergeben, was Du bieten kannst/darfst.
Die externe DLL muss dann dieses Interface implementieren und kann dann die Daten so weiter reichen, wie es für die jeweilige Version richtig ist.
Diese Implementierung findest Du dann via Reflection oder einer Art Registrierungs-Interface, was aber auch wieder implementiert und irgendwie gefunden werden muss.
Dein Programm bekommt dann die Implementierung und hat ein eindeutiges Interface zum Aufrufen.

Bei externen DLLs geht das natürlich nicht so einfach, da brauchst Du denn einen Vermittler dazwischen - je Version.
Dieser Vermittler implementiert dein Interface und ruft darin die externe Version auf, der muss dann immer angepasst werden, dein Haupt-Programm bleibt dabei aber unberührt.
Der Vermittler kann in C# oder C++/CLI geschrieben werden, je nachdem, was einfacher ist, hauptsache es kommt eine managed DLL bei raus.
Du kannst natürlich auch unmanaged arbeiten, kannst dann aber nicht mit .NET-Interfaces arbeiten und musst irgendwie anders die native Methode definieren, die dein Programm aufruft.

Der Nachteil ist, dass Du je Version eine zusätzliche DLL brauchst, aber je nach Umfang der externen Methoden ist das nur sehr wenig Arbeit.
Außerdem sehe ich das nicht als Nachteil, sondern als Vorteil, denn Du hast einen einzigen Punkt, den Du anpassen musst und gefährdest dein restliches Programm nicht.
Die könnte man z.B. sogar zur Laufzeit austauschen oder als einzige DLL raus geben, ohne das ganze Programm aktualisieren zu müssen.

01.06.2021 - 00:21 Uhr

Warum mischst Du jetzt C# mit PHP?
Ich persönlich halte PHP in der heutigen Zeit für ein grausiges Chaos, das ich möglichst vermeiden würde 😁
Und selbst wenn ich es gut fände - ich würde keine zwei Welten mischen, wenn es sich doch eigentlich sehr gut nur mit C# machen lässt.

Dir wurden schon alle notwendigen Technologien genannt: ASP.NET Core und Refit, beides kannst Du mit C# nutzen.

Die Doku von Microsoft zu ASP.NET Core (von Abt verlinkt) ist ziemlich gut, leider bringt dieses Framework auch einige Konzepte mit, die für einen Einsteiger etwas schwierig zu verstehen sind. Dennoch solltest Du das weiter verfolgen, nicht nur, weil dir das einiges an Arbeit ersparen kann, sondern weil Du so gleichzeitig einige Dinge aus dem riesigen Themengebiet Softwarearchitektur und Designpatterns lernst - also das, was Du in deinem Buch vermisst hast und das darin garantiert jeden Rahmen gesprengt hätte. Wie gut das für einen blutigen Einsteiger geeignet ist, kann ich nicht sagen, aber Du hast auf jeden Fall eine Grundlage, an der Du dich entlang hangeln und ggf. fehlendes Wissen anderswo auffrischen kannst.

Und Refit macht den zusätzlichen Aufwand der Gegenseite (deine WinForms-Anwendung) sehr einfach, jedenfalls kenne ich keinen einfacheren Weg, sowas zu machen.

Also nochmal grob umrissen:
Du brauchst eigentlich zwei Programme: Die Server- und die Client-Anwendung, daher auch "Server-Client-Architektur".

Die Server-Anwendung läuft auf irgendeinem gemieteten Server und ist im Internet oder Intranet erreichbar.
Sie darf als einziges Programm mit der Datenbank arbeiten und kann (je nachdem, wie Du das aufbaust) auch einiges an Logik übernehmen, dann muss das nicht in der Client-Anwendung entwickelt werden.
Die Server-Anwendung wird mit ASP.NET Core entwickelt.

Die Client-Anwendung läuft auf den PCs, wo Du dein Programm laufen lassen willst und brauchen eine Verbindung zu dem Server.
Dieses Programm darf nicht mit der Datenbank sprechen, sondern "fragt" die Server-Anwendung, ob sie bestimmte Daten herausgeben oder ändern kann, dafür schickt sie normale HTTP-Anfragen zum Server, Inhalte erden über die URL oder als Inhalt von Form von JSON übermittelt. Die Server-Anwendung prüft diese Anfrage (kann ggf. ablehnen, wenn was falsch ist) und erfüllt ggf. die Aufgabe. Die Client-Anwendung zeigt dann nur noch die Ergebnisse an.
Die Client-Anwendung ist deine WinForms-Anwendung und kommuniziert mit der Server-Anwendung, Refit vereinfacht das enorm.
Auf die Registry zugreifen geht auch, ist ja ein normales WinForms-Programm auf Windows-PCs, die Informationen musst Du dann mit zum Server schicken.

Ach ja:
Nutze bitte eine Quellcodeverwaltung - falls Du es noch nicht tust.
Das hast Du dir mit wenig Arbeit eingerichtet, kannst dir leicht verschiedene Stände pflegen und verlierst nichts bei deinen Experimenten oder z.B. einem Hardware-Problem.

Und lass PHP aus dem Spiel.
Wenn Du das so machst, wie gezeigt, musst Du praktisch alles selber machen.
Oder Du verwendest fertige Frameworks, die es sicher auch für PHP gibt, dann stehst Du aber wieder vor dem Problem, dich in großes Framework einarbeiten zu müssen - noch zusätzlich zur anderen Programmiersprache mit einem ganz anderen Ökosystem drum herum.
Dann kannst Du auch gleich ASP.NET Core lernen.

29.05.2021 - 00:47 Uhr

So ein CI könnte man natürlich auch schnell mit einem Batch-Script implementieren: Pullen/Auschecken, Compilieren, VSTest-Ausführen, Dateien-Kopieren und Setups builden...

Passend dazu eine kleine top aktuelle Geschichte von mir:
Ich hab die letzte halbe Woche mit einem System, das sich "Build-Server" schimpft, gekämpft.
Dieses System hat (so mein Eindruck) genau so begonnen, wie Du beschreibst: Ein simples Script, das Kompiliert und das Ergebnis bereitstellt (keine UnitTests und kein Setup)
Zu diesen simplen Anforderungen sind immer mehr dazu gekommen und das Script wurde immer größer, bis daraus eine interne Website (PHP und CMD) wurde, die man mit Script-Fragmenten in XML "konfiguriert" wird, damit es ein CMD-Script zusammen baut und ausführt. Und dieses Build-System ist voll mit Fallstricken und Bugs, um die bisher irgendwie drum herum gebastelt wurde, weil sich keiner an den Code heran traut.

Ein Kollege hat vorher schon wochenlang damit gekämpft und viel von den Erkentnissen an mich weiter gegeben, nur deshalb habe ich "nur" eine halbe Woche für meine Anpassung gebraucht.

Um nochmal auf deine Aussage zurückzukommen:

So ein CI könnte man natürlich auch schnell mit einem Batch-Script implementieren: Pullen/Auschecken, Compilieren, VSTest-Ausführen, Dateien-Kopieren und Setups builden...

Ja, kann man so machen, wird dann halt scheiße 😁
Oder Du nimmst dir ein/zwei Tage Zeit, arbeitest dich in die Thematik ein und testest, was geht.

27.05.2021 - 19:59 Uhr

Durch unser Geschäftsmodell müssen wir jedoch ständig in der Lage sein, sehr schnell auf gewisse Kundenanforderungen zu reagieren - das zeichnet uns aus und davon leben wir.

Das habe ich fast so oft gehört, wie die Anzahl Firmen, bei denen ich bisher gearbeitet habe 😁
Sie alle hatten Chaos und auf die Frage, warum sie dieses Chaos nicht aufräumen konnten, kam dann die immer-gleiche Aussage (oder Ausrede), dass man schnell und handlungsfähig bleiben muss.

Das geht aber nur so lange gut, bis eben dieses Chaos besagte Handlungsfähigkeit so sehr einschränkt, dass es geschäftsschädigend wird und wenn entscheidende Personen erst dann mit sich reden lassen, ist es oft schon zu spät.
Wenn Du die Zeit und Muße hast, kannst Du ja mal eine Zeit- und Kostenplanung erstellen. Oft ist einem Chef ohne entsprechendes Wissen gar nicht bewusst, welche Probleme manche Entscheidungen entstehen lassen, so eine Zeit- und Kostenplanung kann da aber helfen.

Und bezüglich Zwischen-Schicht:

Ich habe mal in einer Firma gearbeitet, die einen ähnlichen Plan verfolgt hat.
Ein sehr altes und sehr großes C-Projekt, nicht mehr wirklich wartbar, aber man muss ja schnell und handlungsfähig bleiben.
Der Plan war also, das Neuerungen in C# umgesetzt werden, das C
-Projekt nutzt dann die Exe, gibt Daten rein, bekommt Daten raus, etc.
Der Plan war (vermutlich) mehr und mehr den C-Code zu verschlanken und Stück für Stück auf eine modulare C#-Basis hinzuarbeiten und auch von neuen Technologien profitieren zu können.
Das Ergebnis nach über 10 Jahren war, dass das C
-Programm viel von seinem Chaos (z.B. irre Datei-Formate) auf die C#-Projekte übertragen hat und die meisten der C#-Projekte jetzt an einem ähnlichen Punkt stehen, wie das C++-Programm.

Ich will damit auf den ewigen Grundsatz hinaus: Provisorien halten am längsten.
(Und ja, ich weiß, dass das wirklich nicht vergleichbar ist)

Wie auch immer...
Wenn für euch nur diese Herangehensweise in Frage kommt, dann macht das so.
Achte aber darauf, dass sich das nicht im Sand verläuft und Ihr nicht in der gleichen Falle landet, wie mein Beispiel.

23.05.2021 - 11:23 Uhr

This is the official YouTube channel for the .NET team at Microsoft.

😉

22.05.2021 - 21:53 Uhr

Wenn ihr nicht zwei Stände pflegen wollt, einmal für .NET Framework und eimal für aktuelle .NET Version, dann habt ihr keine große Wahl als auf .NET Standard zu setzen.

Meine Worte 🙂
Das setzt aber auch voraus, dass man ein Feature-Stop durchsetzt - zumindest solange die einzelnen Umbauten durch sind.
Steht der Zwischenstand, kann man wieder ein paar Features einbauen und macht danach mit dem Umbau weiter.

Abgesehen davon lassen auch die Kunden sich von so einem Feature-Stop überreden, man braucht aber jemanden, der das gut rüber bringen kann.
Ein Firma, bei der ich mal gearbeitet habe, hat genau das gemacht - fünf Jahre lang. In der Zeit haben sie die Software größtenteils neu entwickelt und an der alten Software nur Bugfixes umgesetzt.
Die Kunden waren natürlich nicht glücklich, doch auch die verstehen irgendwann, dass eine zig Jahre andauernde OP am offenen Herzen irgendwann mal pausiert werden muss.

22.05.2021 - 15:13 Uhr

Wenn die Implementierungen wirklich so unabhängig voneinander sind, wie es klingt, würde ich eigene Assemblies daraus machen.
Eine Datenbank-Schicht = eine Assembly und alles, was intern genutzt wird, ist internal.
Das macht mMn. auch den Überblick deutlich leichter.

22.05.2021 - 14:32 Uhr

Naja, Du kannst es insofern steuern, indem Du die Inner-Classes protected abstract machst.
Die Outer-Class will dann auf irgendeine Weise eine Instanz davon und der Entwickler stellt fest, dass er keine andere Möglichkeit hat, als von dieser Inner-Class abzuleiten und auslagern als normale Klasse geht auch nicht, weil protected.

Aber ich sehe das ähnlich:
Dem liegt vermutlich ein Architektur-Problem zugrunde.
Und ich frage mich auch: Warum nicht einfach ein Interface neben die Klasse legen?

22.05.2021 - 14:29 Uhr

Ich meine nicht, dass Ihr von jetzt auf gleich auf C# umsteigt, sondern dass Ihr "nur" die IDE wechselt.
Da wäre natürlich spannend, was Gupta-spezifisch ist und nicht übernommen werden kann, doch bei C++ oder C# sollte Visual Studio (Code) durchaus realistisch sein.
Über einen Wechsel der Programmiersprache kann man danach ja immer noch nachdenken, wenn der eine Klotz erst mal weg ist.

Allerdings dürfen wir unsere Kunden, die weiterhin das System nutzen, nicht vergessen – für Sie müssen wir parallel weiterhin das System pflegen und zum Teil weiterentwickeln.

Das wäre ich strikt trennen. Wartung und kritische Bugfixes ok, aber wenn neue Features rein sollen, baut ihr euch nur riesige Probleme.
Wie Abt schon schrieb, ist eine Misch-Variante nicht möglich, es bleibt also nur die Möglichkeit, jede Änderung in beide Versionen (mit und ohne Gupta) zu pflegen, aber spätestens wenn Ihr auch noch strukturell umbaut, wird das zur Hölle.

Ich würde es schrittweise machen:

Erst Gupta absägen und möglichst wenig am Code selber ändern.
Danach zieht Ihr alles was geht auf .NET 4.8 oder wenigstens 4.6.1 hoch, so "gefangen" seid Ihr damit gar nicht, das unterstützt nämlich ab .NET Standard 2.0.
Steht diese Grundlage, könnt Ihr mit .NET Standard 2.0 weiter arbeiten, die meisten aktuellen Frameworks unterstützen das und es kann ab .NET 4.6.1 oder Core 2.0 verwendet werden.
Steht erst mal diese Grundlage, könnt Ihr Stück für Stück alles .NET 4.x auf .NET Standard 2.0 portieren und habt so eine gute Grundlage, die auch in .NET 5 verwendet werden kann.
Ist alles .NET Standard 2.0 (geht ja nur bei DLLs) zieht Ihr die Start-Projekte auf .NET 5 hoch. Der Umstieg der .NET Standard 2.0 DLLs auf .NET 5 ist nicht zwingend notwendig, aber in vielen Fällen auch einfach nur eine Property in der csproj.

Zwischen den Schritten ist dann Zeit für neue Features, aber mitten im Umbau würde ich die Weiterentwicklung pausieren.

Lidl kehrte zur Gupta zurück. Also auch mit einem großen Budget ist so ein Vorhaben nicht ganz einfach.

Eine ähnliche Geschichte erzählt man mir auch immer wieder, nur dass es da um die Neuentwicklung einer ganzen Software geht und zig Millionen rein gesteckt wurden.
Für mich klingt das so, als wäre ein großes Budget nicht nur kein Hinweis, dass es gelingt, sondern eher ein Problem, da man sich von dem Projekt erhofft, dass man alles auf einen schlag schafft - Geld ist ja da.
Spoiler: Das geht schief. Nicht alles auf einmal versuchen, lieber in einzelnen Schritten planen, das dauert dann vielleicht etwas länger, aber die Chance, dass es funktioniert, ist größer.

21.05.2021 - 19:30 Uhr

Wenn Ihr den Wartungsvertrag kündigt - warum dann daran fest halten?
Warum nicht auf Visual Studio oder Visual Studio Code oder Andere setzen?

Mir ist nicht ganz klar, wo Du nun was nutzen möchtest, aber als mögliche Brücke gäbe es noch .NET Standard.
Das sollte man zwar auch nicht mehr nutzen, aber es funktioniert ganz hervorragend als gemeinsame Code-Basis für .NET Framework 4.x oder .NET 5 Projekte.

Aber so oder so kannst Du Projekte, die dieses Gupta nicht unterstützt, nicht einfach so einbinden - kompilieren und dann als normale Referenz verwenden sollte aber gehen.

20.05.2021 - 19:50 Uhr

@MrSparkle:

Ich bin mir nicht ganz sicher, ob das hier rein gehört, oder lieber ins Kritik-Forum, aber dein Artikel scheint etwas zerpflückt 🙂
Beim Zitieren bekomme ich auch nicht das, was ich aus dem Forum so kenne, sondern scheinbar den ganzen HTML-Code des Beitrags.

Sieht aus, als hast Du hier Funktionen genutzt, die das neue Forum (noch) nicht unterstützt?
Außerdem gibt's die Bilder nicht mehr.

Kannst Du das korrigieren?

20.05.2021 - 15:10 Uhr

Lernen finde ich immer gut. Und hier handelt es sich um ein kostenloses Angebot.

Gegen Lernen hab ich nichts, ich habe was gegen viele Tutorials, aus ungefähr den gleichen Gründen, die Abt auch genannt hat.
Und bezüglich kostenlos: Rheinwerk Computing :: Visual C# 2012 Das ist auch noch ziemlich gut 😉

20.05.2021 - 13:28 Uhr

Ich weiß nicht, wie es den Anderen geht, aber mit Programmier-Tutorials erntest Du bei mir nur Ablehnung.
Ich bin ein Fan von guten Büchern, der MSDN-Doku (bei C#) oder Blogs, die sich dann aber je Beitrag ein Thema raus picken und das ausführlich behandeln.
Das was Du machst, gibt's so schon 100e mal auf YouTube und ich persönlich habe bisher noch kein gutes C#-Tutorial gesehen.

Abgesehen davon gibt's eine Regel gegen Werbung - müssen die Admins entscheiden, ob das hier ein Verstoß ist.

16.05.2021 - 23:05 Uhr

Deine zwei Arrays sind eigentlich auch sinnlos, die sind ein Paradebeispiel für Spaghetticode, den man sehr leicht mit einer for-Schleife lösen kann.
Schreib einfach in die for-Schleife:

string strZusatz = "_x015id02xa1_Job" + (i + 1) + "_*.*";
// oder 
string strZusatz = $"_x015id02xa1_Job{i + 1}_*.*";

var strDummy = "15id02_Job" + (i + 1) + "_Dummy.txt";
// oder 
var strDummy = $"15id02_Job{i + 1}_Dummy.txt";

Dann nutzt Du die zwei Variablen anstelle des Arrays und gut ist.
Das jeweils zweite Beispiel nennt sich "String Interpolation" und wird vom Compiler in einen string.Format-Aufruf umgebaut.

Und Path.Combine setzt dir den Pfad aus einzelnen Elementen (Ordner/Datei) zusammen, z.B. den Datei-Namen kannst Du damit nicht zusammenbauen.
Aber die Klasse bietet noch andere nützliche Methoden, z.B. für die Arbeit mit den Datei-Erweiterungen.
Es lohnt, immer mal wieder in die Doku zu schauen, was eine Klasse noch so alles kann.

16.05.2021 - 16:27 Uhr

Du solltest Path.Combine nutzen, um Teil-Pfade zusammenzusetzen.
Und bedenke, dass File.CreateText (und andere ähnliche Methoden auch) die Datei öffnet, aber nicht schließt, das musst Du selber machen.

Und dein Fehler liegt nicht bei der for-Schleife, dennoch:

for (int i = 0; i == strZusatz.Length; i++)
{
    // ...
}

Schau dir mal die Bedingung an, macht die so viel Sinn?
Siehe: for (C#-Referenz)

Dein Fehler liegt bei der strDummy-Variable.
Bei C# und den meisten anderen Sprachen auch) fangen die Indizes immer bei 0 an.
Du fängst bei 1 an. Bei strZusatz hast Du's richtig gemacht.

Ach ja:
Mit der ungarischen Notation machst Du dir nicht gerade Freunde 😉
Bleib beim camelCase bzw. PascalCase, je nach Situation.

09.05.2021 - 15:19 Uhr

Die zwei von dir genannten Beispiele sind problemlos mittels DataTrigger (die vom Style) lösbar, bei einfachen Fällen brauchst Du dann auch keine IsXYZ-Properties.
Du definierst dann ein Binding auf irgendeine Property, einen Wert, bei dem der Trigger gelten soll und die Style-Setter, die definieren, welche Werte sich dann ändern sollen. Wenn der Trigger nicht gilt, greifen die alten Werte, die kannst Du normal im Style setzen.

Überleg dir aber gut, ob Du die IsXYZ-Properties nutzen willst.
Sie können den Code schnell größer machen, besonders wenn Du für alle möglichen Fälle eine IsXYZ-Property brauchst.
Diese IsXYZ-Properties können aber auch ein Vorteil sein, da es damit bedeutend leichter ist, Änderungen umzusetzen, besonders wenn nicht mehr nur einfache Wert-Vergleiche notwendig sind.

Oder Du greifst auf Converter zurück, wie der Name schon sagt, convertieren die einen Wert in einen Anderen.
Die sind eine einfache Klasse, die IValueConverter implementiert, eine Instanz davon kann dann im Binding gesetzt werden.
Ich habe z.B. immer einen BoolToVisibilityConverter, den ich für genau solche Fälle nutze.

09.05.2021 - 14:17 Uhr

Woher soll das Binding denn von den privaten Variablen wissen?
Wie gesagt: Das Binding braucht eine Source, die kannst Du am Binding direkt (ElementName, Source, RelativeSource) mit geben, oder Du gibst keine Source an, brauchst dann aber einen DataContext..

Du kannst auch deine eigene DependencyProperty nutzen, allerdings musst Du das dann auf den DataContext umleiten (würde ich nicht machen, führt nur zu Chaos) oder Du verwendest überall diese DependencyProperty als Source, dann funktioniert es.
Besser wäre aber, Du nimmst den DataContext und castest darauf, die View ist sowieso fest davon abhängig, dann stört das Casting auch nicht mehr.

Viel spannender finde ich aber die Frage, wozu Du überhaupt aus dem CodeBehind darauf zugreifen willst, das ist meistens ein Hinweis auf falsch umgesetztes und/oder nicht verstandenes MVVM.
Logik (außer reine UI-Logik) gehört nicht in die View, dafür ist das ViewModel da. Aufgerufen wird besagte Logik im ViewModel mit Hilfe von Commands.
und die reine UI-Logik kann man meistens in Form von AttachedDependencyProperties, Triggern, Behaviors, etc. auslagern.
Reine UI-Logik lagere ich nur dann nicht aus, wenn diese UI-Logik zu aufwändig auszulagern oder sehr spezifisch für das Control ist und sonst nirgendwo benötigt wird.

Für mich klingt das ein bisschen so, als würdest Du Erfahrung von WinForms mit bringen, oder von einem ähnlichen Framework von einer anderen Sprache.
MVVM war mit WPF ganz neu, das macht den Einstieg etwas schwieriger, weil man viele Erfahrungen nicht anwenden kann.

09.05.2021 - 13:23 Uhr

Das ViewModel:*Die Self-Property kann weg, die ist Mist, genauso auch das PropertyChanged dafür. *Die beiden anderen Properties rufen sich selbst auf, ergo: StackOverflowException. Du brauchst private Variablen. *Und anstelle der Property-Namen als string kannst Du auch einfach nameof(DataSource) nutzen, dann sagt dir der Compiler, wenn Du dich verschrieben hast. *Die Implementierung vom NotifyPropertyChanged sollte man so nicht machen. Entweder Du legst das vorher als Variable ab, oder Du nutzt PropertyChanged?.Invoke(...) - ich tendiere zu Letzterem.

Das Control:*Die DependencyProperty kann komplett weg, für das, was Du damit erreichen möchtest, nimmt man den DataContext. Oder Du definierst je Wert eine DependencyProperty, dann bist Du flexibler, hast aber auch mehr Arbeit. Wenn es an vielen verschiedenen Stellen genutzt werden soll, würde ich zu letzterem tendieren, ansonsten tut's auch der DataContext.

Das XAML:*Was soll "_editTag" sein, wo ist das definiert? Ein DataBinding braucht eine Source, die kann man angeben, tut man das nicht, ist der DataContext der Default. Überlege dir also, was Du für einen DataContext hast, im Binding kannst Du dann Properties aus dieser Klasse nutzen. *Das "Mode=TwoWay" brauchst Du meines Wissens nach nicht, bei veränderlichen DependencyProperties (z.B. TextBox.Text) ist das der Default.

Könnte so aussehen:

public class Tag : INotifyPropertyChanged
{
    private string _itemAsString;

    public event PropertyChangedEventHandler PropertyChanged;

    public string ItemAsString
    {
        get { return _itemAsString; }
        set
        {
            if (_itemAsString != value)
            {
                _itemAsString= value;
                NotifyPropertyChanged(nameof(ItemAsString));
            }
        }
    }

    protected void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        // Ist die Kurzform von:

        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));

        // Der Grund für die Zwischenvariable ist, dass sich zwischen der Prüfung und dem Aufruf der Inhalt des Events ändern kann
        // Mit der Zwischenvariable ziehst Du sozusagen einen Zwischenstand, der sich danach nicht mehr ändert.
        // Oder Du nutzt den ?.-Operator, der macht das automatisch.
    }
}
<userctrl:TagAdressBox DataContext="{Binding DiePropertyWoDieTagInstantHerKommt}" Item="{Binding ItemAsString}" />

Der Code geht davon aus, dass Du für die einzelnen Inhalte je eine DependencyProperty hast, einfach, weil's besser als Beispiel passt.
Hast Du das nicht, dann muss das Control intern selber die Werte binden und davon ausgehen, dass der DataContext eine gültige Instanz hat.

09.05.2021 - 11:29 Uhr

Dann wirfst Du scheinbar das Event nicht richtig.
So ganz ohne Code lässt sich dazu aber nichts sagen.

09.05.2021 - 11:21 Uhr

Wenn das Control über Änderungen einer Property informiert werden soll, muss die Klasse mit dieser Property INotifyPropertyChanged implementieren und das Event entsprechend werfen.
Das kannst Du beliebig weit verschachteln.

Oder worauf willst Du hinaus?

08.05.2021 - 22:08 Uhr

Komplett ohne Code-Behind auszukommen. Nur habe ich bisher noch keine Lösungen gefunden.

Das funktioniert mit AttachedProperties oder Behaviors.
Oder Du nutzt Trigger, allerdings gibt's davon drei Varianten: Am Control, Style oder als AttachedProperty aus dem Interactivity-Framework.

StackPanel_MouseDown ermöglicht es wieder, nach einem WindowStyle="None", das Window wieder verschieben zu können

Du willst also ein eigenes Window designen, was es dir ermöglicht, ein Template für z.B. die Kopf-Zeile festzulegen. Für sowas würde ich ein CustomControl nutzen, das von Window ableitet. Außerdem hast Du so den Code, der für das selbst designte Window notwendig ist, getrennt vom Rest.

Wenn Du das alles getrennt hast, kannst Du meiner Meinung nach auch CodeBehind nutzen.
MVVM verbietet das ja nicht, es besagt nur, dass Du keinerlei BusinessLogik in der View implementieren sollst.
CodeBehind ist in Ordnung, solange es sich dabei nur um reine View-Logik dreht, also z.B. das Bewegen des Fensters.

Bedenke aber, dass es bereits Frameworks gibt, die sowas schon anbietet, MahApps zum Beispiel 😉
MahApps kann auch noch sehr viel mehr, wenn das Metro-Design für dich eine Option ist, dann würde ich das nutzen.

Wenn es wirklich nicht anders geht, greife ich selbstverständlich auf externe Libs zurück

Es geht nicht darum, ohne Frameworks auszukommen, sondern dass man von der sehr viel umfangreicheren Erfahrung profitieren kann, außerdem sind sie oft sehr ausführlich getestet.
Wenn ich z.B. nur an einer einzelnen Stelle ein kleines JSON mit ein/zwei Properties brauche, würde ich vermutlich auch nur den String zusammen bauen, aber sobald es ein größeres JSON oder dynamisch wird, wird ein Framework genutzt.

Ich verwende z.B. meistens (zumindest wenn möglich) Microsoft.Extension.DependencyInjection, Microsoft.Extension.Configuration, Microsoft.Extension.Logging, etc.
Diese Frameworks brauche ich zwar nicht zwingend, sie erleichtern mir allerdings die Arbeit und liefern eine einheitliche Herangehensweise an häufige Probleme.

08.05.2021 - 01:54 Uhr

Vorweg:

ja, verstehe Deinen Grund der langen Texte.
In Foren schreckt das jedoch die meisten Helfer ab.

Ich bin bei dem Thema ganz sicher nicht fehlerfrei, aber diesmal aus der anderen Sichtweise: Mich hat's abgeschreckt 😁
Ganz besonders, dass ich erst nach ein paar Absätzen das "So, nun zu meinem Anliegen" und wieder ein paar Absätze später das "Also, was ich erreichen möchte" gefunden habe ...
Ich beziehe mich daher nur auf das, was ich danach gelesen habe, sorry 😁

TabControl:
Das TabControl kann sowohl dynamisch als auch statisch.
Für dynamisch sind die Properties ItemsControl, ItemTemplate und ContentTemplate.
Für statisch kanst Du einfach TabItems in XAML definieren.
Grob im Browser getippt:


public class MyTabViewModel
{
    public ObservableCollection<MyTabItemViewModel> MyTabs { get; }
}


<TabControl ItemsControl="{Binding MyTabs}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding TitleText}" />
        <DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ContentText}" />
        <DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

Damit könntest Du zur Laufzeit Tabs hinzufügen und entfernen, WPF reagiert dann live.
Wenn das nicht notwendig ist, reicht aber auch der einfachere Weg:


public class MyTabViewModel
{
    public MyTabItem1ViewModel MyTab1 { get; }
    public MyTabItem2ViewModel MyTab2 { get; }
}


<TabControl>
    <TabItem Header="{Binding MyTab1.Title}">
        <TextBlock Text="{Binding MyTab1.Content}" />
    </TabItem>
    <TabItem Header="{Binding MyTab2.Title}">
        <TextBlock Text="{Binding MyTab2.Content}" />
    </TabItem>
</TabControl>

OnContentRendered & StackPanel_MouseDown:
Für Beides gibt es meines Wissens nach keine direkte Lösung mittels Commands, das Prinzip funktioniert aber trotzdem, man sich bloß etwas dazu bauen.
Wie man das macht, dafür gibt's viele Wege, z.B.: AttachedProperties, Trigger (Interactivity) oder Behaviors (auch Interactivity).
Oder Du nutzt weiter CodeBehind, das ist mit MVVM nicht verboten, nur solltest Du darauf achten, dass Du die Logik, die da nichts verloren hast, konsequent verbannst.
Besser wäre natürlich, Du verzichtest ganz auf CodeBehind, in den allermeisten Fällen geht das und Du kommst nicht auf die idee, irgendwelche Logik hineinzuschreiben.

Das ist jetzt vielleicht etwas viel, ich hab es bewusst nicht ausführlicher erklärt, um nicht gegen meine eigene Kritik zu verstoßen, außerdem gibt's massig Anleitungen online.
Lies aber lieber ein/zwei Artikel mehr und überlege, ob das Beschriebene wirklich zu MVVM passt, denn leider sind so einige Posts/Tutorials nicht gut geschrieben...

Bei beiden Methoden frage ich mich aber auch: wozu?
Das Bewegen eines Fensters verbieten ... ich hasse es! Und wahrscheinlich lässt sich das gleiche Ziel auch viel besser erreichen.
Genauso, dass Du Dinge erst nach dem Rendern machen willst, mit einer sauber gebundenen View ist das oft gar nicht nötig, weil WPF alles automatisch macht.
Für Letzteres hatte ich bisher nur einen einzigen Fall, wo ich keine bessere Lösung gefunden habe und ich weiß nicht mehr, ob da nicht doch nur der Zeitdruck das Problem war.

Not-invented-here-Syndrom:
Von mir ein kleiner Erfahrungsbericht, was passiert, wenn man möglichst auf externe Libraries verzichtet:
Ich "darf" seit ca. einem Jahr bewundern, was passiert, wenn vom Chef verboten wird, externe Libraries zu verwenden, stattdessen soll man die ebenfalls vom Chef (weiter-) entwickelten Alternativen benutzen. Das betrifft sehr vieles, von Logging über Serialisierung bis zur HTTP-Requests. Manches ist ein Wrapper, manches komplett selbst entwickelt.
Meine Erfahrung damit: Es ist die Hölle.
Selbst einfachste Dinge wie ein simples Logging werden zu einer Herausforderung, weil diese selbst gebastelten Dinge meist nicht zuende gedacht wurden, wir deshalb für neue Projekte daran etwas ändern müssen, oft aber nicht können/dürfen, weil das alte Projekte gefährdet.
Das Ergebnis ist, dass mit jedem Projekt weitere Dinge an die vorhandenen selbst gebauten Frameworks "angeklebt" werden, um ja nichts kaputt zu machen und nach ein paar Jahren hast Du einen Knäuel aus Klebestreifen und ... "Zeug", den niemand mehr wirklich beherrscht. Die Projekte dauern immer länger, die Kollegen sind genervt, die Bugs an den verrücktesten Stellen häufen sich - damit will niemand arbeiten.
Das mag vielleicht ein Extrembeispiel sein, aber das Prinzip bleibt: Selbst entwickelte Frameworks stellen eine enorme Gefahr dar, wenn sie nicht gut durchdacht sind.

07.05.2021 - 13:53 Uhr

Datenbank liegt im RAM vs. irgendwo als Datei

07.05.2021 - 13:02 Uhr

Also wenn Du "Access" mit "Datenbank" in Verbindung bringst, wirst Du hier die eine oder andere allergische Reaktion beobachten können 😉
Access ist keine Datenbank! Man kann es dafür missbrauchen, wird aber über kurz oder lang Probleme damit bereiten.

Wenn Du eine dateibasierte Datenbank haben willst, dann such explizit danach. SQLite ist z.B. ein populärer Vertreter.
Das hat aber den Nachteil, dass Du eine Datei hast, die von verschiedenen Prozessen genutzt werden kann und SQLite dafür keine Mechanismen anbietet.

Wenn mehrere Nutzer gleichzeitig arbeiten können sollen, bleibt dir also nur ein eigenständiger Datenbank-Server oder ein permanent laufender Server-Dienst (z.B. REST-API), der von den Clients genutzt wird und intern die Daten in irgendeiner Form speichert. Beachte aber, dass es auch ein Risiko ist, einen Datenbank-Server frei im Netzwerk erreichbar zu haben, davor kann so ein Server-Dienst schützen.

Oder Du bleibst beim Dateisystem, musst dann aber klug mit den konkurrierenden Dateizugriffen umgehen können.
Oder Du machst es wie z.B. LibreOffice und legst eine temporäre Datei daneben, die signalisiert: Wird gerade genutzt.
Das ginge auch mit SQLite, allerdings betrifft das dann nicht nur diese eine Datei, sondern die ganze Datenbank und alle Operationen darauf.

05.05.2021 - 16:14 Uhr

Für sowas verwende ich meist das CustomControl, das sind dann die, die an mehreren Stellen mit mehreren ViewModels verwendet werden können.
Das CustomControl bekommt dann auch sauber definierte DependencyProperties und einen anständigen Style/Template, die mittels TemplateBinding (oder RelativeSource) auf die DPs zugreifen.
Oder ganz normale bereits bekannte Controls mit Styles/Templates, die auf AttachedDependencyProperties zugreifen.

Die UserControls sind bei mir meistens ausgelagerte Teile einer größeren View, sie sind also nicht unabhängig und dürfen dann auch einen DataContext zugewiesen bekommen.
Wirklich brauchen tut man die UserControls dafür nicht, ich nutze sie trotzdem, da komplexe Views in XAML leider schnell sehr unübersichtlich werden. Die UserControls dienen also hauptsächlich dazu, den Code lesbarer zu machen - oder z.B. in Listen, wenn es mehrfach genutzt wird und das gleiche ViewModel dahinter stehen kann/darf/soll.

Ist vielleicht etwas kleinkariert, aber mir ist der Unterschied wichtig ^^

05.05.2021 - 12:48 Uhr

Und die View kennt das ViewModel nicht

Wie meinen? Müsste das nicht andersherum sein?
Das View bekommt eine Instanz des ViewModels als DataContext und muss daher auch den Typ kennen.
Die DataBindings werden ja auch von der View verwaltet, also kennt es auch die Properties.

Das ViewModel darf die View nicht kennen.

03.05.2021 - 20:31 Uhr

Die DataTrigger können das auch, vermutlich sogar ein Stück besser.
Da ist egal, wo der Wert herkommt, es braucht nur ein Binding und einen Wert.