locked
How to write generic events and delegates. Help needed! RRS feed

  • Question

  • Hi!

    I have this abstract class Component, and a class Button and a class Panel that inherits from the class Component.

     

    Now in the Component class I would like to create generic events and delegates so I tried this:

    public delegate void MouseEnterEvent<T>(T sender);
    


    which is ok, so now i can use this delegate for both my Button and Panel class but now i need the event and I type this in my Component class:

    public abstract event MouseEnterEvent<T> OnMouseEnter;
    

    but this generates the following error:

    "The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?")

     

    So what am I missing, and how do I create a generic event for my generic delegate?

    Wednesday, February 1, 2012 10:10 AM

Answers

  • Here I could not see the usability of generic.
    OK, let's take the first event MouseEnterEvent in your list for example, here I roughly implemented it, and declared a Button class which inherits the Component class. You can see that in the Button class the OnMouseEnter method in the base class is overrided to add some additional behaviors:
        class Program
        {
            static void Main(string[] args)
            {
                Button btn = new Button();
                btn.MouseEnterEvent += new EventHandler<MouseEventArgs>(btn_MouseEnterEvent);
                Console.WriteLine("Waiting for the mouse pointer to enter...");
                Thread.Sleep(3000);
                btn.SimulateMouseEnter();
            }
    
            static void btn_MouseEnterEvent(object sender, MouseEventArgs e)
            {
                Console.WriteLine("Mouse location when entered this control: ({0},{1}).", e.X, e.Y);
            }
        }
    
        public class MouseEventArgs : EventArgs
        {
            public readonly int X;
            public readonly int Y;
    
            public MouseEventArgs(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }
        }
    
        public abstract class Component
        {
            public event EventHandler<MouseEventArgs> MouseEnterEvent;
    
            protected virtual void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Default behavior performed.");
                var handler = MouseEnterEvent;
                if (handler != null) handler(this, e);
            }
    
            //Simulating that the mouse pointer enters this control.
            public void SimulateMouseEnter()
            {
                Random random = new Random();
                OnMouseEnter(new MouseEventArgs(random.Next(0, 500), random.Next(0, 500)));
            }
        }
    
        public class Button : Component
        {
            protected override void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Added behavior performed.");
                base.OnMouseEnter(e);
            }
        }
    

    In this sample, the mouse enter behavior is simulated, in a more pratical situation, we may need a polling on the mouse position.

    Have a nice day,
    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us

    • Edited by Leo Liu - MSFT Thursday, February 2, 2012 10:38 AM
    • Marked as answer by Tobias_K Thursday, February 2, 2012 10:43 AM
    • Unmarked as answer by Tobias_K Thursday, February 2, 2012 11:58 AM
    • Marked as answer by Leo Liu - MSFT Wednesday, February 8, 2012 3:07 AM
    Thursday, February 2, 2012 10:35 AM
  • Ok, but what if i would like to use MouseEnterEvent and return the Button in the eventargs and if i create a new class Panel : Component and then i want to return the Panel ?


    Of course we can involve this in the definition of the inherited EventArgs class, please check out the following intact sample:
        class Program
        {
            static void Main(string[] args)
            {
                Button btn = new Button();
                btn.Name = "Button1";
                btn.MouseEnterEvent += new EventHandler<MouseEventArgs>(btn_MouseEnterEvent);
                Console.WriteLine("Waiting for the mouse pointer to enter...");
                Thread.Sleep(3000);
                btn.SimulateMouseEnter();
            }
    
            static void btn_MouseEnterEvent(object sender, MouseEventArgs e)
            {
                Button currentControl = (Button)e.Sender;
                Console.WriteLine("Current control: {0}", currentControl.Name);
                Console.WriteLine("Mouse location when entered this control: ({0},{1}).", e.X, e.Y);
            }
        }
    
        public class MouseEventArgs : EventArgs
        {
            public readonly int X;
            public readonly int Y;
            public readonly Component Sender;
    
            public MouseEventArgs(int x, int y, Component sender)
            {
                this.X = x;
                this.Y = y;
                this.Sender = sender;
            }
        }
    
        public abstract class Component
        {
            public event EventHandler<MouseEventArgs> MouseEnterEvent;
            public string Name;
    
            protected virtual void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Default behavior performed.");
                var handler = MouseEnterEvent;
                if (handler != null) handler(this, e);
            }
    
            //Simulating that the mouse pointer enters this control.
            public void SimulateMouseEnter()
            {
                Random random = new Random();
                OnMouseEnter(new MouseEventArgs(random.Next(0, 500), random.Next(0, 500), this));
            }
        }
    
        public class Button : Component
        {
            protected override void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Added behavior performed.");
                base.OnMouseEnter(e);
            }
        }
    


    But actually we don't need to perform like this, as the "object sender" parameter has supplied this feature.
    Please use the original code I supplied and just make a modification as below to test:
            static void btn_MouseEnterEvent(object sender, MouseEventArgs e)
            {
                Button currentControl = (Button)sender;
                Console.WriteLine("Current control: {0}", currentControl.Name);
                Console.WriteLine("Mouse location when entered this control: ({0},{1}).", e.X, e.Y);
            }
    


    Note generic is still not needed here. One point I want to add is that if you get familiar with the Event Implementation Mechanism of WinForm or WPF, this kind of problem will not be that difficult.

    Have a nice day,
    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us
    • Marked as answer by Leo Liu - MSFT Wednesday, February 8, 2012 3:07 AM
    Friday, February 3, 2012 3:36 AM

All replies

  • The class in which we declare the event should be a template or generic class

    public

    delegate void MouseEnterEvent<T>(T sender);

    class Program { static void Main(string[] args) { } }

    class TestClass<T>  public event MouseEnterEvent<T> OnMouseEnter; }

     

    Thanks


    Wednesday, February 1, 2012 10:36 AM
  • Could you please clearify which of these classes shoudl be a template or generic:

     

    public abstract class Component

    {

    ...

     public delegate void MouseEnterEvent<T>(T sender);
     public delegate void MouseLeaveEvent<T>(T sender);
     public delegate void MouseClickEvent<T>(T sender);
     public delegate void MousePressedEvent<T>(T sender);
     public delegate void MouseReleasedEvent<T>(T sender);
     public delegate void MouseDraggingEvent<T>(T sender);

    ..

    }

     

    public class Button : Component

    {...}

     

    public class Panel : Component

    {...}

    Wednesday, February 1, 2012 11:01 AM
  • Where you are going to place your abstract event that class should be generic. I think you are going to place it in Component class so that you can override it in button and Panel. Also you can move the delegate declarations outside component class i mean to the same namespace.

    Thanks

    Wednesday, February 1, 2012 11:06 AM
  • Hmmm ok this was hard for me to get, if i put

     

    public abstract class Component<T>

    {

    }

     

    How do i define a list of these elements.....

    List<Component> aList = new List<Component>(); *Does not work
    List<Component<T>> aList = new List<Component<T>>(); *Does not work

    .....

    Wednesday, February 1, 2012 11:13 AM
  • So what am I missing, and how do I create a generic event for my generic delegate?

    You cannot define a generic event. You can define a non-generic event in a generic class:
    class NotGenericClass
    {
            // The sender's type must be a concrete type
            public event MouseEnterEvent<NotGenericClass> OnMouseEnter;
    }
    
    class GenericClass<T>
    {
            // The sender's type can be a generic type parameter defined on the class
            public event MouseEnterEvent<T> OnMouseEnter;
    }
    
    

    Wednesday, February 1, 2012 1:55 PM
  • abstract class Pr<T>
        {
            public abstract event MouseEnterEvent<T> OnMouseEnter;

            void Test()
            {
                List<Pr<T>> aList = new List<Pr<T>>();
            }
     } 


    Wednesday, February 1, 2012 2:15 PM
  • Hmmm ok this was hard for me to get, if i put

     

    public abstract class Component<T>

    {

    }

     

    How do i define a list of these elements.....

    List<Component> aList = new List<Component>(); *Does not work
    List<Component<T>> aList = new List<Component<T>>(); *Does not work

    .....

    That is because a literal type like "T" should be available in the context.
    You can check out the two working situations in the following code:
        public abstract class Component<T>
        {
    
        }
    
        // Situation 1: Generic Class.
        class GenericClass<T>
        {
            public GenericClass()
            {
                List<Component<T>> aList = new List<Component<T>>();
            }
    	}
    
        class NormalClass
        {
            // Situation 2: Generic Method.
            public void GenericMethod<T>()
            {
                List<Component<T>> aList = new List<Component<T>>();
            }
        }
    

    Have a nice day,
    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us
    Thursday, February 2, 2012 7:48 AM
  • So, maybe I should explain what i want to do and maybe this is not the way...

     

    I have this public abstract class Component which i would like to use a base class for components.

     

    Then I have my first real component, public class Button.....

    Right now I have

    public class Button : Component

    and what i want is that the base class Component should register any events and i want the Button class to be forced to handle any event raised from the base class Component, but i would like not to write events foreach class that inherits from my abstract Component class.

    Thursday, February 2, 2012 8:08 AM
  • Here I could not see the usability of generic.
    OK, let's take the first event MouseEnterEvent in your list for example, here I roughly implemented it, and declared a Button class which inherits the Component class. You can see that in the Button class the OnMouseEnter method in the base class is overrided to add some additional behaviors:
        class Program
        {
            static void Main(string[] args)
            {
                Button btn = new Button();
                btn.MouseEnterEvent += new EventHandler<MouseEventArgs>(btn_MouseEnterEvent);
                Console.WriteLine("Waiting for the mouse pointer to enter...");
                Thread.Sleep(3000);
                btn.SimulateMouseEnter();
            }
    
            static void btn_MouseEnterEvent(object sender, MouseEventArgs e)
            {
                Console.WriteLine("Mouse location when entered this control: ({0},{1}).", e.X, e.Y);
            }
        }
    
        public class MouseEventArgs : EventArgs
        {
            public readonly int X;
            public readonly int Y;
    
            public MouseEventArgs(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }
        }
    
        public abstract class Component
        {
            public event EventHandler<MouseEventArgs> MouseEnterEvent;
    
            protected virtual void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Default behavior performed.");
                var handler = MouseEnterEvent;
                if (handler != null) handler(this, e);
            }
    
            //Simulating that the mouse pointer enters this control.
            public void SimulateMouseEnter()
            {
                Random random = new Random();
                OnMouseEnter(new MouseEventArgs(random.Next(0, 500), random.Next(0, 500)));
            }
        }
    
        public class Button : Component
        {
            protected override void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Added behavior performed.");
                base.OnMouseEnter(e);
            }
        }
    

    In this sample, the mouse enter behavior is simulated, in a more pratical situation, we may need a polling on the mouse position.

    Have a nice day,
    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us

    • Edited by Leo Liu - MSFT Thursday, February 2, 2012 10:38 AM
    • Marked as answer by Tobias_K Thursday, February 2, 2012 10:43 AM
    • Unmarked as answer by Tobias_K Thursday, February 2, 2012 11:58 AM
    • Marked as answer by Leo Liu - MSFT Wednesday, February 8, 2012 3:07 AM
    Thursday, February 2, 2012 10:35 AM
  • Ok, but what if i would like to use MouseEnterEvent and return the Button in the eventargs and if i create a new class Panel : Component and then i want to return the Panel ?

     

     

    maybe this is not possible, but I want to create some global or generic events in my COmponent class and when a button is entered by the mouse the event will trigger and return the button, and so for the PAnel as well.

     

    I was hope not to have to write event handler for all my classes that inherits from the component class, so i thought it was possible to make some kind of "template" or "generic" event handling in the component class .....

    • Edited by Tobias_K Thursday, February 2, 2012 11:33 AM added text
    Thursday, February 2, 2012 11:10 AM
  • I was hope not to have to write event handler for all my classes that inherits from the component class

    If you don't want to implement it in the derived classes, you should not mark it as abstract in the base class! You can do something like this:

    abstract class Component<T> where T : Component<T>
    {
            public event MouseEnterEvent<T> MouseEnter;
    }
    
    class Button : Component<Button>
    {
            ...
    }
    
    Thursday, February 2, 2012 1:06 PM
  • check this nice tutorial

    http://sharpertutorials.com/delegates-and-events/

    http://www.java-forums.org/forum.php
    Thursday, February 2, 2012 2:44 PM
  • Ok, but what if i would like to use MouseEnterEvent and return the Button in the eventargs and if i create a new class Panel : Component and then i want to return the Panel ?


    Of course we can involve this in the definition of the inherited EventArgs class, please check out the following intact sample:
        class Program
        {
            static void Main(string[] args)
            {
                Button btn = new Button();
                btn.Name = "Button1";
                btn.MouseEnterEvent += new EventHandler<MouseEventArgs>(btn_MouseEnterEvent);
                Console.WriteLine("Waiting for the mouse pointer to enter...");
                Thread.Sleep(3000);
                btn.SimulateMouseEnter();
            }
    
            static void btn_MouseEnterEvent(object sender, MouseEventArgs e)
            {
                Button currentControl = (Button)e.Sender;
                Console.WriteLine("Current control: {0}", currentControl.Name);
                Console.WriteLine("Mouse location when entered this control: ({0},{1}).", e.X, e.Y);
            }
        }
    
        public class MouseEventArgs : EventArgs
        {
            public readonly int X;
            public readonly int Y;
            public readonly Component Sender;
    
            public MouseEventArgs(int x, int y, Component sender)
            {
                this.X = x;
                this.Y = y;
                this.Sender = sender;
            }
        }
    
        public abstract class Component
        {
            public event EventHandler<MouseEventArgs> MouseEnterEvent;
            public string Name;
    
            protected virtual void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Default behavior performed.");
                var handler = MouseEnterEvent;
                if (handler != null) handler(this, e);
            }
    
            //Simulating that the mouse pointer enters this control.
            public void SimulateMouseEnter()
            {
                Random random = new Random();
                OnMouseEnter(new MouseEventArgs(random.Next(0, 500), random.Next(0, 500), this));
            }
        }
    
        public class Button : Component
        {
            protected override void OnMouseEnter(MouseEventArgs e)
            {
                Console.WriteLine("Added behavior performed.");
                base.OnMouseEnter(e);
            }
        }
    


    But actually we don't need to perform like this, as the "object sender" parameter has supplied this feature.
    Please use the original code I supplied and just make a modification as below to test:
            static void btn_MouseEnterEvent(object sender, MouseEventArgs e)
            {
                Button currentControl = (Button)sender;
                Console.WriteLine("Current control: {0}", currentControl.Name);
                Console.WriteLine("Mouse location when entered this control: ({0},{1}).", e.X, e.Y);
            }
    


    Note generic is still not needed here. One point I want to add is that if you get familiar with the Event Implementation Mechanism of WinForm or WPF, this kind of problem will not be that difficult.

    Have a nice day,
    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us
    • Marked as answer by Leo Liu - MSFT Wednesday, February 8, 2012 3:07 AM
    Friday, February 3, 2012 3:36 AM
  • Hi Tobias,

    How is your requirement going now with our friends' suggestions?
    We are looking forward to hearing from you.

    Have a nice day,

    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us
    Monday, February 6, 2012 4:05 AM