EventHandlers   zodpovězená otázka

VB.NET, WinForms, .NET

Dobrý den,

na formu jsou dynamicky vytvořené controls, které sdílí stejnou událost,

která je přiřazena každému z controls pomocí AddHandler, chci se zeptat

jakým způsobem lze zjistit přiřazené eventy příslušnému controlu nebo

kterým controls je přiřazena daná událost a tuto událost jim odebrat?

RemoveHandler je mi znám, spíš mě zajímá něco na způsob GetInvocationList

nebo EventHandlerList, něco se pokouším nastudovat, ale pokud by měl někdo

zkušenost a chuť poradit jakým směrem se vydat, tak předem moc díky.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Je možné udělat obě požadované věci, ale je to hodně ošklivého kódu používajícího reflexi. Nejsnazší řešení jsem našel zde http://stackoverflow.com/q/15630943/8090....

Nejlepší bude, když se při vytváření controlů bude evidovat, komu se jaká událost přiřadila, což bych udělal nějak takto.

Action<object, EventArgs> someEvent = (o, ea) => { MessageBox.Show(o.ToString()); };

button1.Click += new EventHandler(someEvent);
button2.Click += new EventHandler(someEvent);

var tuples = new List<Tuple<string, Action<object, EventArgs>>> { Tuple.Create("button1", someEvent), Tuple.Create("button2", someEvent) };
var lookup = tuples.ToLookup(t => t.Item1, t => t.Item2);

foreach (var control in lookup)
{
    MessageBox.Show(control.Key);
    foreach (var action in control)
       MessageBox.Show(action.ToString());
}

Nejdřív jsem vytvořil událost, kterou jsem pak přiřadil testovacím objektům. "tuples" je kolekce objektů (nechtěl jsem vytvářet speciální třídu, ale můžeš to udělat), kde je vždy v páru jméno controlu a akce mu přiřazená. Poté jsem vytvořil kolekci lookup, což je něco jako slovník (Dictionary), akorát tam můžeš mít k jednomu klíči více hodnot, což je to, co chceme. Lookup typ nemá konstruktor, vytváří se z jiné kolekce implementující rozhraní IEnumerable. Více o Lookup zde: http://msdn.microsoft.com/cs-cz/library/.... Nakonec pro demonstraci projíždím všechny hodnoty v lookup tabulce, vypíšu jméno controlu a po něm všechny události, které má přiřazené.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Díky za reakci, chápu záměr, ale v originálním znění nezrealizuji,

neboť mé (staré) VS2005 tvrdošíjně nezná Tuple, Lookup, bohužel.

Ve snaze vyhnut se té vlastní třídě s přiřazenými události jsem

našel jsem malou inspiraci u stejného zdroje :

http://stackoverflow.com/questions/42081...

Nejdříve se mi některé události nedařilo najít, nakonec se mi vrací

(systémem pokus omyl) vše, co bylo žádáno viz. link, ačkoli potřebuji

najít jen všechny události přidané pomocí AddHandler, mám všechny

controly a jejich události přidány dynamicky za běhu, ale je-li event

přidán v návrhu naleznu ho také.

Např. ve formuláři deklaruji :

Dim WithEvents pb as PictureBox

Událost Click přidám v návrhu:

Private Sub pb_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles hb.Click
'
End Sub

Událost Paint přidám pomocí AddHandler:

AddHandler pb.Paint, AddressOf pb_Paint

Událost Paint:

Private Sub pb_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
'
End Sub

Hledání událostí:

Public Sub GetListeners(ByVal myObject As Object)
        Dim targetType As Type = myObject.[GetType]()

        Do
            Dim myFieldList As System.Reflection.FieldInfo() = _
            targetType.GetFields(System.Reflection.BindingFlags.Static Or System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic)

            For Each myInfo As System.Reflection.FieldInfo In myFieldList

                Dim myDelegate As [Delegate] = TryCast(myInfo.GetValue(myObject), [Delegate])

                If myDelegate IsNot Nothing Then
                    For Each myItem As [Delegate] In myDelegate.GetInvocationList
                        If myDelegate.GetInvocationList(0).Method.DeclaringType.FullName = Me.GetType.ToString Then
                            System.Diagnostics.Debug.WriteLine(myDelegate.GetInvocationList(0).Method.Name & "-->" & myDelegate.GetInvocationList(0).Method.DeclaringType.FullName)
                        End If
                    Next


                End If

                Try
                    Dim eventList As System.ComponentModel.EventHandlerList = DirectCast(myObject.GetType().GetProperty("Events", _
                  (System.Reflection.BindingFlags.FlattenHierarchy Or (System.Reflection.BindingFlags.NonPublic Or _
                    System.Reflection.BindingFlags.Instance))).GetValue(myObject, Nothing), System.ComponentModel.EventHandlerList)

                    myDelegate = eventList(myInfo.GetValue(myObject))
                Catch ex As Exception

                End Try

                If myDelegate IsNot Nothing Then
                    For Each myItem As [Delegate] In myDelegate.GetInvocationList
                        If myDelegate.GetInvocationList(0).Method.DeclaringType.FullName = Me.GetType.ToString Then

                            System.Diagnostics.Debug.WriteLine(myDelegate.GetInvocationList(0).Method.Name & "-->" & myDelegate.GetInvocationList(0).Method.DeclaringType.FullName)
                        End If
                    Next
                End If
            Next
            targetType = targetType.BaseType
        Loop While targetType IsNot Nothing

    End Sub

Případně jak rozlišit, je-li událost z návrhu nebo pomocí AddHandler?

V mém případě jsou všechny controly a jejich události přidány za běhu

pomocí AddHandler (tak mě to tolik netrápí) avšak před jejich odebráním

se chystám tyto nalezené události controlům odebrat a nevím jak dál.

Co použít pro odstranění nalezeného handleru?

Jak vytvořit event a delegáta z jejich názvu pro RemoveHandler?

[Delegate].Remove? GetRemoveMethod? EventInfo.RemoveEventHandler?

Díky za případnou další pomoc.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Čistě ze zvědavosti, k čemu bude sloužit funkčnost, která zjišťuje, zda jsou k události napojené nějaké obslužné metody?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Před zavřením formuláře odstranuji vytvořené prvky, např. mažu-li TabPages z TabControl

a mám-li událost SelectedTabChange, tak se po odstranění všech tabs tato událost vyvolá

s tím, že nemá žádný objekt SelectedTab, proto se snažím nejprve událost odebrat a pak

teprve vymazat jednotlivé tabs a jejich prvky... Nic jiného mě nenapadlo.

Tolik pro zvědavost, ale možná se mi povedlo najít, jak se s tím poprat...

pb je PictureBox, co mu chci odebrat událost Paint, něco na tento způsob:

Dim ed As System.ComponentModel.EventDescriptor = System.ComponentModel.TypeDescriptor.GetEvents(pb).Find("Paint", True)
Dim ty As Type = pb.GetType.GetEvent("Paint").EventHandlerType
Dim deleg As [Delegate] = [Delegate].CreateDelegate(ty, Me, "pb_Paint")
ed.RemoveEventHandler(pb, deleg)

Pokud máte jiný tip, jak se tomu všemu vyhnout, budu rád.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

A jaký smysl má explicitně odstraňovat ovládací prvky z formuláře před jeho zavřením, když po jeho zavření budou odstraněny automaticky? To je naprosto zbytečná a smysl postrádající práce.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To by bylo fajn, kdyby se vše odstranilo automaticky, ale skončí mi to v té události

a neukončí se aplikace, zůstane mi to celé viset, dokud nedám stop...

Proč se to nestane? :)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jaké události jakého ovládacího prvku? A co je v kódu obslužné metody? Vložte kód a bude jasno.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Omluva, pokračování zítra. Musím se vzdálit do deště...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jaké události jakého ovládacího prvku? A co je v kódu obslužné metody? Vložte kód a bude jasno.

Je to událost TabControlu, nic zvláštního se tam neděje.

Myslím, že buď vadí, že už je odstraněn toolstrip nebo už nemá žádné tabs,

tudíž ani selectedtab. Jinak to pouze pozmění vlastnosti pár tlačítek.

Jistě mohu testovat je-li ts nebo selectedtab nothing, ale proč události úplně

neodebrat před odstaněním controls, nevím, zda-li něco podobného nebude jinde.

Private Sub tc_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim ts As ToolStrip = Me.Controls("ts1")
        Select Case tc.SelectedTab.Name
            Case "1"
                ts.Items("tsb1").Visible = True
                ts.Items("tsb2").Visible = True
                ts.Items("tsbFilter").Visible = False
                ts.Items("tsbRefresh").Visible = True
            Case "2"
                ts.Items("tsb1").Visible = False
                ts.Items("tsb2").Visible = dgmv.Visible
                ts.Items("tsbFilter").Visible = False
                ts.Items("tsbRefresh").Visible = True
            Case "3"
                ts.Items("tsb1").Visible = False
                ts.Items("tsb2").Visible = False
                ts.Items("tsbFilter").Visible = True
                ts.Items("tsbRefresh").Visible = True

            Case "4"
                ts.Items("tsb1").Visible = False
                ts.Items("tsb2").Visible = False
                ts.Items("tsbFilter").Visible = True
                ts.Items("tsbRefresh").Visible = True
        End Select
    End Sub
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud tomu dobře rozumím, tak:

Hlavní problém je, že se snažíte vyvádět různé hovadiny těsně před zavřením formuláře.

Proč? Zavření formuláře okamžitě schová celé okno i s jeho obsahem uživateli a je tedy zcela zbytečné cokoliv schovávat, zakazovat, odstraňovat, atd. Odstranění ovládacích prvků z paměti a podobnou špinavou práci udělá Garbage Collector, nejsme v C++.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ano, problém jste vystihnul naprosto přesně ;), otázkou je, kde je správné místo

např. pro ukončení connection k db, filesystemwatcheru apod.

Nějaké akce je snad dobré před ukončením udělat?

V návrhu formuláře nemám žádné prvky, vše vytvářím za běhu, možná je to tím, že mi tam chybí něco, co se provádí na controlech při zavření, nejhorší je, že někdy se vše ukončí bez problémů a někdy ne, snažím se najít příčinu vypisováním do debug, co proběhlo a co ne.

Potíž je vyvolat zrovna ten stejný případ.:)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Chápu, že něco je nutné vytvářet za běhu, ale většinu prvků snad půjde vytvořit při návrhu aplikace ne? Občas je lepší, než to generovat až podle volby uživatele, dát to tam předem a pak podle volby nastavovat viditelnost prvků.

Správný čas k ukončení připojení do databáze je, když se dokončí operace, které mají spolu logickou souvislost. Jestli používáte SQL databázi, tak většina dnešních už má connection pool, takže zahození připojení a otevření nového nestojí zase tak moc času a paměti, protože v poolu jsou připojení předvytvořená. Tím spíš, pokud nad jednou databází pracuje více uživatelů, neměl by jste čas připojení natahovat déle, než je nutné.

Proč dělat věci složitější, než by mohly být?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Obvykle není nutné dělat při uzavírání formuláře vůbec nic. Prostředky jako spojení do databáze, otevřené soubory a jiné unmanaged věci se používají pouze v momentě, kdy jsou skutečně potřeba a zůstávají smrdět jen po nezbytně nutnou dobu, čehož se docílí pomocí Using...End Using.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback