ContextMenu mit eigener Class in CustomControl

  • VB.NET

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    ContextMenu mit eigener Class in CustomControl

    Guten Morgen,

    nachdem ich nun fast die ganze Nacht nachgedacht und ausprobiert habe, habe ich keine Idee mehr und hoffe, es findet sich jemand, der/die mir helfen kann.

    Folgende Ausgangssituation:
    Ich habe diverse CustomControls geschrieben. Wenn nötig ist dabei über den Quelltext der Class ein ContextMenu mit eingebunden. Das alles läuft fehlerfrei.
    Nun sind einzelne CustomControls im Laufe der Zeit immer größer geworden und aus Gründen der Übersichtlichkeit wollte ich die ContextMenus in eine eigene Class geben, um diese dann im CustomControl mit den gewünschten MenüOptionen einzubinden.


    Mein Code sind dabei folgendermaßen aus (ich beschränke ihn hier auf 1 Option, weil's wohl mehr nicht braucht):

    Zunächst die Class für's ContextMenu

    VB.NET-Quellcode

    1. Public Class TBox_KontextMenu
    2. Inherits ContextMenu
    3. Public Event CM_Copy() ' aus Platzgründen hier nur mit einer Option!!!
    4. Public WithEvents MenuItem_Copy As New MenuItem()
    5. Public Sub New()
    6. MyBase.New()
    7. Me.Name = "FirstMenu"
    8. Call Created_ContextMenu()
    9. End Sub
    10. Friend Sub Created_ContextMenu()
    11. Me.MenuItems.Add(MenuItem_Copy)
    12. With MenuItem_Copy
    13. .Name = "CMI_Copy" : .Text = "Kopieren" : .ShowShortcut = True : .Shortcut = Shortcut.CtrlC
    14. AddHandler .Click, AddressOf MenuItem_Click
    15. End With
    16. End Sub
    17. Private Sub ContextMenu_Popup(sender As Object, e As EventArgs) Handles Me.Popup
    18. Dim SelLength As Integer = DirectCast(Me.SourceControl, CV_TextBox).SelectionLength
    19. MenuItem_Copy.Enabled = CBool(SelLength > 0)
    20. End Sub
    21. Private Sub MenuItem_Click(sender As Object, e As EventArgs)
    22. Select Case DirectCast(sender, MenuItem).Name
    23. Case "CMI_Copy"
    24. RaiseEvent CM_Copy()
    25. End Select
    26. End Sub
    27. End Class



    Dann die relevanten Zeilen im CustomControl:

    VB.NET-Quellcode

    1. Public Class CV_TextBox
    2. Inherits TextBox
    3. Private WithEvents myContext_Menu As TBox_KontextMenu
    4. Public Sub New()
    5. MyBase.New()
    6. Me.ContextMenu = New TBox_KontextMenu
    7. End Sub
    8. Private Sub myContext_Menu_CM_Copy() Handles myContext_Menu.CM_Copy
    9. Call Me.Copy()
    10. End Sub
    11. End Class



    Wenn ich nun das CustomControl in der Form einbinde und mit MouseRechts abrufe, läuft alles ganz normal. Das ContextMenu sieht wie gewünscht aus, die einzelnen Optionen sind aktiviert oder deaktiviert wie's sein soll. Auch der Event wird über die Zeile RaiseEvent CM_Copy() angesteuert. Nur ausgelöst wird dieser Event nicht, zumindest kommt in der Form nichts davon an. Bei der Gestaltung des CustomControls wurde mir der Handler myContext_Menu.CM_Copy ganz normal angeboten, so wie z.B. der Handler Me.TextChanged, der völlig normal ausgeführt werden würde.

    Weiß jemand Rat, wie der Event CM_Copy im CustomControl ausgelöst werden kann und feuert (also Zeile 11-13 ausführt)?

    So weit, so gut (oder eben schlecht). Ich hoffe nun, ich habe hier nichts vergessen und meine Beschreibung ist verständlich geblieben. Der Editor hier hat's mir nicht leichtgemacht und meistens gemacht, was er aber nicht ich wollte. 2 x neu angefangen und jetzt sieht's immer noch nicht so aus, wie's soll. Die vielen Leezeilen z.B. sind nicht gewollt, aber kommen auch nach dem löschen immer wieder. Geht es da anderen eigentlich genauso oder bin nur ich zu doof, um ihn richtig zu bedienen?

    Also, ich brauch jetzt noch 'ne Mütze Schlaf.
    Vielen Dank für Eure Hilfe.


    Nachtrag: Die ungewünschten Leerzeilen sind jetzt, nach dem zweiten Mal, löschen weg, aber ich traue mich nicht, die Zeilen wie im .Net-Editor einzurücken. Darum siehts jetzt so aus.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „claus“ ()

    @claus Erstell ein Rumpfprojekt, das Deinen Effekt reproduziert und hänge dies als ZIP ohne bin und obj-Ordner an.
    Teste vorher, dass dieser Code compiliert und Deinen Effekt reproduziert.
    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!
    Du schreibst im Code: Public WithEvents myContext_Menu As NumBox_KontextMenu
    Aber nirgends dafür ein New. Das heißt, dass das ContextMenu zwar deklariert wird, aber nie erzeugt wird, also existent gemacht wird. Dementsprechend kann es auch keine Events feuern, die man dann irgendwo abfängt. Stattdessen erzeugst Du in der Constructor-Region ein neues Kontetmenü, auf das aber nicht zugegriffen wird.
    Me.ContextMenu = New NumBox_KontextMenu
    Hast Du vergessen, da eine Zuweisung zu myConext_Menu zu machen?

    btw: Call sollte schon lange nicht mehr dafür verwendet werden, Prozeduren aufzurufen. Einfach das Call weglassen.
    Und bei MenuItem_Copy.Enabled = CBool(SelLength > 0) kannst Du das CBool weglassen. Der geklammerte Ausdruck ergibt eh den gewünschten Boolean-Wert.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @VaporiZed Etwas anders.
    @claus Du instanziierst Me.ContextMenu = New NumBox_KontextMenu, die Variable myContext_Menu bleibt Nothing.
    Die Basisklasse sollte ContextMenuStrip sein.
    Deine Events haben keine Argumente.Vielleicht tastest Du Dich ran, indem Du einem normalen Control ein eigenes ContextMenu zuweist.
    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!
    Interessanterweise bekomm ich es zum Laufen, wenn man nach der Instanziierung von myContext_Menu die Events per AddHandler mit den entsprechenden EventHandlern verdrahtet. Mit WithEvents und Handles - so wie es im geposteten Projekt steht - klappt es nicht. Mal sehen, wo sich da der Wurm verkrochen hat.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hallo Ihr Beiden!

    @Rod : Joop, mit der Instanzierung der Klasse hast Du recht. Peinlich, peinlich, daß ich das übersehen habe. Es liegt wahrscheinlich daran, daß ich bei allem, was Menüs betrifft, etwas ins Schwimmen gerate. So muß ich gestehen, daß sich mir der Unterschied zwischen "ContextMenu" und "ContextMenuStrip" und ihre jeweiligen Eigenheiten bisher nicht so wirklich erschlossen hat. Und gelesen dazu habe ich schon vieles.
    Deinen Rat bzgl des normalen Controls mit eigenem ContextMenu habe ich, denke ich, schon hinter mir. Wie ich eingangs meines 1. Beitrags hier schrieb waren das Control und die MenuClass in der ZIP-Datei früher 1 Datei. (Sind es noch, weil dies will ich jetzt ja ändern) . Da lief das ContextMenü fehlerfrei. Also ich denke, auch wenn mir der o.g. Unterschied nicht klar ist, das mit dem "normalen" ContextMenu bekomme ich schon hin. Ich dachte darum zunächst, die fehlende Instanzierung wäre der Grund des Scheiterns. Aber leider Nein. Das allein ist es nicht.

    @VaporiZed: Aufgrund des gerade für RfG geschriebenen erscheint mir Dein Ansatz mit den AddHandler interessant. Wie sieht das aus, was Du gemacht hast? Wie ich schrieb, läuft das bei mir alles bis inklusive dem RaiseEvent. Das läßt sich im Debug-Modus und/oder mit Haltepunkten einfach feststellen. Nur der Event feuert dann ins Leere. An den fehlenden Argumenten (wie RfG schreibt) liegt es nicht. Als Control und Contextmenu noch 1 Datei waren, hatte ich folgende Sub (hier auf eine Option gekürzt):

    Quellcode

    1. Private Sub Created_ContextMenu()
    2. myContext_Menu = New ContextMenu
    3. myContext_Menu.Name = "TBox_ContextMenu"
    4. myContext_Menu.MenuItems.Add(MenuItem_Copy)
    5. With MenuItem_Copy
    6. .Name = "MICopy"
    7. .Text = "Kopieren" : .ShowShortcut = True : .Shortcut = Shortcut.CtrlC
    8. AddHandler .Click, AddressOf MenuItem_Click
    9. End With 'MenuItem_Copy
    10. Me.ContextMenu = myContext_Menu
    11. End Sub


    Das Click-Event wurde dann wie folgt abgearbeitet:

    Quellcode

    1. Private Sub MenuItem_Click(sender As Object, e As EventArgs)
    2. Select Case DirectCast(sender, MenuItem).Name.ToUpper
    3. Case "MICOPY"
    4. If Me.SelectionLength > 0 Then
    5. Call Me.Copy()
    6. End If
    7. End Select
    8. End Sub


    Dabei funktioniert alles fehlerfrei.
    Wie gesagt, Dein Ansatz mit AddHandler würde mich interessieren. Würdest Du davon bitte etwas hier posten?

    Da ich ein ehrlicher Mensch bin, muß ich aber auch sagen, daß mich Deine Ausführungen im 1.Beitrag 2.Absatz weniger interessieren. Ich weiß, daß ich das weglassen kann, aber kürzer ist nicht unbedingt besser. Ich persönlich mag auch keine Programmiersprachen mit großer Fehlertoleranz wie z.B. PHP . Du darfst mir glauben, ich weiß, warum ich das so schreibe. Und falsch ist es ja nicht. In meinen Original-Dateien stehen sicherlich noch mehr Konstrukte, die Du so nicht schreiben würdest. Und für die 2 Dateien im ZIP habe ich das alles entfernt, da ich genau so eine Diskussion hier nicht wollte. Mir geht es hier einzig und allein um die Frage, warum meine Events ins Leere feuern. Ich programmiere jetzt (phasenweise) seit rund 30 Jahren. Ich weiß, warum ich manche Dinge so schreibe, wie ich sie schreibe. Für andere mögen andere Gründe schwerer wiegen und die dürfen das dann gerne anders machen. Alles klar?

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „claus“ ()

    Bzgl. Deines Codestils: Ist mir wurscht, da fühl ich mich nicht auf den Schlips getreten (u.a. weil ich eh keine trage ;) ) Es funktioniert ja, wie Du geschrieben hast. Sollte nur ne Anmerkung sein, weil wir hier schon viele erlebt haben, die es nicht besser wussten, weil sie sich irgendwo was zusammengeschustert haben und dann Zeug zusammenkommt, welches aufgrund fehlendem besseren Wissens immer weiterverwendet wird. Wenn Du es mit Absicht machst, ist da ja ok. Das mit Call ist ein Ratschlag von Mikrosaft selbst:

    MSDN schrieb:

    You typically use the Call keyword when the called expression doesn’t start with an identifier. Use of the Call keyword for other uses isn’t recommended.
    Aber was Du draus machst: Es ist Deine Coding-Guideline.

    Bzgl. Event feuert ins Leere: Na, wie geschrieben. Das feuernde Objekt ist nicht das, was Du erwartest. myContext_Menu ist im geposteten Code = Nothing, daher kommen von diesem Objekt auch keine Events bei den EventHandlern an, die mit Handles myContext_Menu.WasAuchImmer gekennzeichnet sind.
    Dass Du in obigen Post#7 Deinen alten, funktionierenden Code zeigst, der mit AddHandler arbeitet, bestätigt seine Funktionalität durch mein Experiment. Denn mehr habe ich da auch nicht gemacht. Letztenendes bestätigst Du, was ich (nach Dir) rausgefunden habe: Mit AddHandler klappt's. Warum es nicht mit der Festverdrahtung à la Handles myContext_Menu.WasAuchImmer klappt, weiß ich noch nicht. Von daher habe ich nichts Neues rausgefunden, was Du nicht schon wusstest. Leider.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    claus schrieb:

    "ContextMenu" und "ContextMenuStrip"
    ContextMenu ist eine ältere Klasse, sie wird im Designer nicht mehr angeboten.
    ContextMenuStrip bietet mehr Möglichkeiten, leg einfach mal eins im Designer an.
    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!

    RodFromGermany schrieb:

    "ContextMenu" und "ContextMenuStrip"
    ContextMenu ist eine ältere Klasse, sie wird im Designer nicht mehr angeboten.
    ContextMenuStrip bietet mehr Möglichkeiten, leg einfach mal eins im Designer an.



    Ach so. Jetzt verstehe ich meine Probleme damit besser.
    Ich arbeite nicht so viel mit dem Designer und habe mir die Möglichkeiten immer über den Objectmanager angesehen. Das ist zwar schwerer zu verstehen, aber umfassend, wenn auch oft erdrückend. Über den Daumen gepeilt bietet dort jedoch "ContextMenu" ein Mehr an Möglichkeiten. Und, was mir wichtig ist, hat alle Optionen, die ich brauche. Darum bin ich bei ihm geblieben. Außerdem stehe ich auf dem Standpunkt: Neuer ist nicht unbedingt besser. So habe ich mir VS2010 erst vor 2-3 Jahren gekauft, wo es schon neuerer Versionen gegeben hätte. Der Preis spielte dabei eine sekundäre Rolle. Wichtiger war mir, daß ich das meiste, was seither an Neuerungen gekommen ist, für meinen Bedarf nicht brauchen konnte.
    Aber dennoch: Danke für das Info, Rod. Deinen Rat habe ich in diesem Fall trotzdem befolgt und das Vorhandene Modul umgeschrieben. Das werde ich jetzt auch so lassen, auch wenn es mir bei meinem eigentlichen Problem nicht weiterhilft.

    Durch das Umschreiben ist mir etwas aufgefallen, was ich sehr merkwürdig finde:
    Wenn ich im CustomControl den Aufruf zum Event also Private Sub myContext_Menu_CM_Copy(sender As Object, e As System.EventArgs) Handles myContext_Menu.CM_Copy
    automatisch (und mit den von Dir angemahnten Argumenten) generieren lasse, wie es auch angeboten wird, dann steht es hinterher da, wie oben geschrieben. Wenn ich aber nun hergeh' und lösch in dieser Zeile die Argumente wieder raus, dann wird kein Fehler angemahnt, was sonst ja immer passiert, wenn im Event Argumente stehen. Seltsam.
    Und ich bin mir auch ziemlich sicher, nur noch ein Object Contextmenu zu haben, nachdem ich die angemahnte Instanzierung durchgeführt habe.

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

    claus schrieb:

    Seltsam
    Sieh Dir mal alle Vorkommnisse des Events und seiner Abonenten an.
    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!
    Das verstehe ich jetzt nicht ganz. Wie und wo muß ich da nachsehen?

    Das interessiert mich obwohl ich mein Problem scheinbar gerade gelöst habe. Ich habe mit der Test-Version, die im ZIP-File steht, weitergearbeitet. Deinen Tip bzgl ContextMenuStrip war dort eingebaut worden. Mir fehlt zwar im Moment noch die letzte Gewißheit, aber ich glaube, der Fehler lag bei "()", das sich irgendwie in die Zeilen zur Item-Instanzierung wie z.B.

    Public WithEvents MenuItem_Copy As New MenuItem()

    am Ende eingeschlichen hatten. Ich muß das zwar noch rückwärts prüfen, aber ich denk, das ist's, auch wenn's kaum zu glauben ist.

    Trotzdem muß ich sagen, daß der Hinweis von Dir und von VaporiZed mit der fehlenden Instanzierung, entscheidend war. Ich glaub, da hätte ich noch Jahre draufschauen können, und es dennoch nicht gesehen. Also besten Dank Euch beiden.

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

    claus schrieb:

    Wie und wo muß ich da nachsehen
    ContextMenü: Rechte Maustaste, alle Verweise anzeigen.
    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!