vom Löffelmann: Neues in Sachen WinForms und VB aus Redmond

  • VB.NET

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Bluespide.

    vom Löffelmann: Neues in Sachen WinForms und VB aus Redmond

    Hallo lieber VBlers, WinFormsler und natürlich auch CSharpler,

    wir vom WinForms Team ...

    (für die von Euch, die mich nicht so gut kennen: ich bin der "Löffelmann", ich war erst Fachbuchautor für MS Press, dann Microsoft MVP und dann Software Engineer bei Microsoft, und lebe mit meiner Frau Adriana seit fast 2 Jahren in der Nähe von Redmond/Seattle/Washington State. Ich bin aktuell Senior Software Engineer im Windows Forms Team, und bin auch recht intensiv in alles rund um Visual Basic eingebunden)

    ... waren fleißig in den letzten 12 Monaten, und insbesondere sehr mit Performance-Verbesserungen rund um den neuen WinForms Designer beschäftigt. Seit Visual Studio Version 2019-16.10 sieht man im Designer nicht deutlich neuere Funktionalität, aber es hat sich eine Menge in Sachen Performance getan. Auschecken lohnt sich für .NET (>=5) ganz bestimmt!

    Es gibt aber auch nennenswertes zu neuen Features zu berichten für Visual Studio 2022, das es für den (klassischen) Framework Designer nicht gibt (und auch nicht geben wird):
    * WinForms und der WinForms-Designer unterstützt nunmehr Default Fonts (>.NET 6 - Application.SetDefaultFont in Program.cs). Damit können Formulare auf der Basis eines anderen als dem Standard-Font, der von der WinForms Runtime vorgegeben wird, designt werden. Insbesondere für Pixel-Perfect-Designs, die vom Framework übernommen werden sollen, stellt das eine deutliche Vereinfachung dar, bringt aber auch Viel für neu kreierte Forms, die mit anderen Fonts oder Font-Größen-Standards erstellt werden sollen.
    * In VB gibt es deswegen im Application Framework einen neuen Application Event namens ApplyApplicationDefaults (>.NET 6). Mit dessen Hilfe könnt ihr in dem ApplicationEvent File den Default Font, den HighDpiMode sowie nunmehr auch die MinimumSplashScreenDisplayTime eines etwaigen SplashScreens setzen, den ihr anzeigen lassen wollt.
    * Für die Custom Control Designer unter euch: Neu in der WinForms Runtime ist auch eine Property namens Control.IsAncestorSiteInDesignMode mit der man - am besten im Overwrite von CreateHandle - feststellen kann, ob ein Control im DesignMode ist oder nicht. Im Gegensatz zum DesignMode funktioniert das auch, wenn ein Control in einem UserControl in einem UserControl in einem Form gehosted wird. Ihr versteht, was ich meine, nicht? :)

    Könnt ihr das jetzt schon alles ausprobieren?
    Ja. Mit Visual Studio 2022 Preview. Gibt es hier.

    Und der aktuellen .NET 6 runtime (Achtung! Beta! Nightly Build! Experimente auf eigene Verantwortung!):
    Gibt es hier. (Main, 64-Bit Windows. Für andere Versionen schaut hier nach.)

    Auch sehenswert im Kontext von "was gibts Neues in Sachen VB", wie ich mit meiner Frau und einem altem Surface 3, auf dem eine VB-Software läuft, durch die hiesige Nachbarschaft fahre:
    Achtung, dieses YouTube ist auf English.

    Und noch eines, was ich mit meiner lieben Kollegin und Freundin Kathleen vor einiger Zeit im Rahmen der .NET Conf gemacht habe.
    Achtung, dieses YouTube Video auf auch auf English.

    So, nun auch noch eine Bitte:
    Ich hätte gerne Feedback von Euch (positive UND negative), wenn ihr experimentiert mit den neureren Features von WinForms, VB und C#, oder auch was ihr in der jüngsten Vergangenheit aus eurer Praxis (Hobby/Beruf ganz gleich!) mit diesen Dingen zu berichten wisst. Was sind eure Sorgen und Nöte, was ist das Vordringlichste, was euch aus Eurer Sicht daran hindert, um von .NET Framework auf .NET zu portieren/migieren.
    (Übrigens, noch ein paar Worte zur Nomenklatur: Wir nennen VB seit langem nur noch Visual Basic, und nicht mehr Visual Basic .NET. Und wir ließen das "Core" hinter dem .NET seit 5.0 fallen. (Im Gegensatz zum alten .NET Framework - bis Version 4.8).)

    Tobt euch aus!

    Liebe Grüße aus Redmond,

    Klaus
    Also erst mal finde ich es beeindruckend, dass jemand von Microsoft aus dem VB-C# Bereich hier im Forum auftaucht und sich dann auch noch Feedback von der Community holen möchte. Anscheint gibt es da doch noch Leute die sich für das Produkt interessieren und nicht nur im Unternehmen aufsteigen möchten und daher irgend ein Mist einbauen, als Erfolg Berichten und dann weg sind.

    .NET 5/6 hat mit dem neuen C# definitiv viele coole (und unnütze) neue Sachen, da sieht der Code schon ganz anders aus als im Framework. Ich benutzte zurzeit nur noch direkt .NET 6 um irgendwas zu Testen. Ich kann da jetzt aber gar nicht auf einzelnen Sachen direkt eingehen ... obwohl, 1 oder 2 Dinge fallen mir dann doch auf, die aber eher mich persönlich stören und nicht die breite Masse betreffen.

    -High DPI mit GDI+
    Funktioniert unter WinForms .NET 6 meiner Meinung nach noch schlecht. Ich habe letztens erst versucht eine Anwendung für normal und 4K 150% Skalierung umzusetzen und das sehr lange mit biegen, brechen und tricksen nur so halb geschafft. Die zwei größten Probleme sind finde ich, dass die ClientSize Property nicht mehr funktioniert und der Wert der DpiXY Property einfach falsch ist. Ich habe 2 Bildschirme, den einen in 4k auf 150% Skalierung (links) und den anderen 1080p mit 100% Skalierung (rechts). Dazu folgende simple Form, die mir ein Bild in den Hintergrund zeichnet, die ​ClientSize per Button fest auf 400x400 setzt und die ​DpiY des Graphics ausgibt:

    C#-Quellcode

    1. public partial class Form1 : Form {
    2. private Bitmap b = new(@"C:\400.png");
    3. public Form1() => InitializeComponent();
    4. private void button1_Click(object sender, EventArgs e) => this.ClientSize = new(400, 400);
    5. private void Form1_Paint(object sender, PaintEventArgs e) {
    6. e.Graphics.DrawImage(this.b, new Rectangle(0, 0, 400, 400), new Rectangle(0, 0, 400, 400), GraphicsUnit.Pixel);
    7. System.Diagnostics.Debug.WriteLine(e.Graphics.DpiY);
    8. }
    9. }


    Wenn ​Application.SetHighDpiMode(HighDpiMode.SystemAware); steht, sieht die Anwendung links ganz normal aus und rechts wird diese eben runterskaliert und ist dann verwaschen.

    Das wollen wir natürlich nicht, also wird der DpiMode umgestellt.


    Bei ​Application.SetHighDpiMode(HighDpiMode.PerMonitor); ist links wieder alles normal, aber rechts wird die Form nicht richtig skaliert. Sie ist zu klein. Trotz explizitem setzen der ​ClientSize auf 400 ist die Form nur 394x383 Pixel groß:

    Starte ich die Form jedoch direkt auf dem rechten Bildschirm, dann ist die Größe initial passend. Nach dem rüber ziehen nach links und wieder zurück ist diese aber wieder zu klein.

    Bei ​Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); ist erstmal links und recht die Größe passend.

    Perfekt? Ne, starte ich die Form auf dem rechten Bildschirm ist diese einfach zu groß. 406x417 Pixel:

    Nach dem rüber ziehen nach links und wieder zurück passt es dann wieder.

    Auch beim überschreiben der ​OnGetDpiScaledSize Funktion und setzen der ​desiredSize stimmt die Größe je nach Setup nicht. Für mich also Application.SetHighDpiMode(HighDpiMode.PerMonitor); und die Form darf halt einfach nicht verschoben werden. ...

    In allen Konstellation gibt mir ​e.Graphics.DpiY einfach ​144 zurück. Ist also unbrauchbar, denn das ist ja nur die DPI von dem Hauptbildschirm und nicht von der Form in diesem Moment. Dazu muss dann die WinAPI GetDpiForWindow mit dem Window-Handle aufgerufen werden. Da passt dann die DPI. Solle das nicht im Graphics-Objekt der Form direkt gesetzt sein?

    In .Net Core konnte die Einstellung ​EnableWindowsFormsHighDpiAutoResizing auf ​false gesetzt werden. Gibt es dafür eine Property/API-Call? Geht das überhaupt noch in .NET 6?
    Vielleicht mach ich aber auch alles Falsch und das geht ganz einfach.


    -Performance Profiler
    In VS2019 haben die finde ich eigentlich ganz gut funktioniert. Ich habe diese auch ziemlich häufig benutzt und bin echt begeistert gewesen, dass so viele noch dazu gekommen sind wie z.B. der .NET Objet Allocation Tracker, den ich auch ziemlich häufig benutze. VS 2022 ist zwar noch in der Preview also mag das nichts heißen, aber das Profiling funktionier gar nicht gut. Entweder lädt er nach dem Messen ewig und kommt nicht wieder, das Ergebnis kann leider nicht angezeigt werden oder der ​Visual Studio Standard Collector Service 150 hat die Datei noch in Benutzung und muss über den Taskmanager abgeschossen werden um weiter machen zu können.



    -Unnötiges Boxing von Struct's
    Durch den .NET Objet Allocation Tracker bemerkt man auch, was das Framework alles so macht. Ich weiß, dass wenn man nicht Low-Level unterwegs ist, dann ist einem sowas wie Garbage egal. Das ist ja auch richtig so, aber gerade wenn dieser dann einfach nur unnötig ist, kann man da ja schon mal ein Auge drauf haben. Im alten Framework gab es einen ComponentManager und der hatte ein ​Hashtable OleComponents. Aber nicht als Generika Hashtable<Object> sonder nur Hashtable und genau hier liegt das Problem. Der Iterator ist eine Klasse und kein Struct. Hier wird aber immer für den Idle der Form drüber iteriert und das passiert ziemlich häufig:

    C#-Quellcode

    1. ​if (OleComponents != null) {
    2. IEnumerator enumerator = OleComponents.Values.GetEnumerator();
    3. while (enumerator.MoveNext()) {
    4. ComponentHashtableEntry idleEntry = (ComponentHashtableEntry)enumerator.Current;
    5. continueIdle |= idleEntry.component.FDoIdle(-1);
    6. }
    7. }


    Für z.B. ein kleines Spiel, dass ich mal geschrieben habe und bei dem ich den GC so gut wie komplett umgangen habe, ist das ganz schön ärgerlich. Also wenn dann das einfache Fester auf so unnötige Weise den meisten Garbage erzeugt.
    Für .NET 5 hatte ich zumindest keine Hoffnung das dieser Teil umgeschrieben wird. Warum auch, das meiste wird ja vermutlich einfach übernommen und das stört eh keinen. Außer mich.
    Aber!
    Es wurde umgeschrieben!
    Jetzt ist OleComponents ein Dictionary<UIntPtr, ComponentHashtableEntry> und das hat ein Struct als Enumerator!
    Super, der Garbage reduziert sich dadurch beim ....... was? Nicht? Noch mehr Allokationen?

    C#-Quellcode

    1. ​if (OleComponents is not null)
    2. {
    3. IEnumerator enumerator = OleComponents.Values.GetEnumerator();
    4. while (enumerator.MoveNext())
    5. {
    6. ComponentHashtableEntry idleEntry = (ComponentHashtableEntry)enumerator.Current;
    7. continueIdle |= idleEntry.component.FDoIdle(msoidlef.All).IsTrue();
    8. }
    9. }


    Hochmodernes C# mit ​is not null und dann anstatt einer stink normalen foreach-Schleife oder var als Typen ein Boxing des Enumerators in das Interface IEnumerator. Klasse.
    Nach gerade mal 10 Sekunden mit der Maus über die Form fahren legt er satte 22480 Boxings an.

    Ich weiß, der GC ist super gut mit diesen kurzlebigen Objekten ist und vermutlich kann einem das total egal sein, aber ... das ganze nur weil keine foreach-Schleife oder der exakte Typ genutzt worden ist.


    Grundsätzlich sehe ich aber keinen Grund, der gegen das portieren von .Net Framework WinForms Anwendungen auf .NET 5/6 spricht. :thumbup:
    Das ist richtig cooles Feedback, vielen Dank!!
    Einiges ist bei uns (WinForms, GDI/GDI+) richtig aufgehoben, anderes geht eher Richtung Runtime.

    Ich hab's gerade erst gesehen, und werde frühestens nächste Woche dazu kommen.
    Aber gerade HighDpi und object allocations schauen wir uns eher dringlicher als desmnaechst an!

    Besten Dank!
    Danke nochmal für das Feedback.
    Was das unnötige Boxing von Objekten im Idle-Loop anbelangt: Mein Kollege Jeremy hat das gestern spät Abend noch direkt gefixt.
    https://github.com/dotnet/winforms/pull/5545/files

    Ob das den Bar für ein Servicing erreicht, werden wir klären - in der kommen .NET Version ist der Fix aber enthalten.

    Dann: HighDPI ist ohnehin gerade in der Mache - das Thema und die Issues, die du angesprochen hast, schauen wir uns an in dem Zusammenhang genauer an, wird aber etwas länger dauern.

    WICHTIG:
    Wenn ihr noch weiteres Feedback für anderem Kontext habt (.NET (Core) WinForms Designer, Data Binding, Performance WinForms, GDI, GDI+) - gerne auch einfache Dinge, Kleinigkeiten oder Erfahrungsberichte mit dem Core Designer: immer her damit!

    Mich würde auch einfach sehr (!) interessieren, wer von Euch mit .NET (3.1, 5.0, 6.0) arbeitet und nicht mehr .NET Framework (also bis 4.8), und welche Erfahrungen ihr da macht.
    Oder wer seine Anwendungen von .NET Framework zu .NET portiert!

    Wichtig zu wissen: Die neuen Dinge finden in .NET statt, nicht in .NET Framework.
    Wenn ihr in den Genuss von neuen APIs in der Runtime oder neuen Features im (WinForms) Designer kommen wollt, sollte .NET (Core - aber das nennen wir nicht mehr) euer Interesse wecken!

    Bis dahin,

    Gruß

    Klaus
    Leider muss ich noch lange mit .NET Framework (4.8) leben. In unserem Konzern geht so ein Wechsel nicht so schnell.
    Selbst mein Stand vom VS 2019 ist aus dem letzen Jahr (16.3.7).
    Ich würde sehr gerne und habe privat auch schon mal eines meiner Projekte auf .NET 3.1 portiert. Das war damals schrecklich, denn ich hatte keinen Zugriff mehr auf meine Forms. Das ist jetzt wohl anders wie es aussieht, aber mir Fehlen leider auch da die Erfahrungen. Portieren von Winforms scheint auch nicht unbedingt die beste Idee zu sein, vielleicht irre ich mich aber auch.
    Zudem: ich bin von der C# Fraktion, was aber IMO eine nur untergeordnete Rolle spielt.
    Hi,

    Ich würde es lieben WinForms auf Linux von Haus aus nutzen zu können. Hab mir mit SDL2(C++) ein kleines Control-Framework für meine Zwecke gebastelt um auf mich zugeschnittene Controls haben zu können. Aber ich würde Winforms vorziehen wenn ich denn könnte. Ein DataGridView mit Binding und allem drum und dran zur Ansicht der Daten meiner Wetterstation direkt auf'm Raspberry Pi das wäre was.

    Die vorschau der Intellisense ist wirklich gut gelungen, ich liebe das. Werde direkt aufs neue Studio umsteigen sobald der erste stable release da ist. (Hatte unerklärbare Exceptions) Beim hinzufügen eine CustomControls auf's Form(Anhang ex1), das passierte nach einem wechsel der Architektur, hab versucht einfach erst nochmal zu kompilieren vor'm hinzufügen, aber brachte nichts. Hin und wieder kam dann etwas total unerwartetes(Anhang ex2), bei ex2 konnt ich nicht mal mehr speichern, kam sofort wieder diese Exception.
    Bilder
    • ex1.jpg

      175,41 kB, 515×685, 19 mal angesehen
    • ex2.jpg

      22,54 kB, 408×269, 35 mal angesehen
    Die Natur ist bekanntermaßen knallhart, sie sortiert aus was sich nicht bewährt hat.(Harald Lesch, 2021)

    Demnach müssten wir bald dran sein...

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Takafusa“ ()

    @Bluespide Da steht leider nichts von Linux Support, mit Mono oder Wine geht es vllt. aber neben was neuen lernen, müsste ich experimentieren um das auf'm Raspi zum laufen zu kriegen. Da bleib ich erstmal bei SDL2, da spar ich auch das mit XAML(mag ich nicht). Wäre ein offizieller Linux/Linux-ARM Support vorhanden, täte ich mir XAML an.
    Die Natur ist bekanntermaßen knallhart, sie sortiert aus was sich nicht bewährt hat.(Harald Lesch, 2021)

    Demnach müssten wir bald dran sein...
    Möglicherweise kann ich auch zum Feedback beitragen...

    Auf Arbeit programmier ich derzeit mit .NET Framework 4.7.2. Werd ich aber sobald möglich auf .NET umstellen. Hier gibt es noch einige Randbedingungen die hier mit reinspielen.

    Ich denke interessanter sind die privaten Projekte.

    1. Anzeigentafel
    Ich habe eine Anzeigentafel (ähnlich wie im Stadion) in klein gebaut, bzw. mehrere. Die Hauptanzeige ist 192x320 Pixel groß (insegsamt 61440 pixel). Angesteuert wir dies mittels einer Aufsteckplatine auf dem RaspberryPi. Die Software habe ich mit .NETCore 3.1 umgesetzt, die Bedienung erfolgt mittels Blazor. Datenspeicherung per EFCore auf SQLite. Die Software baut einen WiFi Hotspot auf. Weitere Panels können sich darauf verbinden und erhalten dann Bilddaten von der Hauptanzeige.
    Es besteht die Möglichkeit Bilder, Videos, Gif und Fonts hochzuladen, diese in beliebiger Größe variabel zu platzieren und die Zusammenstellung zu speichern. Es können mehrere Bilder erstellt werden zwischen denen per schnellzugriff gewechselt werden kann. Die Bedienung erfolgt mittels Webseite welche mit Blazor umgesetzt wurde. Das Bediengerät verbindet sich per WIFI oder LAN auf die Anzeige.
    Betriebssystem ist DietPi Linux. Die Ansteuerung der Matrix erfolgt über eine Library (github.com/hzeller/rpi-rgb-led-matrix) welche die PWM-Signale zur Ansteuerung generiert.
    Als Hauptanzeige kann auch ein PC fungieren, jedoch ohne Darstellung, da hier die GPIOs fehlen. Auch hier können sich die Slave-Panels drauf verbinden.

    Zur Bildbearbeitung verwende ich GDI+ welches durch eine passende Library auch unter Linux funktioniert.

    Es war sehr einfach das Projekt am Raspberry zu betreiben, da eigentlich nur dotnet auf dem Pi installiert werden muss... Eine entsprechende Anleitung findet sich auf der Downloadseite um alles korrekt einzurichten, was sehr einfach ist! Somit sind erstmal keine tieferen Linux Kenntnisse notwendig.

    In Blazor lassen sich JavaScript Bibliotheken einbinden. Somit war zum Schluss sogar die Möglichkeit gegeben, auf die Smartphonekamera zuzugreifen und einen Videostream auf die Anzeige zu bringen.

    Alles in allem war es ein sehr interessantes Projekt und vorallem die Möglichkeiten welche durch .NETCore zur verfügung standen machten es auch für einen nicht-gelernten Programmierer relativ einfach das umzusetzen.

    2. Lichterkette über Azure
    Für Arduino-Controller besteht die Möglichkeit eine Verbindung zu Azure aufzubauen. Mittels IoT-Hub können Daten empfangen und in einer Datenbank abgespeichert werden. Ein weiteres Blazor-Projekt gehostet auf Azure kann die Daten dann anzeigen. Auch hier erfolgte die Umsetzung wieder mittels .NETCore3.1. War auch sehr einfach gestaltet.

    3. Turnierplaner spielerei
    Einfach mal ein Test wie das mit dem Store funktioniert. Umgesetzt mit .NETCore3.1 und WPF. Habe leider schon lange nichts mehr dran gemacht aber bis auf ein paar kleinigkeiten funktioniert das einwandfrei: https://www.microsoft.com/store/productId/9MZ2QB7TTWKM

    Ich hoffe mein Beitrag konnte etwas Beitragen.

    Grüße
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!

    Takafusa schrieb:

    Hi,
    ... (Hatte unerklärbare Exceptions) Beim hinzufügen eine CustomControls auf's Form(Anhang ex1), das passierte nach einem wechsel der Architektur, hab versucht einfach erst nochmal zu kompilieren vor'm hinzufügen, aber brachte nichts. Hin und wieder kam dann etwas total unerwartetes(Anhang ex2), bei ex2 konnt ich nicht mal mehr speichern, kam sofort wieder diese Exception.


    Kannst du nen Screenshot von deiner Version posten?
    Falls es eine ältere Version ist, und du die Gelegenheit hast, eine neue Version zu installieren (nach Möglichkeit größer 16.10), kannst du es damit nochmal probieren.
    (Bitte bedenke, dass du für Control Designer ein WinForms Designer SDK benötigst, dass wir erst zum Release von VS2022 geplant haben.)
    Gerne doch, hab auch noch wegen einem Update geschaut, es war mit der aktuellen Version. Es war einfach nur eine Klasse welche von Control geerbt hat, ich konnte exception-1 nicht mehr reproduzieren, aber exception2 trat wieder auf mit .Net 6.

    Hab eben ein C++ Projekt im neuen Studio getestet, keine Probleme mit dem Wechsel der Architektur. Auch in einem .Net-Framework(4.7.2) Projekt geht es ohne Probleme. Dann hab ich das noch mit einem .Net 5 Projekt getestet, da bekam ich auch Exception-2, es muss also am neuen Studio im Zusammenhang mit .Net liegen.
    Bilder
    • vs_version.jpg

      38,63 kB, 459×226, 35 mal angesehen
    Die Natur ist bekanntermaßen knallhart, sie sortiert aus was sich nicht bewährt hat.(Harald Lesch, 2021)

    Demnach müssten wir bald dran sein...

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Takafusa“ ()