Podpinanie eventów z innych komponentów - Delphi a C#

0

Hej,

Zdaje sobie sprawę, że ten post powinien się znaleźć tylko na forum C#, ale programiści stricte C# - bez obrazy, ale jacyś niemrawi są. Potrzebuję więc pomocy programisty Delphi/C# [napisałem to, a pierwszy post i tak dostałem z forum c#]

Mam takie pytanie. Potrzebuję utworzyć na kontrolce nowy komponent i podpiąć pod jego obsługę zdarzenia Click metodę obsługującą zdarzenie Click w innym komponencie.

W Delphi dało się to zrobić, zdaje się tak jakoś:

procedure (...).GetEvents( ... )
begin
  (...)
  vNewControl.OnClick := iOldControl.OnClik;
end;

Dodajmy, że potrzebuję to wszystko zrobić w klasie statycznej, w której w jednej z metod za parametr podaje kontrolkę.
Czy w C# jest coś takiego w ogóle możliwe? Bo tracę wiarę w ten język :P

Właśnie dostałem mail, z którego dowiedziałem się, że to co zdaje się być wielkie w języku C# jest równocześnie jego ogromnym OGRANICZENIEM:

You can't assign the event of another control in C# like you can in
Delphi. This has probably to do with the fact that in .NET you can
have multiple listners.

No cóż. Teraz trzeba pisać do twórcy C#, żeby przywrócił to co działało dobrze w Delphi :P

0

Moj pomysl to skopiowanie delegatow, aczkolwiek bazuje sie na zalozeniu, ze nazwa eventu sie nie zmieni

Kladziemy na forme button1 i button2 do 1 dodajemy cos na click np MessageBox

       Type t = button1.GetType();
            Delegate[] de = null;
            do
            {
             FieldInfo[] fields = t.GetFields( BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
             foreach (FieldInfo field in fields)
             {
                 if (field.Name == "EventClick")
                    {

//pobieramy zdarzenia
                        EventHandlerList eventHandlers = ((EventHandlerList)(button1.GetType().GetProperty("Events", 
                            (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic | 
                            BindingFlags.Instance))).GetValue(button1, null)));
                        Delegate d = eventHandlers[field.GetValue(button1)];
                        if ((!(d == null)))
                        {
                           de = d.GetInvocationList();
                        }
                    }
                }
                t = t.BaseType;
            } while (t != null);

//kopiuj delegaty do button2
            if (de != null)
            {
                for (int i = 0; i < de.Length; i++)
                {
                    button2.Click += (EventHandler)de[i];
                }
            }

zadzialalo, ale moze jest cos prostszego (to mozna zapakowac w funkcje i nazwac copyEvents ...)

0

Użytkownik wasiu na forum C# napisał:

Napisz wlasny handler zdarzenia tego komponentu, który będzie wywoływał akcje Click w drugim komponencie i po problemie :)

To zdaje się być prostsze. Wystarczy tylko przechowywać gdzieś wskaźnik do starego przycisku. Tylko jak u licha wywołać akcję Click na drugim komponencie w C#?

0

Pytanie co da wywolanie samego zdarzenia click w drugim komponencie (np PerformClick czy cos w ten desen) w jaki sposob ma sie to odbywac dynamicznie ? I tak trzeba jakos powiedziec, ze ma byc to wlasnie ten komponent. Moze np tak, ze tworzymy sobie wlasny handler i w nim umieszczamy procedure wraz ze zdarzeniem buton_z_ktorego_kopiujemy.PerformClick.

Mozna tez utworzyc swoja liste zdarzen na bazie EventHandlerList i nimi manipulowac (to chyba najladniejsze rozwiazanie - bez stosowania reflector'a). Oczywiscie informacja co gdzie jest zapisywana jest przez ciebie a zatem nie jest to tak proste jak skopiowanie w delphi. Albo tez na liscie List<EventHandler> (podobne rozwiazanie).

Rozwiazanie podane przez wasiu ma ten minus (ale nie wiem czy Ci to potrzebne - kopiowanie w delphi to ma), ze gdy jest to nieznany komponent OLD to kopiujemy to co ma w sobie, natomiast piszac wlasny handler zawsze kopiujemy to co jest w nim (zamotalem ...) reasumujac:

i tak gdzies masz (procedura juz gdzies jest, nie jest ona tworzona dynamicznie, BTW a tak tez sie da - emit).

iOldControl.Clik += new EventHandler(button_click_event);

wiec rownie dobrze mozesz dodawac ta obsluge tak

vNewControl.Clik += new EventHandler(button_click_event);

Podana przeze mnie metoda ma ta zalete, ze nie musimy znac nazwy procedury ale chyba tobie to nie potrzebne.

PS. i jeszcze jedno, wyglada na to ze mozna pominac petle zakladajac, ze znamy klase ( wypada wtedy t = t.BaseType;) w ktorej sa nasze eventy typu click (chyba COntrol i wtedy typeof). Podane rozwiazanie jest po prostu bardziej ogolne i szuka po wszystkich klasach w dol.

PS2. Najprosciej
button2.Click += delegate(object _sender, EventArgs _e) { button1.PerformClick(); };

0

Ale kopiowanie delegatów wydaje się mi świetnym pomysłem - sprawdzę tylko czy u mnie zadziała:)

:-[
Zrobiłem z tego klasę, ale nie chce mi kopiować delegatów.

Robię to w CompactFrameworku, może to jest tego wina. On jest dosyć obcięty. Usunąłem nawet warunek

if (field.Name == "EventClick")

by mu było wszystko jedno... Ale CP ma mnie gdzieś....

    static class DelegatsCopier
    {
        static public void CopyDelegates(Control iSourceControl, Control iDestinationControl)
        {
            Type t = iSourceControl.GetType();
            Delegate[] de = null;
            do
            {
                FieldInfo[] fields = t.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
                foreach (FieldInfo field in fields)
                {
                    //if (field.Name == "EventClick")
                    {

                        //pobieramy zdarzenia
                        EventHandlerList eventHandlers = ((EventHandlerList)(iSourceControl.GetType().GetProperty("Events",
                            (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic |
                            BindingFlags.Instance))).GetValue(iSourceControl, null)));
                        Delegate d = eventHandlers[field.GetValue(iSourceControl)];
                        if ((!(d == null)))
                        {
                            de = d.GetInvocationList();
                        }
                    }
                }
                t = t.BaseType;
            } while (t != null);

            //kopiuj delegaty
            if (de != null)
            {
                for (int i = 0; i < de.Length; i++)
                {
                    iDestinationControl.Click += (EventHandler)de[i];
                }
            }
        }
    }
0

i tak gdzies masz (procedura juz gdzies jest, nie jest ona tworzona dynamicznie, BTW a tak tez sie da - emit).

iOldControl.Clik += new EventHandler(button_click_event);

wiec rownie dobrze mozesz dodawac ta obsluge tak

vNewControl.Clik += new EventHandler(button_click_event);

No właśnie cała zabawa polega na tym, że nie znam nazwy button_click_event i nie mam do niej dostepu z poziomu klasy, w której potrzebuję zrobic przepisanie eventu

//A co do:
vNewControl.Click += delegate(object asender, EventArgs ae) { iControl.PerformClick(); };

// to chyba TAK powinno raczej być:
vNewControl.Click += delegate(object asender, EventArgs ae) { (iControl as Button).PerformClick(); };

Poza tym i tak dupa zbita, bo dla Compact Frameworka:
'System.Windows.Forms.Button' does not contain a definition for 'PerformClick'
Dziękujemy Microsoftowi, że dbając o naszą pamięć Flash w urządzeniach z WindowsCE obciął najważniejsze rzeczy.

:-[ :-[ :-[ [glowa] [glowa] [glowa]
Najbardziej mi zależy na kopiowaniu tych delegatów. Teraz wyszły takie rzeczy z tym, że idzie się zabić. W normalnym Frameworku to działa i byłbym Ci wdzięczny do końca życia za tą cenną radę. Bo wystarczy zawołać procedurę podmiany event-ów i wszystko hula radośnie.

DelegatsCopier.CopyDelegates(iControl, vNewControl);

W Compact Frameworku dla Windows CE uparcie nie chce mi skopiować tych delegatów, pomimo że używam tej samej klasy dla jednej platformy .NET i drugiej.
Jestem załamany ;-( ;-( ;-( [glowa] [glowa] [glowa]

1 użytkowników online, w tym zalogowanych: 0, gości: 1