ProgressBar (Teil)Automatisierung?

  • VB.NET

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    ProgressBar (Teil)Automatisierung?

    Hallo Leute,

    ich bin ein großer Freund des ProgressBar (PB), mich stört aber die Handhabung. Derzeit läuft der Spaß ja so:

    1) manuell Abzählen wie viele Arbeitsschritte ich per PB "visualisieren" möchte
    2) PB entsprechend konfigurieren (Min, Max, Step, etc)
    3) nach jedem Arbeitsschritt manuell Performstep ausführen (ich gehe jetzt mal von einer Problemstellung aus, die sich nicht per Schleife lösen lässt)

    Mich nervt dabei vor allem, wie leicht sich durch eine Modifikation des Codes ein Fehler(naja eher Ungenauigkeit) in der PB einschleicht (Codezeile hinzugefügt, Max nicht angepasst oder PerformStep vergessen, etc). Problematisch ist insbesondere, dass mein aktuelles Projekt zukünftig durch absolute Laien weitergepflegt wird - da schleicht sich schonmal die ein oder andere Unachtsamkeit ein, zumal es für meine Nachfolger ja auch fremder Code ist.


    Nun zur Idee:

    a) Könnte man einen Codeabschnitt analog zu Region/Using/TryCatch markieren, meinetwegen als "UseProgressBar"?
    b) Lässt sich das Betreten* des so markierten Abschnitt auswerten und per Event anzeigen? (*wie ein StepInto im Debugger)
    c) Lässt sich automatisiert zählen, wieviele Befehle (zur Not auch LOC) im markierten Abschnitt sind?
    d) Lässt sich feststellen wann ein einzelner Befehl im Abschnitt abgearbeitet wurde? (wie NextStatement im Debugger)

    Damit könnte man folgende Struktur umsetzen:

    Quellcode

    1. #UseProgressBar
    2. Befehl 1
    3. Befehl 2
    4. ...
    5. Befehl n
    6. #End UseProgressBar


    Das Programm springt in den "Codeblock", ein Event wird gefeuert damit eine Routine die Anzahl der Befehle im Abschnitt zählt und ein ProgressBar entsprechend konfiguriert.
    Nach jedem Befehl feuert ein anderes Event und PerformStep wird ausgeführt. Damit würde die ganze Konfiguration und das manuelle Setzen von PerformStep entfallen. Man müsste nur darauf achten, dass der entsprechende Code vom oben definierten Block umgeben ist.

    Nun zum Problem:
    Ich habe keine Idee ob oder wie man so etwas umsetzen könnte und ob es überhaupt Sinn macht. Es ist erstmal nur eine fixe Idee, evtl übersehe ich eine deutlich einfachere Lösung. Es wäre cool, wenn ihr eure Meinung dazu und etwaige Ideen wie man einen Lösungsansatz gestalten könnte, hier reinschreibt.

    lg

    cl10k schrieb:

    Nun zum Problem:
    Eine ProgressBar ist ein GUI-Element zur Visualisierung eines zeitlichen Ablaufs. Nicht mehr und nicht weniger.
    Du willst iwie eine Schritt-Ablauf-Dokumentation.
    Trenne Daten / Prozesse und GUI.
    Beschreibe Dein Problem noch einmal, ohne die Buchstabenfolge ProgressBar zu verwenden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Nein. Das ist absolut sinnlos, unmöglich und hat mit einer ProgressBar nichts zu tun. Bei ordentlichem Programmierstil gibt es auch diese Probleme die du beschreibst nur in sehr eingeschränktem Maße.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Du könntest die Befehle in ein Dictionary packen (String, Action) und
    dann eine Evaluation Method machen.

    Dann rufst Du einfach auf Evaluatee("DeinBefehl1")
    und in der Evaluatee Methode kannst Du dann ja Events feuern / ein Property hochzählen
    wie Du lustig bist.
    Danke für die beiden Antworten!

    @RodFromGermany
    Nochmal zum warum:

    Ich habe ein (sehr) rudimentäres Framework (eigentlich passt diese Bezeichnung nicht wirklich) geschrieben, mit dem die Kollegen aus meinem Institut zukünftig bestimmte technische Problemstellungen modellieren werden. Die Leute sind alles Ingenieure mit nur oberflächlichen Programmierkenntnissen. Ich habe also versucht mein Framework nach gewissen Standards zu implementieren (Objektorientierung, Modularität, Robustheit gegen Fehler und Fehlbedienung, Schichtenarchitektur, etc etc etc) damit meine Kollegen möglichst wenig falsch machen und mit einem Baukasten ihre Probleme modellieren können. In diesem Zusammenhang habe ich mich auch mit dem Konzept von User Centered Design beschäftigt und wie ich möglichst viel davon bereits in mein Framework integrieren kann, ohne das Andere sich erst ausführlich mit der Theorie beschäftigen müssen - wenn ihr so wollt, ein Rundumsorglospaket für Programmierlaien um standardisiert einen ganz bestimmten Typus von Anwendungen zu erstellen.

    Dieses Framework greift z.B. immer wieder auf externe Anwendungen zu - das kann u.U. ziemlich lange dauern, ein ProgressBar wäre das ideale Control um Nutzer über den Bearbeitungsfortschritt zu orientieren. Wie bringe ich nun meine Kollegen dazu, ProgressBar richtig einzusetzen?

    Der Grundgedanke war nun also, ProgressBar soweit zu automatisieren, dass die gesamte manuelle Konfiguration entfällt und um diese lahmen COM-Zugriffe (aber auch File-Operationen, numerische Geschichten u.a.) einfach eine Abschnittsmarkierung gesetzt wird und der Rest erledigt sich von selbst.

    @thefiloe
    Könntest du etwas differentierter antworten? Ich schätze die Kommentare von erfahrenden Usern wie dir sehr, aber deinem Post kann ich wenig entnehmen was mich gerade weiter bringt. Es kann wirklich gut sein, dass meine Idee totaler Quatsch ist, aber dann ist mir immer noch nicht klar warum :)

    Ich muss sagen, ich komme mir z.B. blöd dabei vor, wenn ich an eine externe CAD-Anwendung eine Sequenz von ~50 Befehlen schicke und hinter jeden einzelnen Befehl PerformStep setzen muss (und nein, dass lässt sich nicht in eine Schleife stopfen)....

    @Daniel Baumert
    Danke für den Ansatz, dass werde ich gleich mal anschauen.

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

    Es wird dir da aber auch nicht viel anderes übrig bleiben. Meistens stehen ProgressBars in Verbindung mit Daten die in einer Schleife oder ähnlichem abgearbeitet werden. Es ist nur selten der Fall, dass einzelne "Befehle" so lange dauern, dass es sich rentiert dafür eine ProgressBar zu machen. Bei CAD mag das natürlich der Fall sein. Ist aber auch fraglich ob hier eine ProgressBar mit Fortschritt tatsächlich Sinn macht. Diese ~50 CAD Befehle werden nicht alle gleich lange brauchen bzw. lassen sich nicht vergleichen. Wenn du 50, vom Aufbau identische, Datensätze verarbeitest kannste sagen 10 von 50 Datensätzen habe ich verarbeitet. Bei den Befehlen sehe ich da eher ein Problem.
    Würde mir da von dem her sowieso überlegen, da nicht die ProgressBar im marquee Style einzustellen und darunter zu schreiben welchen Befehl du gerade verarbeitest bzw. was das Programm gerade macht.
    Hier Code sinnvoll zu vereinfachen kann schwer werden. Ich kenne natürlich deinen Code nicht und die Tatsache, dass eine Schleife nicht in Frage kommt lasse ich mal dahin gestellt.
    Aber was du z.B. versuchen könntest was je nach Situation den Code etwas dynamischer gestaltet wäre z.B. sowas:

    VB.NET-Quellcode

    1. ​Module Module1
    2. Sub Main()
    3. Dim foo As New Foo()
    4. foo.Run()
    5. Console.ReadKey()
    6. End Sub
    7. End Module
    8. Public Class Foo
    9. Private _progress As Integer
    10. Public Sub Run()
    11. ExecuteCommand(Sub() Math.Pow(10, 100))
    12. ExecuteCommand(Sub() Math.Sin(Math.PI))
    13. ExecuteCommand(Sub() Math.Exp(10))
    14. End Sub
    15. Private Sub ExecuteCommand(ByVal action As Action)
    16. action.Invoke()
    17. 'Fortschritt hier aktualisieren:
    18. '...
    19. _progress += 1
    20. Console.WriteLine("Ausgeführte Befehle: {0}", _progress)
    21. End Sub
    22. End Class


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    @thefiloe und @Daniel Baumert

    Vielen Dank für eure Beiträge - damit habe ich die notwendigen Schlagwörter (v.a. Actions) um mir etwas zu basteln. Mal sehen ob sich damit ein Konzept umsetzen lässt, dass dann in der Handhabung tatsächlich einfacher und weniger fehleranfällig ist als der konventionelle Weg. So richtig glücklich bin ich mit der Lösung nicht - aber seis drum :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „cl10k“ ()

    Beim lesen hab ich mich gefragt ob ich einfach die Aufrufe zu PerformStep zählen kann und schau da es funkioniert. Ob du das jetzt wirklich für dein Projekt brauchen kannst musst du überlegen, aber ich wüsste nicht wo ich es sonst posten soll :D

    Spoiler anzeigen

    AutoProgressBar.cs:

    C#-Quellcode

    1. public partial class AutoProgressBar : ProgressBar
    2. {
    3. public AutoProgressBar()
    4. {
    5. InitializeComponent();
    6. }
    7. public void AutoProgressbarStart()
    8. {
    9. MethodInfo targetMethod = typeof(Form1).GetMethod("Method1", (BindingFlags)62);
    10. MethodInfo myMethod = typeof(AutoProgressBar).GetMethod("PerformStep", (BindingFlags)62);
    11. Int32 performStepToken = myMethod.MetadataToken;
    12. Byte[] performStepByte = new Byte[5];
    13. performStepByte[0] = (Byte)System.Reflection.Emit.OpCodes.Callvirt.Value;
    14. BitConverter.GetBytes(performStepToken).CopyTo(performStepByte, 1);
    15. MethodBody targetMethodBody = targetMethod.GetMethodBody();
    16. Byte[] methodBody = targetMethodBody.GetILAsByteArray();
    17. Int32 max = Count(methodBody, performStepByte);
    18. this.Invoke(new MethodInvoker(() =>
    19. {
    20. this.Value = 0;
    21. this.Maximum = max;
    22. this.Step = 1;
    23. }));
    24. }
    25. private static unsafe Int32 Count(byte[] haystack, byte[] needle) //http://stackoverflow.com/questions/283456/byte-array-pattern-search/5227131#5227131
    26. {
    27. Int32 count = 0;
    28. fixed (byte* h = haystack) fixed (byte* n = needle)
    29. {
    30. long i = 0;
    31. for (byte* hNext = h, hEnd = h + haystack.LongLength; hNext < hEnd; i++, hNext++)
    32. {
    33. bool found = true;
    34. for (byte* hInc = hNext, nInc = n, nEnd = n + needle.LongLength; found && nInc < nEnd; found = *nInc == *hInc, nInc++, hInc++) ;
    35. if (found) count++;
    36. }
    37. return count;
    38. }
    39. }
    40. public void PerformStep(string str){
    41. this.Invoke(new MethodInvoker(() =>
    42. {
    43. this.PerformStep();
    44. }));
    45. }
    46. }

    Form1:

    C#-Quellcode

    1. public partial class Form1 : Form
    2. {
    3. public Form1()
    4. {
    5. InitializeComponent();
    6. }
    7. private void button1_Click(object sender, EventArgs e)
    8. {
    9. Task.Factory.StartNew(Method1);
    10. }
    11. private void Method1()
    12. {
    13. autoProgressBar1.AutoProgressbarStart();
    14. autoProgressBar1.PerformStep("");
    15. Thread.Sleep(1000);
    16. autoProgressBar1.PerformStep("");
    17. Thread.Sleep(2000);
    18. autoProgressBar1.PerformStep("");
    19. Thread.Sleep(3000);
    20. autoProgressBar1.PerformStep("");
    21. Thread.Sleep(4000);
    22. autoProgressBar1.PerformStep("");
    23. Thread.Sleep(5000);
    24. autoProgressBar1.PerformStep("");
    25. }
    26. }

    Total cool das du dir die Mühe gemacht hast über meine Idee nachzudenken!

    Ich muss mir das mal in Ruhe anschauen - ich kann c# (noch) nicht so flüssig lesen und verstehen (mal parallel in einem vb-Konverter anschauen).

    Ohne es bisher wirklich verstanden zu haben, übergibst du den Befehl als Argument an PerformStep? Warum die Sleeps?

    lg Christian
    Naja die sleeps sind einfach nur da um fortschritt zu simulieren.
    Hier in dem bsp spielt der parameter keine rolle, ich hatte nur überlegt ihn zu benutzen um die aufrufe den verschiedenen objekten zuordnen zu können. Dann macht zwar die aktuelle implementierung als instanz methode keinen sinn aber ist ja auch nur nen testprojekt.

    die zuordnung hätte dann über das string metadatatoken, den 4 bytes hinter dem ldstr opcode 0x72, stattgefunden

    C#-Quellcode

    1. /* 72 | (70)00004B */ ldstr ""
    2. /* 6F | (06)000004 */ callvirt instance void AutoProgressbar.AutoProgressBar::PerformStep(string)

    tolio schrieb:


    die zuordnung hätte dann über das string metadatatoken, den 4 bytes hinter dem ldstr opcode 0x72, stattgefunden


    Mit diesem Satz hast du mich aber sowas von meilenweit abgehängt. :)

    Ich werde dein Beispiel mal heute Nachmittag zerpflücken und dann garantiert mit einem ganzen Haufen von Fragen zurückkommen...
    mich auch. mir sieht das eh viel zu kompliziert aus, bei mir wäre das einfach:

    VB.NET-Quellcode

    1. Public Class ActionProgressbar : Inherits ProgressBar
    2. Private _Actions As IList(Of Action)
    3. Public Property Actions() As IList(Of Action)
    4. Get
    5. Return _Actions
    6. End Get
    7. Set(ByVal NewValue As IList(Of Action))
    8. If Object.Equals(NewValue, Me.Actions) Then Return
    9. _Actions = NewValue
    10. Value = 0
    11. Minimum = 0
    12. Maximum = _Actions.Count
    13. End Set
    14. End Property
    15. Public ReadOnly Property IsFinished As Boolean
    16. Get
    17. Return Value >= Actions.Count
    18. End Get
    19. End Property
    20. Public Function MoveNext() As ActionProgressbar
    21. Actions(Value)()
    22. Value += 1
    23. Return Me
    24. End Function
    25. End Class

    Und im Form zB

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub btReset_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btReset.Click
    3. Dim actions As New List(Of Action)
    4. actions.Add(Sub() Label1.Top -= 5)
    5. actions.Add(Sub() Label1.Top -= 5)
    6. actions.Add(Sub() Label1.Left -= 5)
    7. actions.Add(Sub() Label1.Left -= 5)
    8. actions.Add(Sub() Label1.Top += 10)
    9. actions.Add(Sub() Label1.Left += 5)
    10. actions.Add(Sub() Label1.Left += 5)
    11. ActionProgressbar1.Actions = actions
    12. End Sub
    13. Private Sub btStep_Click(sender As Object, e As EventArgs) Handles btStep.Click
    14. If ActionProgressbar1.IsFinished Then Throw New Exception("hab doch gesagt, ist finsished!")
    15. If ActionProgressbar1.MoveNext.IsFinished Then MessageBox.Show("finished")
    16. End Sub
    17. End Class
    Zur Endüberschreitung die Exception kann man auch in die PB einbauen - kann man auch lassen, denn da fliegt eh eine ArgumentOutOfRange-Exception, was ja den Fehler schon ziemlich genau angibt.

    Edit: Ein besser wiederverwendbares Designe wäre aber, einfach paar Extensions an die Progressbar-Klasse dranzuproggen:

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Public Module ProgressbarExtensions
    3. Private _Actions As New ConditionalWeakTable(Of ProgressBar, Action())
    4. <Extension> _
    5. Public Function Setup(pb As ProgressBar, actions As IEnumerable(Of Action)) As ProgressBar
    6. _Actions.Remove(pb)
    7. Dim acts = actions.ToArray
    8. _Actions.Add(pb, acts)
    9. pb.Minimum = 0 : pb.Maximum = acts.Length : pb.Value = 0
    10. Return pb
    11. End Function
    12. <Extension> _
    13. Public Function Movenext(pb As ProgressBar) As ProgressBar
    14. _Actions.GetOrCreateValue(pb)(pb.Value)()
    15. pb.Value += 1
    16. Return pb
    17. End Function
    18. <Extension> _
    19. Public Function IsFinished(pb As ProgressBar) As Boolean
    20. Return _Actions.GetOrCreateValue(pb).Length <= pb.Value
    21. End Function
    22. End Module

    Dateien

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()