Laden...

Forenbeiträge von Palladin007 Ingesamt 2.064 Beiträge

06.12.2023 - 09:53 Uhr

Wenn das alles so doof ist, frage ich mich, warum das dann in C# eingebaut wird?

Es ist nicht doof, aber man muss es - wie Abt schreibt - überlegt verwenden.

Microsoft baut viele Funktionen ein, wie Du sie benutzt, ist deine Sache.
Und sie haben auch eine Funktion eingebaut, dass man vor dem ganzen Code auf null prüfen und dann ein Default-Verhalten implementieren kann, das macht die ganzen null-Checks danach überflüssig.
Der Null-conditional Operator lohnt sich besonders dann, wenn man nur eine Null-Prüfung braucht, dann kann man es kompakter schreiben, aber das ist hier nicht der Fall, hier setzt sich das ganze fort.

if (collection is null)
    return;

var enumerator = collection.GetEnumerator ();
enumerator.Reset();

while ( enumerator.MoveNext() )
{
    var value = enumerator.Current;
    ...
}

By the way: Das Reset() braucht man nach dem GetEnumerator()-Aufruf nicht.
Nur wenn man erneut iterieren möchte, braucht man Reset(), was ich persönlich bisher noch nicht hatte.

04.12.2023 - 09:03 Uhr

Zu .NET Framework, .NET Standard, .NET Core und .NET >5 findest Du hier einen ausführlichen Artikel von Abt.
Und ja, das neue .NET ist plattformunabhängig, läuft also auch auf den von dir genannten Betriebssystemen.

Und wenn es dir um die Zukunft geht und Du das alte .NET Framework nicht unterstützen musst, dann nimm das aktuellste .NET, beachte dabei aber die Support Policy.
Wenn Du auch das alte .NET Framework unterstützen musst (viele Frameworks müssen/wollen das), ist .NET Standard die einfachste, aber nicht die einzige Möglichkeit. Du kannst alternativ auch mehrere .NET Versionen unterstützen.

Wenn es dir um Desktop- und Mobile-Anwendungen geht, sieht das ganze aber komplizierter aus.

Von Microsoft gibt es MAUI, das ist prinzipiell plattformunabhängig, läuft aber (noch) nicht auf Linux.
Alternativ kannst Du auch vollständig auf Web setzen und z.B. mit Blazor eine Web-Anwendung entwickeln, die Du dann in verschiedenen Desktop-Clients darstellen kann, das braucht auch nicht unbedingt einen Web-Server, mit MAUI Hybrid läuft Blazor auch als reine Desktop-Anwendung.

Außerhalb der Microsoft-Welt kenne ich noch:

  • Avalonia
  • UNO
  • Electron (Web)
  • Photino (Web)

Das vermutlich flexibelste wäre, Du setzt auf Blazor und suchst dann nach Clients, die das unterstützen, damit hast Du die ganze Bandbreite von Web-Technologien zur Verfügung. Umgekehrt bist Du aber eingeschränkt, wenn es um reine Desktop-Technologien geht, da musst Du dann auf die Möglichkeiten des jeweiligen verwendeten Clients zurückgreifen.

Oder Du willst kein Web, dann kannst Du zusätzlich noch WPF nutzen, das läuft zwar nur auf Windows, wird aber immer noch unterstützt.

Ich persönlich habe für ein privates Projekt Photino (für Blazor) rausgepickt, da es schlanker ist, als Electron und .NET 8 auch ohne Dirty-Fix unterstützt.

28.11.2023 - 18:07 Uhr

Als Tipp am Rande:

In der DbContext-Ableitung kann man in der OnConfiguring-Methode (oder wenn man die DbContextOptions baut) einen Interceptor registrieren:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.AddInterceptors(new EnableForeignKeysConnectionInterceptor());
}

Und die Klasse dazu:

internal sealed class EnableForeignKeysConnectionInterceptor : DbConnectionInterceptor
{
    public override void ConnectionOpened(DbConnection connection, ConnectionEndEventData eventData)
    {
        // PRAGMA foreign_keys = ON
    }
    public override async Task ConnectionOpenedAsync(DbConnection connection, ConnectionEndEventData eventData, CancellationToken cancellationToken)
    {
        // PRAGMA foreign_keys = ON
    }
}

Dann passiert das bei jeder Connection automatisch.

13.11.2023 - 19:15 Uhr

Ich habe soeben Version 0.3.0 hochgeladen:

v0.3.0 - 2023-11-13

Hinzugefügt

  • PackScan.Tool-Parameter, um das TargetFramework für das Projekt anzugeben.
  • PackScan.Tool-Parameter, um den RuntimeIdentifier für das Projekt anzugeben.
  • Funktion, um die maximale Größe für heruntergeladene Icons im Format "<Breite>x<Höhe>" festzulegen.

v0.2.1 - 2023-11-06

Behoben

  • Ein Fehler wurde behoben, der Probleme mit spezifischen Plattformversionen verursachte, wie sie in MAUI-Projekten verwendet werden.

v0.2.0 - 2023-11-01

Hinzugefügt

  • Neue Konfigurationsoption, die es ermöglicht, Inhalte nur aus Dateien zu laden, wenn keine URL angegeben ist und umgekehrt.
01.11.2023 - 00:05 Uhr

Ich habe soeben die erste Version hochgeladen.

30.10.2023 - 16:06 Uhr

Danke für das Lob 😃

Angenommen ich würde das nun in mehrere CI-CD Pipelines einbauen, wie hast Du Dir gedacht, dass das Setup zentral gemanaged werden könnte?

Das Projekt bietet ja einen SourceGenerator oder ein .NET Tool an, beide können MSBuild-Eigenschaften lesen. Das .NET Tool bietet zusätzlich noch eigene Parameter an, die dann die MSBuild-Eigenschaften überschreiben.

Du könntest deine zentrale Verwaltung der Einstellungen also mit Hilfe einer props-Datei lösen und in jedem Projekt einbinden.

Wenn die mehreren Projekte aber zusammengehören, solltest Du aber nur für das Start-Projekt der Code generiert lassen, da dieses Projekt als einziges alle Referenzen hat. Das Ergebnis kann man dann über Dependency Injection verteilen und dort nutzen, wo man es tatsächlich braucht.

29.10.2023 - 23:33 Uhr

Hallo zusammen,

Ich möchte Euch mein Projekt PackScan vorstellen, das die Handhabung von NuGet-Paketen erleichtert.

Kurz gesagt, PackScan ist ein Code-Generator, der die project.assets.json ausliest und sowohl die darin enthaltenen Daten als auch die Informationen aus den NuGet-Paketen selbst in Code umwandelt. Darüber hinaus kann es konfiguriert werden, um Texte und Icons aus dem Internet zu holen und in den Code zu integrieren. Dies erleichtert es, alle genutzten Pakete und Lizenzen im Blick zu halten und auf anschauliche Weise in der Anwendung darzustellen – eine Anforderung, die in früheren Projekten von mir aufkam.

Hier die Highlights:

  • Automatische Sammlung von Informationen der verwendeten Pakete.
  • Analyse der Lizenzen und Warnungen bei unerwünschten Lizenzen.
  • Verfügbar als .NET Tool und Source-Generator für MSBuild.
  • Umfangreiche Konfigurationsmöglichkeiten für individuelle Anpassungen und Performance-Optimierung.

Ich freue mich auf eure Meinungen, Vorschläge und Kritik! Bitte hinterlasst eure Kommentare hier oder auf der GitHub-Seite des Projekts.

Beste Grüße

23.10.2023 - 20:00 Uhr

Hilft vielleicht:

Uninstall or remove Visual Studio
Dort gibt's einen Bereich "Remove all with InstallCleanup.exe"

06.10.2023 - 16:19 Uhr

Zum Lesen von Excel such mal nach "ClosedXML". InterOp brauchst Du mit dem OpenXML-Format nicht, aber ohne NuGet-Packages geht's nicht.

Wie wäre es, wenn Du eine DLL programmierst, als Datei irgendwo hin legst, in dem Script die Datei lädst und mit Reflection irgendeine Methode ausführst?

22.09.2023 - 09:11 Uhr

So funktioniert das nicht.
Was Du da unbewusst nutzt, heißt Pattern-Matching.

Diese Switch-Schreibweise wird vom Compiler in "typeof(T) is IClassItemMethodArgumentAttribute" (und so weiter) aufgelöst und das "is" prüft, ob die Instanz links dem Typ rechts zugeordnet werden kann. In deinem Fall heißt das, dass Du prüfst, ob das RuntimeType-Objekt (das wird von "typeof) zurückgegeben) das jeweilige Interface implementiert, was natürlich nie der Fall ist.

Deine Prüfung musst Du manuell mit Reflection vornehmen, Du brauchst die Type.IsAssignableFrom(Type) Methode.

13.09.2023 - 14:14 Uhr

"Key" ist ein Enum

if (e.Key == Key.D4)
{
}
22.08.2023 - 13:16 Uhr

Zitat von Abt

Es ist absolut valide (und notwendig) dass Handler andere Handler aufrufen.

Hierzu hatte ich vor einer Weile eine gegenteilige Aussage gelesen - Quelle finde ich nicht mehr.

Verstehe ich das richtig, dass ein Handler auch andere Handler aufrufen darf, also eine theoretisch beliebig lange Kette von Handlern entstehen kann?
Mit MediatR würde also ein RequestHandler eine IMediator-Dependency erhalten und andere Requests absenden.

Das ist ein Detail, was mir auch noch unklar ist:
Was ist, wenn mehrere Handler teilweise die gleiche oder ähnliche Logik haben?
Es wäre einfach, das alles in einen weiteren Handler auszulagern und den aufzurufen, aber ist das der beste Weg?

21.08.2023 - 14:45 Uhr

Wozu auch immer sie ein Repository haben wollten, ich hätte gedacht, der Code reicht 😄
Naja, jedenfalls gibt's jetzt ein Repository mit dem Bug.

Hatte zwar auch mal im Source geschaut, bin aber leider damit nicht sonderlich vertraut.

Dito - ich werde aus dem Code auch nicht schlau, zumindest sehe ich keinen Grund, warum er IRootObjectProvider nicht finden sollte, alles andere aber schon.

19.08.2023 - 20:55 Uhr

Ne, hilft leider nicht.

Ich brauch den IRootObjectProvider ja abhängig vom Kontext der MarkupExtension.
Die Services der Application sind dagegen ohne Kontext und enthalten auch keinen IRootObjectProvider.

Ich habe auch keine Lösung für das Problem gefunden, ist scheinbar ein Bug in MAUI.
Ich habe aber einen Workaround gefunden, sodass ich zumindest weiterarbeiten kann.

18.08.2023 - 18:07 Uhr

Hallo zusammen,

ich hab das Problem, dass mein MAUI-Projekt nach dem Publish sich mit einem EventLog-Eintrag sofort wieder beendet.

Das Problem ist Folgendes:
Ich habe eine MarkupExtension, die anhand des IServiceProvider-Parameters den IRootObjectProvider-Dienst abruft.
Genau diesen Dienst scheint es im Relase-Mode aber nicht zu geben? Im Debug-Mode ist alles korrekt da.

Das ist der Code der ServiceProvider-Klasse, die da zum Einsatz kommt:
https://github.com/dotnet/maui/blob/main/src/Controls/src/Xaml/XamlServiceProvider.cs
Der Code ergibt für mich keinen Sinn, wenn es den IRootObjectProvider nicht gibt, müsste der context null sein, aber dann gäbe es auch andere Dienste nicht, die aber da sind.

Hat jemand eine Idee, woran das liegt und wie ich es beheben kann?

Besten Dank 😃

04.08.2023 - 14:15 Uhr

Zitat von gfoidl

Aber von vornhinein würde ich (heute) keine Projekte mehr zwangsmäßig in (thematische) Assemblies aufteilen (XYZ.View, XYZ.ViewModel, XYZ.Models, etc.), sondern das nach Anwendungsfällen (use cases), Benutzeraktionen, usw. aufteilen.

Kannst Du das näher erleutern, woran Du das normalerweise orientierst?
Z.B. Feature, Modul, Dialog getrieben, etc.?

30.07.2023 - 15:41 Uhr

Probier's aus, ob das von Haus aus geht, weiß ich nicht, ich rate mal Nein.
Aber sowohl in Newtonsoft.Json, als auch System.Text.Json kann man Converter bauen.
Ich würde das auch tun, einfach um den Color-Type verwenden zu können.

30.07.2023 - 15:32 Uhr

Beide Varianten sind suboptimal, Variante 1 ist aber ein bisschen weniger suboptimal, als Variante 2. 😉
So spontan fällt mir kein einziger sinnvoller Fall ein, wo ein Methoden-Ergebnis als Property bereitgestellt wurde.

Richtig wäre:

public class Test
{
    private int _ctorArg;
    
    // Wenn Du den Wert public brauchst, dann eine Property:
    // public int CtorArg { get; }

    public Test(int arg)
    {
         _ctorArg = arg;
    }

    public int CalculateSomething(int arg)
    {
        return _ctorArg * arg;
    }
}

Ist natürlich schwer zu beurteilen, wie man etwas macht, ohne das Ziel zu kennen.
Es gibt viele verschiedene Wege, das selbe Ziel zu erreichen, doch normalerweise sollte man diesen Weg wählen (außer man braucht etwas anderes), da es am einfachsten ist und am wenigsten fehleranfällig.

(Und natürlich sollten bessere Namen gewählt werden)

30.07.2023 - 14:03 Uhr

Woher soll der geneigte Programmierer wissen, welche Objekte aus einer UI-Klasse stammen?

DispatcherObject ist das magische Stichwort, alle die Klassen, die von DispatcherObject ableiten, dürfen nur im UI-Thread verwendet werden.

aber ich werde die PK-Klasse auf String-Brushes (Stringwert, der ein Color-Wert darstellt) umstellen

Nimm doch direkt die Color-Werte? Ist etwas intuitiver, man sieht am Typ, was es ist.

Und den BrushConverter hast Du schon, Du musst ihn nur noch so umstellen, dass es ein ValueConverter ist (IValueConverter) und dann im Binding benutzen. Du bindest dann die String/Color-Property, als Converter deinen ColorToBrushConverter und siehe da, es funktioniert 😉

... zumindest solange es kein anderes Problem gibt.

30.07.2023 - 09:30 Uhr

Wenn Du dich mit WPF beschäftigst, sollte eigentlich kein Weg an MVVM vorbei führen.

Aber wenn Du noch so sehr Anfänger bist, solltest Du eher mit der Konsole anfangen.
WPF mag auf den ersten Blick zwar einfach wirken, aber an vielen Stellen gibt's dann doch Komplexitäten (wie z.B. MVVM), die einen Anfänger überfordern können.

Mit den Grundlagen, der OOP und den häufigen Sprach-Funktionen solltest Du mindestens sattelfest sein, bevor Du mit WPF startest.

30.07.2023 - 09:15 Uhr

@Th69

Tatsache, Du hast Recht, ich hab's gerade nochmal getestet.

Dass ein Brush ein UI-Objekt ist, wusste ich, aber ich dachte, das Ding tut solange nichts, bis es irgendwo benutzt wird.
Laut meinem Test ist das auch der Fall, man kann das Brush-Objekt im anderen Thread erstellen und damit arbeiten, aber in der UI nutzen darf man es danach nicht, selbst wenn das wieder im UI-Thread stattfindet. Deshalb knallt es auch erst bei der PK-Zuweisung, wo er eigentlich im richtigen Thread ist.
Und wenn man es Freezed, tritt das Problem nicht mehr auf.

Ist ein gutes Beispiel, warum man View und ViewModel nicht mischen sollte 😉


Mein kleines Test-Projekt:

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Click="Button_Click" Content="Test" HorizontalAlignment="Left" />
    </StackPanel>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        var brush = await Task.Run(CreateBrush);
        Background = brush;
    }

    private Brush CreateBrush()
    {
        var brush = new SolidColorBrush()
        {
            Color = Colors.Red,
        };

        //brush.Freeze();

        return brush;
    }
}
30.07.2023 - 08:55 Uhr

Das mit dem Binding hatte ich aber erwähnt 😉
Das ist aber auch nicht der Fehler, dass er da eine Exception wirft, ist korrekt so, weil er aus einem anderen Thread kommt.
Du hast also irgendwas, was dafür sorgt, dass er aus einem anderen Thread kommt und das gilt es zu finden.
Im hier gezeigten Code sehe ich davon aber nichts und der Debug.WriteLine-Test hat das auch bestätigt.

Wie genau das CanFreeze da hinein spielt, weiß ich nicht, ich vermute aber, dass das eine Optimierung ist, damit WPF die Farben nicht auch noch überwachen muss. Dann ändert sich die Farbe nicht, also muss auch im UI-Thread nichts aktualisiert werden.

29.07.2023 - 21:53 Uhr

Ich meine sowas:

vm.PropertyChanged += MyOnPropertyChangedHandler;

// ...

private void MyOnPropertyChangedHandler(object? sender, PropertyChangedEventArgs e)
{
    // ...
}
29.07.2023 - 21:04 Uhr

Die Ausgabe ist wie erwartet korrekt - hätte mich auch gewundert, aber sicher ist sicher.

Mit EventHandler meine ich aber nicht das OnChanged, sondern ob Du irgendwo das PropertyChanged Event selber abonierst und darauf reagierst. Wäre möglich, dass der Fehler ganz woanders liegt und nur indirekt durch die PK-Zuweisung ausgelöst wird.

29.07.2023 - 17:41 Uhr

Schreib mal zwischen jedes await in der AssociatedObject_Loaded ein Debug.WriteLine($"{Environment.CurrentManagedThreadId} | {SynchronizationContext.Current?.GetType()?.Name}")
Das sollte eigentlich immer die gleiche ID sein, aber da es bei der PK-Zuweisung zu der Exception kommt, deutet es darauf hin, dass es dabei eben nicht die richtige ID ist.
Und der SynchronizationContext sollte nicht null sondern DispatcherSynchronizationContext sein, da der für die Synchronisation zuständig ist.

Oder das PropertyChanged-Event tut mehr, als wir wissen. In den Kommentaren von der MVVM_Base steht etwas komisches über den Dispatcher. Hast Du irgendwo eigene PropertyChanged-EventHandler geschrieben, ggf. in der MVVM_Base.

29.07.2023 - 15:30 Uhr

Deine Benennungen sind nicht sehr intuitiv. Was ist PK?

Nutzt das irgendwelche Events, INotifyPropertyChanged?
Du übergibst das an ProgrammDaten_LoadPK2 in einen anderen Thread und änderst Dinge.

Warum es erst danach knallt, sehe ich so auf Anhieb nicht, aber die genannten Zugriffe sehen ziemlich falsch aus.

29.07.2023 - 13:37 Uhr

Wo genau tritt die Exception auf?
Wenn er dir nur ein await anmeckert, dann geh mal in die Methode und debugge dort, um zu sehen, wo er raus springt.

Ich rate mal, Du machst in ProgrammDaten_LoadPK2 irgendetwas, was da nicht gemacht werden darf, denn genau das läuft auf einem ThreadPool-Thread und darf nicht auf den UI-Thread zugreifen. Das kann auch ein Event sein, oder eine ViewModel-Property, die Du änderst, wenn die UI irgendwie darauf reagiert, führt das zu der Exception.

29.07.2023 - 13:31 Uhr

Die Übergabe eines Externen Wertes als Methodenparameter an die Methode eines Objektes wäre also ok ?

Ich weiß nicht, was Du unter "externen Wert" verstehst.

Wenn Methode A eine andere Methode B aufruft, dann darf sie natürlich jeden Wert, der benötigt wird, als Parameter übergeben, dafür sind sie da.

29.07.2023 - 11:01 Uhr

Du darfst auch keine UI-Controls außerhalb des UI-Threads anfassen. Das gilt auch für ViewModel-Eigenschaften und Events, wenn die UI daran bindet bzw. darauf horcht.

Dafür gibt's ja (unter Anderem) async/await. Wenn Du vor dem await im UI-Thread warst, bist Du es hinterher wieder, selbst wenn dazwischen ein anderer Thread am arbeiten war - einfach ausgedrückt. Das setzt aber voraus, dass Du vor dem await auch im UI-Thread warst.

Was sich nun wo und wie und aus welchem Thread aufruft, wissen wir nicht, das zeigst Du nicht.
Aber der Hintergrund-Thread darf nichts an der UI ändern.
Wenn Du aus dem Hintergrund-Thread live die UI aktualisieren willst, musst Du das über Umwege machen, z.B. indem Du aus der UI regelmäßig nach Aktualisierungen fragst. Es gibt aber auch Möglichkeiten, die Synchronisierung selber zu nutzen, entweder direkt über den SynchronizationContext (nicht nutzen, bevor Du weißt, was Du tust), oder Du guckst dir die JoinableTaskFactory-Klasse aus dem NuGet-Package "Microsoft.VisualStudio.Threading" an, dazu gibt's auch noch ein Analyzer-Package, was dich auf async/await-Fehler hinweisen kann - solltest Du auch installieren.

29.07.2023 - 00:49 Uhr

Frage 1:

Wann weise ich den Klassenvariablen Werte per Konstuktor zu bei der Objektinstantiierung mittels

Wenn Du es brauchst, der Wert also vor "Lebensbeginn" des Objektes bzw. im Konstruktor benötigt wird.
Ich behandle das immer so: Alle Werte, die das Objekt benötigt, um grundlegend zu funktionieren, werden über den Konstruktor verlangt.

wann erst nach der Instantiierung z.B. mittels direkten Ansprechens nach der Variablen mittels

Nie, weil immer Properties genutzt werden sollten.
Wenn ich die Frage auf Properties beziehe:

Dann, wenn Du es brauchst und die Klasse damit umgehen kann.
Ich halte immer möglichst viel immutable, also möglichst kein Setter, demnach auch möglichst viel über den Konstruktor oder einer Kombination der beiden neuen Features "required" und "init".


Frage 2:

Wann wähle ich die Variante mit getter/setter wie oben bei "Input" und wann arbeite ich ohne getter und setter wie bei "_input" ?

Das heißt "Property".
Und alles, was public ist, ist eine Property.
Alles was private ist, darf auch eine Variable sein.
Nutz das, was Du brauchst.


Frage 3:

Methode und Methodenparameter.
Allerdings sollte die Methode das Ergebnis selber zurückgeben und nicht mit einer "_output"-Variable arbeiten.


Frage 4:

Die sind ein Thema, aber selten bzw. für besondere Situationen.
Für einen Anfänger sind sie aber wahrscheinlich irrelevant.

28.07.2023 - 09:39 Uhr

Task hat doch sowas wie .ContinueWith(). Schau dir das mal an.

Das ContinueWith() sollte man normalerweise nicht benutzen.

Mit einem await erreicht man das gleiche (Code ausführen nach Beendigung des Tasks), nur dass das await einen besser lesbaren Code ermöglicht.

Für mich sieht das eher nach einem Bedien-Fehler mit async/await aus.

PS:
Hier stand vorher eine andere Nachricht.
Die habe ich gelöscht, da ich meinen Vorredner falsch verstanden hatte.

27.07.2023 - 23:08 Uhr

Was Du da zeigst, ist genau richtig so.

public async Task LoadDataAsync()
{
    data1 = await LoadDataFromXML1Async();
    data2 = await LoadDataFromXML2Async(data1);
    data3 = await LoadDataFromXML2Async(data1);
    data4 = Vorgang1_BuildView(data1,data3);
}

Genau so macht man das - das komische BuildView mal ausgenommen 😉

Das BuildView am Ende passt deshalb nicht ins Bild, weil in WPF eigentlich keine View gebaut werden muss. Du baust stattdessen ViewModels und bindest die View daran, den Rest macht WPF automatisch. Wenn Du aber genau das meinst (also das Aufbauen von ViewModels auf Basis der Daten), dann ist das auch richtig so.

27.07.2023 - 12:45 Uhr

Lies dich ein, wie man richtig mit async/await/Tasks arbeitet, durch Trial&Error wirst Du nicht glücklich.

Und deinen bisherigen hier gezeigten Code musst Du weg werfen.

// Immer einen Task zurückgeben, außer Du weißt, was du tust
public async Task LoadDataAsync() // Das "Async" ist Konvention
{
    // Hier darfst Du normal auf Properties vom ViewModel zugreifen, hier bist Du noch im UI-Thread.
    
    _data = await LoadDataFromDatabaseAsync();
    
    // Hier darfst Du normal auf Properties vom ViewModel zugreifen, das "await" kümmert sich um die korrekte Thread-Synchronisation
}

Im Behavior:

// Für Event-Handler keinen Task zurückgeben - ist eine Ausnahme.
private async void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
    // ...
    await vm.LoadDataAsync();
}

Das await synchronisiert alles selbständig, Du brauchst dafür keinen Dispatcher.
Das funktioniert aber nur, wenn das await im UI-Thread stattfindet, daher muss das auch im Event passieren, weil die sind in der Regel im UI-Thread.
Der Inhalt der asynchronen Methode wird dann auf einem anderen Thread ausgeführt und nach dem Await wechselt die Methode wieder zum UI-Thread.

27.07.2023 - 11:31 Uhr

NICHT im Konstruktor 😉 Ist generell eine blöde Idee.

Am besten ist es, Du lädst aus der UI gesteuert die Daten, also z.B. durch einen Button-Klick (Command).
Du kannst es aber auch in einem UI-Event das ViewModel über den DataContext suchen und dort die LoadDataAsync-Methode aufrufen, das wäre auch noch MVVM konform.
Ich hatte das früher ganz gerne durch meine selbst gebaute Navigation gelöst, das ViewModel wird informiert, wenn es angezeigt wird und lädt dann die Daten. Der Aufbau verlangt aber eine gewisse Infrastruktur.

23.07.2023 - 20:02 Uhr

mutable = änderbar

Ändere das Entry-Objekt und ersetze es nicht mit einem Neuen.
KANN besser sein, muss aber nicht, hängt von deinem Problem ab.

public int Number { get; } // immutable
public int Number { get; set; } // mutable
23.07.2023 - 14:48 Uhr

Nein, keine Abkürzung.
Du könntest aber für den Getter eine Lambda-Expression nutzen:

public NoTradeDay Monday
{
    get => _monday;
    set
    {
    	_monday = value;
    	RecalculateValues();
    }
}

Vielleicht ändert sich das aber in Zukunft noch, es ist ein Semi-Auto-Properties-Feature in der Entwicklung.

Das soll (soweit ich weiß) etwa so aussehen:

public int A
{
    get;
    set => field = value + 1;
}

"field" ist dabei ein Keyword und repräsentiert die Variable.

Wie der konkrete Stand ist, weiß ich aber nicht, offiziell bei Microsoft auf der Seite steht noch nichts.

23.07.2023 - 08:57 Uhr

@Th69: Im Debugger bleibt die Anwendung ja nie hängen - zumindest habe ich das so verstanden.

Und als externe Anwendung kannst du per "Attach to Process" die Anwendung nachträglich debuggen

Das ist aber ein guter Punkt.

Du müsstest die Anwendung  als Debug-Version bauen und damit den Fehler reproduzieren.
Wenn die Anwendung dann hängt, kannst Du ggf. Visual Studio an den Prozess attachen und so herausfinden, welche Threads/Tasks wo hängen.

22.07.2023 - 20:17 Uhr
var index = HorizontalLinesTillTouch.IndexOf(Entry);

HorizontalLinesTillTouch[index] = Entry2;

Oder Du machst die Entries mutable, kann unter Umständen besser sein.

22.07.2023 - 15:36 Uhr

Das klingt nach Multithreading-Chaos, passt auch dazu, dass es beim Debuggen immer läuft.

Dass die Form nicht geschlossen werden kann, würde ich so erklären, dass der UI-Thread in einem Deadlock fest hängt, dann hilft nur noch Thread oder Prozess killen.
Und wo er stehen bleibt, wirst Du ohne Debugger nur mit entsprechendem Logging feststellen können.

22.07.2023 - 15:11 Uhr

Wieder ein Fall von: Ohne MVVM machst Du es dir unnötig schwer.

https://mycsharp.de/forum/threads/118261/artikel-mvvm-und-databinding

Nimm eine ObservableCollection, wirf da deine Objekte rein und binde sie als ItemsSource.
Dazu ein Command, den Du an den Button bindest und als CommandParameter bindest Du den aktuellen DataContext ("{Binding}").

In der Methode zum Command kannst Du dann einfach den Parameter casten, aus der ObservableCollection entfernen und der Rest passiert automatisch.
Ist auch eine Ecke weniger Code.

17.07.2023 - 20:33 Uhr

Meine Glaskugel ist leider kaputt.

Finde raus, wo er sich auf hängt, der Debugger hilft dabei: [Artikel] Debugger: Wie verwende ich den von Visual Studio?

Mit etwas Code können wir dann auch weiterhelfen.

13.07.2023 - 11:44 Uhr

Wenn Du DevExpress brauchst, würde ich da mit der Recherche beginnen - für was bieten die ihre Frameworks denn überhaupt an? Für WPF gibt's meiner Erfahrung nach recht viel und WPF wird denke ich auch nicht so schnell eingestampft, dafür ist das zu verbreitet.


Und ja, MAUI ist in gewisser Weise eine Neuauflage von Xamarin.
MAUI arbeitet aber in einigen Punkten anders als Xamarin, insbesondere die Art und Weise, wie die verschiedenen Plattformen verwaltet werden.


Und ich persönlich finde Blazor hervorragend, meine Firma setzt auch voll auf Blazor und ist bisher zufrieden.
Es ist aber immer noch eine recht neue Technologie, das darfst Du nicht vergessen.

Insofern halte ich deinen Plan, Blazor als "Haupt"-Technologie in Zusammenarbeit mit MAUI zu verwenden, für durchaus sinnvoll. Allerdings gibt es meines Wissens nach aktuell neben MAUI keine andere Desktop-Technologie, die mit Blazor arbeiten kann, vielleicht ändert sich das aber noch.

Für ein privates Projekt habe ich mich auch für MAUI + Blazor entschieden, aus dem Grund, dass es hoffentlich bald eine Linux-Variante für MAUI oder ein Linux fähiges Desktop-Framework gibt, das mit Blazor arbeiten kann. Bis dahin läuft mein Projekt eben nur auf Windows.
Das Projekt ist aber noch so frisch, dass ich nichts konkretes dazu sagen, nur, dass ich den Plan prinzipiell gut finde.

11.07.2023 - 16:27 Uhr

Zitat von Dolce

Ich habe das Privileg, ein neues, größeres Desktop-Projekt erstellen zu dürfen.

Soll das Projekt denn nur auf Windows laufen?

Dann würde ich auch WPF in die Betrachtung mit einbeziehen.
Das ist zwar alt und das merkt man auch an vielen Stellen, aber es ist immer noch ein mMn. hervorragend gutes Framework mit sehr umfangreichen Frameworks (z.B. MahApps) und vielen Leuten in der Community, die sich gut damit auskennen. Außerdem hat es MVVM quasi "erfunden", andere Frameworks führen es nur fort, Du bist konzeptionell also nicht so weit entfernt vom aktuellen Stand der Technik.
Aber Du bist natürlich an Windows gebunden.

Ich habe selber vor zwei Jahren ein WPF-Projekt umgesetzt, das einzige, was ich vermisst und daher selber nachgerüstet habe, ist das Hosting-Konzept von ASP.NET. Später habe ich dann ein Framework gesehen, was das auch macht, selber genutzt habe ich es aber noch nicht: Wpf.Extensions.Hosting
MAUI nutzt das gleiche Konzept, daher denke ich, dass das ein prinzipiell guter Weg ist.

Wenn es auch auf Linux laufen soll, hast Du von Microsoft meines Wissens nach sowieso keine wirkliche Option. WinUI ist Windows only, MAUI gibt's aktuell (noch) nicht für Linux und WPF ist sowieso Windows only. Du kannst eine Website entwickeln und einen Wrapper, der sie hostet und einen Client mit Browser-Control startet. Oder Du greifst auf Community-Projekte wie Avalonia zurück, das läuft auf Linux.


Zu MAUI und WinUI kann ich leider nicht viel sagen.
Mit MAUI habe ich bisher nur experimentiert, mir gefallen viele Dinge und ich würde gerne mal ein größeres Projekt damit umsetzen, aber ob das wirklich schon dafür geeignet ist, weiß ich nicht.
Und mit WinUI habe ich noch gar nichts gemacht.


Zitat von Abt

Auf der anderen Seite höre ich, dass es ein enorm zufriedenes Ökosystem rund um Avalonia gibt.

Dem kann ich mich aber nicht anschließen.
Avalonia sieht auf den ersten Blick wie WPF aus (was ja auch gewollt ist), auf den zweiten und dritten Blick hatte ich bei meinem letzten Avalonia-Projekt viele Probleme. Ich weiß nicht, ob das meine Fehler waren, oder wirklich Design-Fehler. Z.B. ist das Styling-Konzept mMn. nicht zuende gedacht und viel zu flexibel (nicht unflexibel), das hatte WPF mMn. deutlich besser gelöst.

10.07.2023 - 17:04 Uhr

Der Enum-Datentyp kann sowohl einen einzelnen Wert, als auch mehrere Flags darstellen. Technisch ist es am Ende nur ein long, aber der Compiler muss wissen, wie er mit den Werten umgehen soll, wenn Du long und das Enum zusammen nutzen möchtest, weiß der Compiler nicht mehr, von welchem Typ er die Operatoren nun nutzen soll.

By the way, als Tipp:

[Flags]
enum MyEnum : long
{
    None = 0,
    A = 1 << 0, // 1
    B = 1 << 1, // 2
    C = 1 << 2, // 4
    D = 1 << 3, // 8
}

So ist das bedeutend übersichtlicher.
Du kannst auch die Bit Flags direkt schreiben:

[Flags]
enum MyEnum : long
{
    None = 0,
    A = 0b0000_0000_0000_0001, // 1
    B = 0b0000_0000_0000_0010, // 2
    C = 0b0000_0000_0000_0100, // 4
    D = 0b0000_0000_0000_1000, // 8
}

Ich persönlich würde das aber nur nutzen, wenn Du z.B. technisch bestimmte Bit-Flags vorgegeben hast und die nicht mit einem einfachen left-shift "gezählt" werden können, wie im oberen Beispiel.

Zitat von Abt

0 als Value ist immer schlecht/ungünstig - außer es soll eben "Nichts" ausdrücken.

Das stimmt natürlich - daher auch immer nur "None", weil es "Nichts" ausdrücken soll.
Außerdem gibt es den 0-Fall immer gibt, ganz egal, was ich tue. Mit "None" gebe ich dem einen Namen, der als falsch erkennbar ist.

10.07.2023 - 13:33 Uhr

Ich verwende für sowas gerne einen None-Eintrag mit 0, aus dem einfachen Grund, dass es der Default-Wert ist. Und man braucht kein zusätzliches Nullable, der Typerzeugt von sich aus kein potentiell ungewolltes Verhalten.

10.07.2023 - 13:12 Uhr

In deinem Eingangs-Beispiel mischst Du Enum und Long:

long MyFlags = StdVerzListenFlag.MYDOCUMENTS & StdVerzListenFlag.MYMYMUSIC; // Long

if((MyFlags & StdVerzListenFlag.DESKTOP) == StdVerzListenFlag.DESKTOP) // Enum

Teste mal nur mit dem Enum:

StdVerzListenFlag MyFlags = StdVerzListenFlag.MYDOCUMENTS & StdVerzListenFlag.MYMYMUSIC; // Enum

if((MyFlags & StdVerzListenFlag.DESKTOP) == StdVerzListenFlag.DESKTOP) // Enum
08.07.2023 - 20:37 Uhr

Eher so:

ConsoleApp2.exe --id 1 --value1 a1 --value2 b1 --id 2 --value1 a2 --value2 b2

Da kommen drann 1 = a1, b1 und 2 = a2, b2 raus.
Die ID mehrfach vergeben möchte ich eigentlich nicht.

Vielleicht sollte ich doch eine Config-Datei bauen, das hätte auch andere Vorteile und wäre sehr viel flexibler.

Das Optimum wäre ja, ich könnte die ganzen Daten aus der csproj lesen (da hab ich die Daten für einen Analyser auch, über ein Item mit Metdaten), aber MSBuild Projekt-Dateien lesen scheint ziemlich komplex.

08.07.2023 - 17:32 Uhr

Danke für den Vorschlag, aber das funktioniert in meinem Fall leider nicht.

Eigentlich habe ich zwei Werte, die einer ID zugeordnet werden sollen, es müssen aber nicht beide Werte angegeben werden, die Anzahl ist also variabel.
Ich hatte es eingangs nicht geschrieben, weil ich das Beispiel einfach halten wollte und nicht an diesen Weg gedacht habe - sorry ^^

Oder übersehe ich etwas?

08.07.2023 - 15:53 Uhr

Guten Nachmittag,

ich suche einen Command line parser, der aufeinander folgende Werte gruppieren kann.

Als Beispiel:

--foo x --id 1 --value a --bar true --id 2 --bla y --value b

Das Beispiel soll dann "1a" und "2b" in einer Liste sammeln.
Dass zwischen den Werten auch was Anderes (der "--bla" Wert) stehen kann, ist nice to habe, aber nicht notwendig.

Ideal wäre, wenn das mit System.CommandLine geht, weil darauf meine bisherige Lösung basiert.
Andere Frameworks gehen aber auch, ist dann nur mehr Arbeit für mich.

Kennt jemand ein Framework, was sowas bietet?

Besten Dank