Zu Doof für Enable=False ???

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von 0-Plan.

    Zu Doof für Enable=False ???

    Hallo Gemeinde

    Vielleicht könnt ihr mir hier bei einem allgemeinen Verständnisproblem helfen.

    Bisher ging ich immer davon aus, dass ich mit z.B. Button1.Enable=False den Button inaktiv schalte und er auch nicht bedienbar ist.
    Nun stellt es sich mir aber so dar als würde der Klick auf einen Enable=False-Button nicht ignoriert sondern nur zwischengespeichert
    bis der Button wieder Enable=True ist. Sobald er wieder "Enable" ist wird der Klick dann nachgeholt.

    Hab ich das Ganze jetzt falsch verstanden oder programmiere ich das nur falsch?


    Zur Demonstration reicht ein Form, ein Button eine Multiline-Textbox mit Scrolleiste und einige Zeilen Code.
    (Nicht schön, aber erfüllt seinen Demo-Zweck)

    Starte ich das und Klicke den Butten wird die Textbox vollgeschrieben. Dauert bei mir ca. 5 Sekunden
    Starte ich und Klicke 2 x schnell hintereinander wird der Ablauf auch 2 x ausgeführt und in der Textbox
    steht der ganze Kram 2 x drin obwohl beim 2. Klick der Button eindeutig auf enable=false steht. ;(


    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim i As Integer = 0
    4. TextBox1.Text = "Klick" & DateTime.Now.Second.ToString & "-" & DateTime.Now.Millisecond.ToString & vbCrLf & TextBox1.Text
    5. Button1.Enabled = False
    6. While i < 1000
    7. TextBox1.Text = i & " / " & DateTime.Now.Second.ToString & "-" & DateTime.Now.Millisecond.ToString & vbCrLf & TextBox1.Text
    8. i += 1
    9. End While
    10. Button1.Enabled = True
    11. End Sub
    12. End Class


    Vielleicht kann mir einer der Experten hier etwas auf die Sprünge helfen.
    Vielen Dank schon mal

    Gruß

    Ben

    MrTrebron schrieb:

    Was soll das False setzen dann bringen?


    Mal rein Theoretisch. Angenommen die Schleife braucht fuer einen ganzen durchlauf 10 Sekunden, was wenn in dieser Zeit wieder auf den Button geklickt wird? Es wird erneut ausgefuehrt, evtl. unnoetig oder ungewollt. Das der Button nochmal trotz Enabled = False klickbar ist, liegt wohl daran da die Messages der Window Message Loop nicht mehr ausgewertet und ausgefuehrt werden koennen, da das so im GUI Thread laeuft und dieser auch Freezed, versuch mal waehrend der Aktion die Form zu verschieben. Hier waere Threading angebracht.

    Wobei auch das BOESE Apllication.DoEvents vor der Schleife(nach BTN.Enabled=False) abhilfe ohne Threading schaffen wuerde.
    And i think to myself... what a wonderfuL World!
    Hallo

    Ja, die Sub wird mit dem 1. Klick gestartet und der Ablauf macht sein Ding.
    Durch das Enable=False hätte ich erwartet, dass der Benutzer rumklicken kann wie er will aber nix passier.
    Erst wenn der Ablauf durch ist und alle Anzeigen aktualisiert sind wird der Button wieder freigegeben.
    Erst dann sollte eigentlich die Bedienung wieder möglich sein.
    Is aber nicht so. Durch den 2. Klick auf den Button zum Zeitpunkt wo dieser noch Enable=False ist wird der Ablauf
    nach Ende des 1. Durchlaufs gleich nochmal gestartet
    Ja,

    Der Button wird Disabled dargestellt.
    Die GUI friert dann kurzftistig ein.
    Im richtigen Programm ca. 1 Sekunde.
    War so weit also kein Problem
    Das BÖSE Application.DoEvents() endert am Verhalten nichts.
    Habe den Butten Testweise auch Visible=False geschaltet.
    Dann is er nicht mehr zu sehen.
    Ein 2. Klick auf die jetzt leere Fläche startet den Ablauf nach Ende des 1. Durchlaufs ebenfalls ein 2. mal
    Im GUI-Thread lange Berechnungen auszuführen ist meistens keine gute Idee. Denn wenn der GUI-Thread an Deinen Berechnungen arbeitet, dann kann er die GUI gerade nicht aktualisieren.
    Warum das passiert, was Du im Startpost erklärst, ist ganz einfach:
    Bei Windows-Anwendungen gibt es eine sogenannte "Message Pump". Das ist einfach eine Liste von Nachrichten an die Anwendung. Das funktioniert so: Wenn Du mit der Maus über den Button gehst und dann klickst, dann fügt Windows in diese Liste eine Nachricht ein, in der steht: "Der User hat mit der linken Maustaste hier geklickt." (Stark vereinfacht.) Der GUI-Thread arbeitet diese Nachrichten nacheinander ab und macht dann halt gewisse Sachen. Im Falle des Button1 ist das folgendes:

    Quellcode

    1. Nächste Nachricht (nennen wir sie "Nachricht 1") aus der Liste holen.
    2. Aha, der User hat auf den Button geklickt.
    3. Ist dieser Button aktiv (ist Enabled auf True gesetzt)?
    4. Ja, ist er.
    5. Button.OnClick() ausführen.
    6. Darin Button.Click-Event auslösen.
    7. Und dadurch Button1_Click() ausführen.

    Hier setzt Du jetzt Button1.Enabled auf False. Aber der GUI-Thread arbeitet immer noch. Inzwischen klickst Du nochmal auf den Button und Windows fügt in die Liste nochmal so eine Nachricht ein ("Nachricht 2").
    Der GUI-Thread wird den komplexen Berechnungen fertig. Du setzt jetzt Button1.Enabled wieder auf True. Jetzt geht der GUI-Thread wieder durch die ganzen Methodenaufrufe zurück und damit ist Nachricht 1 abgearbeitet.
    Der GUI-Thread macht dann genau gleich weiter:

    Quellcode

    1. Nächste Nachricht (Nachricht 2) aus der Liste holen.
    2. Aha, der User hat auf den Button geklickt.
    3. Ist dieser Button aktiv?
    4. Ja, ist er. (Denn Du hast das ja vorher wieder auf True gesetzt.)
    5. Button.OnClick() ausführen.
    6. Darin Button.Click-Event auslösen.
    7. Und dadurch Button1_Click() ausführen.

    Und jetzt wird die Methode nochmal ausgeführt.

    Ich hoffe, das konnte erklären, warum das passiert.


    Wie löst man das jetzt?
    Du musst die komplexen Berechnungen in einen Nebenthread auslagern. Dann kann der GUI-Thread weiter fleißig Nachrichten abarbeiten (und dann kann man das Fenster auch weiter herumziehen/vergrößern/verkleinern/minimieren/etc.) und der Nebenthread kümmert sich um die Berechnungen.
    Dafür gibt es eine Vielzahl an Herangehensweisen. Zum Beispiel kannst Du einen BackgroundWorker verwenden. Da Du ein Einsteiger zu sein scheinst, würde ich Dir erst mal das empfehlen. Das Tutorial ist inzwischen etwas in die Jahre gekommen, aber besonders für Einsteiger ist es wichtig, zu lernen, richtig mit Multithreading umzugehen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:

    Das Tutorial ist inzwischen etwas in die Jahre gekommen, aber besonders für Einsteiger ist es wichtig, zu lernen, richtig mit Multithreading umzugehen.
    Naja - den BW halte ich aber nicht für "richtigen" Umgang mit Threading.

    IMO gibts eh keinen "besonders leichten Einstieg". Um mit Threads umzugehen, muss man minimal was davon verstehen.
    ZB die Methode, die der TE zeigt - 1001 mal Text in eine Textbox einzufügen - sowas ist für Threading vollkommen ungeeignet - egal, wie mans versucht, anzufassen.

    Auf Controls kann man numal nicht nebenläufig zugreifen - das muss man halt verstehen.
    Also richtiger Umgang mit Threads fängt schon damit an, zu bestimmen, wo Nebenläufigkeit opportun, anwendbar bzw. unumgänglich ist. Weil wenns Alternativen zur Nebenläufigkeit gibt, sind die fast immer vorzuziehen.

    Um nochmal auf Post#1 einzugehen: Das für dich eigenartige Verhalten kommt von der Vollbelastung des Main-Threads her - in dem auch alle automatischen Control-Aktualisierungen erfolgen - durch deine Schleife.
    Lässt sich in diesem Falle nicht abstellen, denn deine Schleife besteht numal daraus, dass 1001 mal der Textbox-Text erneuert wird, mit immer längeren Texten. Also Nebenläufigkeit scheidet aus, denn einen Textbox-Text kann man nicht im NebenThread erneuern.
    Also man kann theoretisch die Schleife nebenläufig machen, aber innerhalb der schleife müsste man jede Umdrehung wieder in den HauptThread wechseln, und hätte so den Performance-Fresser eben doch haupt-läufig drinne.

    "Richtige" Lösung: Erstell den endgültigen Text erst in einer normalen String-Variablen her, und erneuere den Textbox-Text nur genau einmal, nämlich wenn die String-Variable fertig ist.
    Wirst sehen: das geht schneller als du gucken kannst, und damit ist dann ist auch die Geschichte mit dem Button disablen/enablen hinfällig. Das Problem wäre nicht eiglich "gelöst", sondern es wäre verschwunden.

    Etwas anderes wäre, wenn du dich nu genau mit Threading auseinandersetzen willst.
    Dann schreib aber erstmal eine Methode, für die Threading auch wirklich opportun ist. Also sie müsste mehrere Sekunden ununterbrochen laufen, und dabei nicht auf Controls zugreifen.
    Dann hätte man ein threading-geeignetes Problem, und könnte den Umgang mit solch erlernen (und insbesondere was besseres als den BW).

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

    Moin! :)
    Um deinen Code lessbarer zu machen, verwende String.Format.
    Ein kleines Beispiel für dich Siehe unten!
    BIG THX

    VB.NET-Quellcode

    1. Me.Text = String.Format("Dies ist ein kleines Beispiel {0}", Now.ToShortTimeString)
    Visual Basic.NET 8o
    MS-SQL
    8o
    Jou.
    @0-Plan Um den Effekt besser zu verdeutlichen, gib die Anzahl der Klicks in einem Label aus:

    VB.NET-Quellcode

    1. Private counter As Integer = 0
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. counter += 1
    4. Label1.Text = counter.ToString
    5. Label1.Update() ' Sofortige Anzeige
    6. '...
    7. End Sub
    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!