Excel: Befehlsschaltflächen funktionieren nicht mehr

Dieser Thread ist Teil einer Diskussion zu einem Artikel:  Zum News-Artikel gehen

Fritz50

Stammgast
Guten Abend

Seltsames Verhalten eine Excel Applikation mit ActiveX-Buttons

Vorerst ohne weit auszuholen. Da habe ich eine Anwendung, bei der ich in einigen Zellen ein DblClick Ereignis abarbeite und in bei einigen Zellen beim Change-Ereignis etwas mache.

Weiter habe ich vier Befehlsschaltflächen, die ich auch gruppiert habe (damit die Positionen untereinander satbil bleiben). Eine Befehlsschaltfläche ruft auch die Changeprozedur auf.

Soweit so gut. Dann passiert es oft, dass das Programm nicht mehr auf die Schaltflächen reagiert. Auch wenn ich die Applikation schliesse, oder speichere und erneut öffne, die Schaltflächen reagieren nicht mehr. Dabei sehe ich im Entwurfsmodus, dass der Code zu jeder Schaltfläche da ist und der Curor bei „Code anzeigen“ an die richtige Stelle springt.

Wenn ich einen Breakpoint setzte, wird dieser nie erreicht, d.h. die Prozeduren werden nicht mehr aufgerufen.

Nun habe ich etwas Interessantes entdeckt. Die „Blockade“ löst sich, wenn ich im Entwurfmodus die gruppierten Befehlsschaltflächen minimal verschiebe und dann den Entwurfmodus wieder verlasse (und dann das ganze speichere fürs nächste mal). Hat jemand ähnliche Erfahrungen/Beobachtungen gemacht.

Wie prüft man den Link von der Schaltfläche zum Code? Eigentlich sollte diese Blockade ja nicht auftreten. Ist die Doppelnutzung der Change-Ereignis-Prozedur heikel? Falls das Ereignis eine Iteration auslöst, müsste diese doch bei Abbruch gespühlt werden. Es könnte sein, dass die Blockade nur im Zusammenhang mit dieser Schaltfläche auftritt und dann alle Schaltflächen blockiert. Ich habe das noch etwas zu wenig aufmerksam beobachtet.

Gruss Fritz
 

Fritz50

Stammgast
Guten Morgen Andreas

Die Liste ist ein Weininventar. Links sind Angaben zum den Flaschen. Rechts ist das Weingestell dargestellt. Nun kann man mit anklicken hin und her springen (Wo ist dieser Wein oder welche Flasche liegt hier?).
Wenn man die Liste editiert, wird die Position nachgeführt. Mit nachführen aus/ein will ich verhindern, dass Change-Ereignisse auftreten, weil das mühsam ist (siehe unten). Mit aktualisieren wird die "Weinablage" aktualisiert.
Dann ist da noch ein Thema (und darum habe ich nachführen ein/aus gemacht).
Wenn ich die Liste ändere durch Löschen oder Ergänzungen, wird die Position im Gestell aktualisiert. Dann ist diese Zelle aktiviert und ich muss mühsam wieder in die Liste zurück. Ich habe das noch nicht weiter bearbeitet. Wahrscheinlich könnte ich die Zelle in einer Variablen retten und an Schluss wieder zurückspringen. > Das habe ich jetzt versucht und es ist so: erst Target sichern, am Schluss Target.Activate, ganz einfach. (In dieser Mappe aber nicht drin).
Oder kann ich die ActiveCell bestehen lassen und die Aktualisierung(en) unabhängig lösen.
Es wäre auch angenehm. wenn ich die Ablage immer sehe, auch wenn Flaschen weiter unten selektiert sind. Mit 2 Fenstern gehts jedenfalls auch nicht, weil immer die aktive resp. selektierte Zelle in beiden Fenstern erscheint.

Gruss
Fritz
 

Anhänge

  • Weinkeller_Inventar-Organisator(4e).zip
    109,6 KB · Aufrufe: 3
Zuletzt bearbeitet:

Fritz50

Stammgast
Um zurückzuspringen muss ich Target nicht einmal sichern, einfach am Ende der Prozedur Target.Activate. Target ändert in der Prozedur nicht.
 

nochEinAndreas

Stammgast
Hallo Fritz,

ich habe versucht, dein Ursprungsproblem (Schaltflächen reagieren nicht mehr) nachzuvollziehen. Das ist mir nicht gelungen. Bei mir hat immer alles funktioniert. Ich glaube, du musst mal ganz genau Buch führen über deine Aktionen, Klicks und Änderungen, um der Sache auf die Spur zu kommen. Wenn bei dir die Knöpfe ab und zu nicht funktionieren, muss es einen Grund geben. Das ist sicher viel Fummelarbeit. Du musst jede deiner Aktionen genau dokumentieren. Dann kannst du vermutlich dahinter kommen.

Zu deinem anderen Thema: Selektierungen, die sich durch den Makro ändern. Da hast du ja selber schon eine Lösung gefunden. Allerdings ist es in meinen Augen eine unnötige Lösung. Ich habe mal grob deinen Code angeschaut. Mir sind da viel zu viele unnötige Selects drin. Ein Beispiel aus der Prozedur Worksheet_aktualisieren:
Das steht

Code:
Target.Offset(0, 4).Activate
Range(ActiveCell.Value2).Select
            
With Selection.Font
        .ThemeColor = xlThemeColorDark1
        .TintAndShade = 0
End With

Das geht einfacher ohne Activate und Select:

Code:
With Range(Target.Offset(0, 4)).Font
        .ThemeColor = xlThemeColorDark1
        .TintAndShade = 0
End With

Hier wird deine Auswahl nicht geändert, und du kannst dir das Target.Activate am Ende der Prozedur sparen.

Jede Struktur der Form

Code:
Range("...").Select
With Selection ...

kann man vereinfachen zu

Code:
With Range("...")

Dann bleibt auch das Herumhüpfen der Auswahl weg. Das kostet erstens Zeit. In deinem Fall hier vermutlich noch nicht merklich. Aber wenn du z.B. aktualisieren lässt, läuft der Makro durch 168 Zellen, immer mit Activate und Select. Da läppert sich ganz schön Zeit zusammen. Und zweitens sieht das unschön aus.

Übrigens hast du da noch zwei Fehler beim Aktualisieren:
  • Die Prozedur "Worksheet_aktualisieren" darf kein "Private Sub" sein. Sie steht ja im Modul1, soll aber aus dem Modul der Weinkeller-Tabelle aufgerufen werden. Als "Private Sub" wird sie von dort aus nicht gefunden. Sie darf nur "Sub" sein, ohne "Private".
  • In der Prozedur "Worksheet_aktualisieren" steht die Zeile
    If CommandButton_enable_disable.Caption = "nachführen aus" Then
    Hier ist "CommandButton_enable_disable" nicht definiert, und VBA versucht den Ausdruck als Variable zu interpretieren.
    Aber du willst ja, dass es sich auf den Button auf dem Weinkeller-Blatt bezieht. Also musst du schreiben:
    If Weinkeller.CommandButton_enable_disable.Caption = "nachführen aus" Then
    Weinkeller ist ja der Codename der Blattes Weinkeller. Ich vermute, du hast den Codenamen vergeben. sonst würde er einfach Tabelle... heißen.
    Wenn du im VBA-Editor Weinkeller und einen Punkt eintippst, dann schlägt dir Intellisense auch den Namen des Buttons vor.
Weiter habe ich mir deinen Code erst mal nicht angeschaut.
Wie gesagt: Es können viele, vermutlich alle Selects und Activates raus. Das macht das ganze schlanker, schneller und fürs Auge ansprechender.

Gruß und schönen Abend,
Andreas
 

Fritz50

Stammgast
Hallo Andreas

Danke für die Hinweise. Die Prozedur 'Worksheet aktualisieren' ist eine Kopie vom Worksheet_change-Ereignis, weil ich versuchweise mit aktualisieren nicht die gleiche Prozedur wie beim Change durchlaufen wollte. So ist das Private noch geblieben. Muss noch prüfen, ob die Routine nicht erreicht wird. Dann zum CommandButton_enable_disable.Caption. Ist die Eigenschaft ausserhalb der Tabelle nicht verfügbar? (Du hast sie wahrscheinlich darum als (nicht deklarierte) Variable bezeichnet.

Dann habe ich eine weitere Unsicherheit (weiss nicht ob wir das schon einmal diskutiert haben, habs nicht grad gefunden. Ich möchte die eben verlassene Zelle nachträglich manipulieren (Farbe ändern). Das möchte ich in Worksheet_SelectionChange handeln.
Wenn ich darin einen Range Public rCellLeft As Range deklariere, kann ich diesen nicht initialisieren. Ich muss die Zelle ja verändern, bevor ich die aktuelle Zeile speichere. Das erst Mal ist der Range definiert und es gibt einen Fehler.

Die folgende Prozedur löscht den Hintergrund des ganzen Bereichs (es ist aber nur eine Zelle eingefärbt)

Private Sub Worksheet_SelectionChange(ByVal Target As Range) With Range("L2:X17").Interior ' einfachere Lösung .Pattern = xlNone .TintAndShade = 0 .PatternTintAndShade = 0 End With End Sub

In der folgenden Prozedur möchte nur den Hintergrund der eingefärbten und eben verlassenen Zelle löschen.
Dazu sichere ich diese Zelle in rCellLeft. rCellLeft ist lokal als Static deklariert.

Private Sub Worksheet_SelectionChange(ByVal Target As Range) Static rCellLeft As Range ' = Range("C2":"C2") rCellLeft kann (hier) nicht initialisiert werden MsgBox ("Worksheet_SelectionChange, ActiveCell: " & rCellLeft.Address & Target.Address) With rCellLeft.Interior .Pattern = xlNone .TintAndShade = 0 .PatternTintAndShade = 0 End With Set rCellLeft = Target ' Zelle als Range sicher fürs nächste mal End Sub


geht das gar nicht?

Gruss
Fritz

P.S. kriege die Darstellung für den Code nicht mehr hin
 
Zuletzt bearbeitet:

nochEinAndreas

Stammgast
Hallo Fritz,

das mit dem "merken" der vorherigen Zelle hatten wir schon mal. Hier noch mal eine kurze Erklärung:
  1. Du brauchst eine globale Variable, die die "vorherige" Zelle darstellt. Hierzu schreibst du in ein Standardmodul:
    Code:
    Public vorherigeZelle As Range
  2. Beim Öffnen der Mappe belegst du diese Zelle mit der aktuellen Zelle. Hierzu schreibst du in den Codebereich von "DieseArbeitsmappe":
    Code:
    Private Sub Workbook_Open()
       Set vorherigeZelle = ActiveCell
    End Sub
  3. Beim Wechseln der Zelle soll die vorherige Zelle entfärbt und die neue Zelle gefärbt werden. Und dann muss die neue Zelle für den nächsten Zellenwechsel als "vorherige" gesetzt werden. Dazu schreibst du in den Codebereich des entsprechenden Tabellenblattes:
  4. Code:
    Private Sub Worksheet_SelectionChange(ByVal Target As Range)
       ' entfärbe die vorherigen Zelle:
       vorherigeZelle.Interior.Color = xlNone
    
       ' färbe die aktuelle Zelle
       Target.Interior.Color = RGB(255, 0, 0)
    
       ' setzte die aktuelle Zelle als vorherige,
       ' für den nächsten Aufruf von Worksheet_SelectionChange
       Set vorherigeZelle = Target
    End Sub

Gruß, Andreas
 

Fritz50

Stammgast
Ich dachte/hoffte, dass es mit einer statischen lokalen Variable möglich wäre.
Das Problem ist, dass die Variable/Range einen Anfangswert braucht, oder man müsste die Abfrage beim allerersten Aufruf "umgehen", eine in Initialisierung tricksen.

Es gibt doch für Variablen auch eine Deklaration mit Initalierungswert. Z.B.

Dim i As Integer = 7 und wenn statisch, meine ich sollte auch das gehen:
Static iS As Integer = 4 ??
und für Rangevariablen auch ....

Bezüglich Einfärben / Einfärbunglöschen will ich die rote Einfärbung löschen, wenn ich die Zelle (Position) verlasse um das "Bild" nicht falsch zu interpretieren.
Das Einfärben geschieht dann, wenn ich in der Liste einen Namen doppelklicke und der "Cursor" dann zur entsprechenden Position/Zelle springt.

Dann ist mir die erste Lösung doch fast sympatischer: Ich setzte einfach den Hintergrund in allen Zellen dieses Bereichs zurück. Der Bereich ist ja immer der gleiche. Die Rechenzeit ist wohl beim Vergleich der Lösungsvarianten kein Thema. Dann muss ich mir die Zelle nicht sichern

Gruss
Fritz
 

nochEinAndreas

Stammgast
und noch eine Anmerkung:
Es gibt doch für Variablen auch eine Deklaration mit Initalierungswert. Z.B.

Dim i As Integer = 7 und wenn statisch, meine ich sollte auch das gehen:
Static iS As Integer = 4 ??
Nein, das geht nicht. Diese Konstruktion funktioniert nur mit Konstanten:
Const i As Integer = 7
Und der Wert ist dann halt konstant und eben nicht veränderbar.

Im Fall von meinem Beispiel kommst du auch um eine globale Variable nicht rum, weil du den Wert zum Initialisieren im Workbook_Open brauchst und zum Neubelegen im Worksheet_Selection. Da reicht eine Static-Variable nicht aus, da sie nur in einer Routine gelten kann, dort wo sie dimensioniert wird.

Gruß, Andreas
 

Fritz50

Stammgast
der feine Unterschied, der Zeit und Nerven kosten kann ;-)
Bei Konstanten muss die Zuweisung ja wohl in der gleiche Zeile sein, sonst wärs keine Konstante (mehr).
Ist VBA immer im Zusammenhang mit einer Office Anwendung? Und VB unabhängig (auf Windows, Linux, Android u.a.)

Gruss
Fritz
 

nochEinAndreas

Stammgast
Also ich kenne VBA nur als "Beiwerk" zu Office-Programmen. Ähnliches gibt es wohl auch für nicht-MS Office-Varianten. Da kenne ich mich aber überhaupt nicht aus.

Ja, Konstanten werden immer bei der Dimensionierung mit einem Wert belegt.

Mit VB habe ich nur ganz wenig rumgespielt. Und auch nur bis VB6. Es ist von Microsoft und deswegen, so viel ich weiß, auch nur auf Windows lauffähig. Es ist eine eigenständige Programmiersprache, ohne Zusammenhang mit Office-Programmen.
Den Nachfolger von VB6, VB.NET kenne ich überhaupt nicht.
Wenn du dich schlauer machen willst, gugel oder binge (oder sonst was) mal nach "VB VBA unterschied".

Gruß, Andreas
 
Oben