0

I want to bubble up a message through classes. I used events and did this:

public class TopLevel{
    public event EventHandler<string> Message;
    public MiddleLevel mid;

    public TopLevel()
    {
        mid.Message += (s, e) => { Message(s, e) };
    }
} 

public class MiddleLevel{
    public event EventHandler<string> Message;
    public BottomLevel bottom;

    public MiddleLevel()
    {
        bottom.Message += (s, e) => { Message(s, e) };
    }
}

public class BootomLevel{
    public event EventHandler<string> Message;

    public void DoSomething()
    {
        Message?.Invoke(this, "I did it.");
    }
}

public class Handler{
    public void HandleEvent(TopLevel top)
    {
        top.Message += PrintMessage;
    }

    public void PrintMessage(object sender, string message)
    {
        Console.WrteLine(message);
    }
}

Also tried changing constructor to lambda expressions like this:

public class TopLevel{
    public event EventHandler<string> Message;
    public MiddleLevel mid;

    public TopLevel()
    {
        mid.Message += (s, e) => { Message?.Invoke(s, e); };
    }
} 

public class MiddleLevel{
    public event EventHandler<string> Message;
    public BottomLevel bottom;

    public MiddleLevel()
    {
        bottom.Message += (s, e) => { Message?.Invoke(s, e); };
    }
}

public class BootomLevel{
    public event EventHandler<string> Message;

    public void DoSomething()
    {
        Message?.Invoke(this, "I did it.");
    }
}

public class Handler{
    public void HandleEvent(TopLevel top)
    {
        top.Message += PrintMessage;
    }

    public void PrintMessage(object sender, string message)
    {
        Console.WrteLine(message);
    }
}

Codes above doesn't print any message. Even if I handle event in MiddleLevel class, I still get no message. I assume it is because message call is made in constructor (Even though linq quarries update themselves)? If I handle event in Handle class straight from BottomLevel class, it obviously - works. But I need to bubble the message up, I can't think of any other way to to this, because of how classes are constructed. Is it even possible to do what I have in mind with a standard Eventhandler class? If so than how? Should I just make an event class myself as in one of the sites i refered?

I refered to these sites:

What is the preferred way to bubble events?

https://www.carlosble.com/2016/04/event-bubbling-in-c/

AtticFizz
  • 51
  • 7

1 Answers1

0

Updated answer:

If you want 'Handler' to be triggered you will have to make sure that 'BottomLevel' falls within the hierarchy of the 'TopLevel' class being passed to the handler, this can be done via dependency injection (DI).

If 'BottomLevel' instantiates it's own classes (no DI) then it will not know about 'Handler', so handler will never be triggered.

If you comment out the DI setup and un-comment the 'BottomLevel' instantiation you can see the different behaviors.

   class Program
    {
        static void Main(string[] args)
        {
            //setup the classes (dependency injection)
            TopLevel topLevel = new TopLevel();
            MiddleLevel middleLevel = new MiddleLevel(topLevel);
            BottomLevel bottomLevel = new BottomLevel(middleLevel);

            //set up the handler
            Handler h = new Handler(topLevel);
            
            //using this will not link to 'Handler' as there is no relation between this bottom and top
            //BottomLevel bottomLevel = new BottomLevel(); 

            //trigger the bottom class
            bottomLevel.TriggerBottom();
            //or
            bottomLevel.DoSomething(null, "call from main");

            Console.ReadLine();
        }
    }

    public class Handler
    {
        TopLevel _topLevel;
        public Handler(TopLevel topLevel)
        {
            if (topLevel != null)
                _topLevel = topLevel;

            _topLevel.Message += _topLevel_Message;
        }

        private void _topLevel_Message(object sender, string e)
        {
            Console.WriteLine($"handler triggered : {e}");
        }
    }

    public class TopLevel
    {
        public event EventHandler<string> Message;

        public TopLevel()
        { }

        public void TriggerTop()
        {
            Message?.Invoke(this, "origin top");
        }

        public void DoSomething(object sender, string e)
        {
            Console.WriteLine($"Do something at top : {e}");
            Message?.Invoke(this, e);
        }
    }

    public class MiddleLevel
    {
        TopLevel _TopLevel;
        public event EventHandler<string> Message;

        public MiddleLevel(TopLevel topLevel) : this()
        {
            _TopLevel = topLevel;
        }
        public MiddleLevel()
        {
            if (_TopLevel == null)
                _TopLevel = new TopLevel();
            //subscribe this message to bottom message event method
            Message += (s, e) => { _TopLevel.DoSomething(s, e); };
        }

        public void TriggerMiddle()
        {
            Message?.Invoke(this, "origin middle");
        }

        public void DoSomething(object sender, string e)
        {
            Console.WriteLine($"do something in middle : {e}");

            //invoke the event(s)        
            Message?.Invoke(sender, e);
        }
    }

    public class BottomLevel
    {
        MiddleLevel _MidLevel;
        public event EventHandler<string> Message;

        public BottomLevel(MiddleLevel midLevel) : this()
        {
            _MidLevel = midLevel;
        }

        public BottomLevel()
        {
            if (_MidLevel == null)
                _MidLevel = new MiddleLevel();

            ////here you assign it
            Message += (s, e) => { _MidLevel.DoSomething(s, e); };

        }

        public void TriggerBottom()
        {
            DoSomething(this, "origin bottom");
        }

        public void DoSomething(object sender, string e)
        {
            Console.WriteLine($"do something at bottom : {e}");
            Message?.Invoke(sender, e);
        }
    }
CobyC
  • 2,058
  • 1
  • 18
  • 24
  • Hmm. It works, but not in the way I need :D My message is sent from BottomLevel or MiddleLevel or TopLevel class, and the final receiver must be Handler, because of message printing order. (right now it fires immediately and I have no control over it) – AtticFizz Jun 29 '20 at 16:49
  • What is the scenario you are using it in? if you want handler to be triggered all other classes will have to be dependent on each other, or static, or handler needs to subscribe to them? – CobyC Jun 29 '20 at 17:40
  • I am trying to make something like a pokemon game. It isn't going to be a legit game, I am only doing this purely for learning purposes. And the class structure I used is same as shown here Player has Party, Party has Pokemon. In original game you get a message when pokemon levels up. this is what I try to simulate. So message comes from pokemon and then is handled through player class object. As I mentioned in my question maybe events isn't the right approach? – AtticFizz Jun 29 '20 at 18:03