Link to home
Start Free TrialLog in
Avatar of i-Thomas
i-Thomas

asked on

object sender: how to get the id / name / index of a user control in array list?

Hi Experts!

I have defined ArrayList in my main Win app that contains several myUserControls:

public ArrayList myUserControls = new ArrayList();




Further I have defined an event in myUserControl:

public event EventHandler ClosePanel;

:
:

private void uxClosePanelBtn_Click(object sender, System.EventArgs e)
{
       this.ClosePanel(this, new System.EventArgs());
}



When I drag myUserControl to my MainForm, I can see the event and access it.

In the app I will not have any static myUserControl. I will add all of them on runtime.

How can I implement the event and how can I get the id / name / index via the event object sender?


Thanks a lot for helping!




Avatar of b1xml2
b1xml2
Flag of Australia image

The event delegates must have the signature (object sender,EventArgs e)

an event is simply a special class member that provides notifications. The delegate for the event is multicasted. It means that mutliple subscribing to the event can happen.

public class UserControl1 : UserControlBase
{
      
      private void Change()
      {
            OnValueChanged(new ValueChangedEventArgs(9));
      }
}

public class UserControl2 : UserControlBase
{
      private void Change()
      {
            OnValueChanged(new ValueChangedEventArgs(10));
      }
      
}



public class UserControlBase : UserControl
{
      
      private static readonly ValueChangedEvent = new object();
      
      public event ValueChangedEventHandler ValueChanged
      {
            add { this.Events.AddHandler(ValueChangedEvent,value); }
            remove { this.Events.RemoveHandler(ValueChangedEvent,value); }
      }
      
      protected void OnValueChanged(ValueChangedEventArgs args)
      {
            ValueChangedEventHandler handler = this.Events[ValueChangedEvent] as ValueChangedEventHandler;
            if (handler != null)
                  handler(this,args);
      }
      
      
      
      
}

public delegate void ValueChangedEventHandler(object sender,ValueChangedEventArgs args);

public class ValueChangedEventArgs : EventArgs
{
      private int id;
            
      private ValueChangedEventArgs() {}
      
      public ValueChangedEventArgs(int id)
      {
            this.id = id;
      }
      
      public int ID
      {
            get { return this.id; }
      }
}
By using the EventList object found within the ComponentModel and applicable to WinForms, WebForms. UserControls, you create one instance of the event per class. This optimises the event handling. The reason why the event has a void returned value is that it doesnt need to return a value as it can pass values/objects via the 2nd parameter. The first parameter often takes the nearest parent or self control that implements the IContainerControl.
Avatar of Bob Learned
Use a different structure to store the objects, such as HybridDictionary.  The HybridDictionary is a specialized type, in that it acts like a List with < 10 items, and a Hashtable with > 10 items.  You add items to the collection using the Add method with a key and a value, and retrieve objects using the key.

Bob
to furthe explain, with the code posted, you can have as many instances of UserControl1 and UserControl2 but the event is only created once! That is to say once event for all instances. This is not available to VB.NET and is a key feature of CustomControls both in WinForms as well as WebForms.

Along with the lack of operator overloading, it makes C# the choice language when writing custom controls as well.

HTH
You can convert the 'sender' object into a UserControl type, and get the name, and look it up in the collection.

Bob
Avatar of i-Thomas
i-Thomas

ASKER

Hi both of you! Thank you very very much!

I think your feedback is really great, but at the moment it is hard to digest for me. I am not an expert, yet ;-)

I think if you could answer with a bit more concrete code and concrete explanation here and there, I can manage with it. And then I will understand also your more general example.


I would like to create "ClosePanel" event for a UserControl that consists of a panel that contains a button on it that publishes this event.

The panel exists not once, but many times on the form and is created during runtime from the main app code. Each panel is stored in an ArrayList.

@TheLearnedOne: I do not understand your comment about HybridDictionary, did you adress me with that?


So I try to place more concrete questions:

public class myUserControl: System.Windows.Forms.UserControl
{

What to write here in the UserControl code to publish the event so that it can be "seen" from main app code?

    private void PanelClose()
    {
           OnPanelClose(new PanelCloseEventArgs());
    }

does not work

}

-----

public class FormMain : System.Windows.Forms.Form
{

    private void InitializeComponent()
    {
             // What to write here to initialize the events?
    }


    public ArrayList myControlPanels = new ArrayList();    // defining the ArrayList

    private void addMyControlPanel(string Name)             // adding the panels to the form at runtime
        {
            int index = myControlPanels.Count;
            myControls.myControlPanel DynPanel = new myControls.myControlPanel();
            DynPanel.myControlName = Name;
            DynPanel.Location = new Point(10+(index * 150), 10);
            myControlPanels.Add(DynPanel);
            this.MainDynPanel.Controls.Add((myControlPanel)myControlPanels[index]);
        }



      // What to write here to subscribe to the events of the different myControlPanels?


}


Thank you so very much for helping in advance. I hope that you now can better understand what I need. I hope that this thread will be of help also for many other C# - beginners!

Best regards,

Thomas



(1) Add using System.Collections.Specialized

(2) Code to highlight:

// defining the Collection of panels
public HybridDictionary myControlPanels = new HybridDictionary ();    


    private void addMyControlPanel(string Name)            

       // adding the panels to the form at runtime.

        {
            int index = myControlPanels.Count;
            myControls.myControlPanel DynPanel = new myControls.myControlPanel();
            DynPanel.myControlName = Name;
            DynPanel.Location = new Point(10+(index * 150), 10);

            // Add the panel to the collection of panels, using the name as the key to find later.
            myControlPanels.Add(Name, DynPanel);
            this.MainDynPanel.Controls.Add((myControlPanel)myControlPanels[index]);
        }


Bob
Hi Bob!

I am really curious how you can be so fast!

If you have a device that translates your thoughts directly to code and in addition sends it to a textfile from your brain then let me know :-)

Is there a special area for official praises at ee ;-) ?
When you have been developing as long as I have (20+ years), it starts to become intuitive.  Don't get me wrong, though, I still have a lot to learn.  I guess I'll stop learning when I am dead :)

Bob
@TheLearnedOne:

1) You wrote:

"Use a different structure to store the objects, such as HybridDictionary.  ... You add items to the collection using the Add method with a key and a value, and retrieve objects using the key."

So the main advantage of HybridDictionary compared to ArrayList is that I can use the panel's name to retrieve it instead of using the index, right?

2) You wrote:

"...The HybridDictionary is a specialized type, in that it acts like a List with < 10 items, and a Hashtable with > 10 items..."

I cannot fully understand this advantage or difference, because I do not know what a Hashtable is or how it's behaviour differs from that of a List...

It is not unlikely that the count of myControlPanels varies around 10, so sometimes 7, sometimes 12... Will the behaviour of myControlPanels change or is it a matter of speed / optimization somehow?



Definition of hash table:

Hash table
From Wikipedia, the free encyclopedia.

In computer science, a hash table is an associative array data structure that associates keys with values. The primary operation it supports efficiently is a lookup, where it is given a key, an identifier for the information to be found such as a person's name, and asked to find the corresponding value. It works by transforming the key using a hash function into a hash value, a number that describes, roughly speaking, the location of the desired information.

Hash tables are often used to implement associative arrays, sets and caches. Like arrays, hash tables can provide constant-time O(1) lookup on average, regardless of the number of items in the table. However, the rare worst-case lookup time can be as bad as O(n). Compared to other associative array data structures, hash tables are most useful when a large number of records of data are to be stored.

Hash tables store data in psuedo-random locations, so accessing the data in a sorted manner, is at best, a very time consuming operation. Other datastructures such as self-balancing binary search tree generally operate slightly more slowly and are rather more complex to implement than hash tables but maintain a sorted data structure at all times. See a comparison of hash tables and self-balancing binary search trees.

================================================

When hash tables are small they are inefficient, and lists are much more efficient.  The HybridDictionary has the internal ability to switch between a Hashtable and an ArrayList, depending on the number of elements, which is a very slick feature.  You can look up values within the list efficiently (O(1) = Order of 1 = It doesn't slow down when more items are added to the structure).

Bob
Ahh, thanks for additional information. Now I can understand why Hybrid Dictionary is a good choice.

Could you please help me with further with my core question, namely how to get the events running? If you have further questions to that, please do not hesitate to ask me!

Thanks for helping!
@b1xml2 (or TheLearnedOne)

Could you please so kind and give me some more concrete explanations on what you wrote? I tried to be more specific with my questions and explanations above.

I would be very grateful, if you could help me with that. In parallel I started collecting more background info about events and delegates so that I hope to be able to follow your additional explanations without problems! This event handling is a core issue I have to solve and it prevents me from continuation of my project´.

Thank you very much in advance,

i-Thomas
Let's get down to brass tacks:

(1) Add event handling:

      private void addMyControlPanel(string Name)            

       // adding the panels to the form at runtime.

        {
            int index = myControlPanels.Count;
            myControls.myControlPanel DynPanel = new myControls.myControlPanel();
            DynPanel.myControlName = Name;
            DynPanel.Location = new Point(10+(index * 150), 10);

            // Add an event handler to the panel.
            DynPanel.ClosePanel += new System.EventHandler(this.Panel_Close);

            // Add the panel to the collection of panels, using the name as the key to find later.
            myControlPanels.Add(Name, DynPanel);
            this.MainDynPanel.Controls.Add((myControlPanel)myControlPanels[index]);
        }

(2) Catch the event

     private void Panel_Close(object sender, PanelCloseEventArgs e)
     {

        // Convert the sender object to a specific object type.
        myControls.myControlPanel panel = (myControls.myControlPanel) sender;

        // Get the name of the panel.
        string name = panel.Name;

     }

Bob
Hi Bob!

Thank you very much for your additional help. I think that was the key hint I needed concerning how to add the event for each panel and how to subscribe...


Is it possible that you made a mistake in the code below:

        // Add the panel to the collection of panels, using the name as the key to find later.
            myControlPanels.Add(Name, DynPanel);
            this.MainDynPanel.Controls.Add((myControlPanel)myControlPanels[index]);      

// does not work via index, panel is not added or visible

        }

I can add myControlPanel in this way:

this.MainDynPanel.Controls.Add((myControlPanel)myControlPanels[Name]);    // panel is created and added

But I thought I could also use an index for the HybridDictionary items, or can I retrieve them ONLY via a key (in this case the name)?


Regards,

Thomas


ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If you need to do fast lookups by name and by index (a scenario that I encountered recently) you can wrap a Hashtable (or HybridDictionary) that inside a custom collection, and use it to link to the object's index inside the collection, and provide an accessor for the index as well as the hash key.  For example:

    public class MyControlPanelCollection : CollectionBase
    {
      private HybridDictionary _lookupTable = new HybridDictionary();
     
      public MyControlPanel this[int index]
      {
        get { return (MyControlPanel)InnerList[index]; }
      }

      public MyControlPanel this[string name]
      {
        get { return (MyControlPanel)InnerList[(int)_lookupTable[name]]; }
      }

      public void Add(MyControlPanel panel, string name)
      {
        InnerList.Add(panel);
        int totalItems = InnerList.Count;
        _lookupTable.Add(name, totalItems-1);
      }
    }

You could then access elements in the collection by key or by index.

this.MainDynPanel.Controls.Add(myControlPanels[index]);
this.MainDynPanel.Controls.Add(myControlPanels["Some Panel"]);

Ssince the collection is strongly typed, you don't have to worry with typecasting anything.

Just a note - when you lookup an element by name, it's not gonna be quite as fast as using either a standard hashtable since it has to lookup the index in the table, then locate the element in the collection.  My tests show roughly a 10% decrease in performance, which isn't a huge deal in most cases.
Is there an echo in here?  :)

Bob
@jdraper:

Thank you very much for your additional comments. I will figure out, if I really need indexing with key and numbers, but for sure I will have to cope with it sonner or later!


@b1xml2:

You posted

public class ValueChangedEventArgs : EventArgs
{
     private int id;
         
     private ValueChangedEventArgs() {}   // this line is not clear to me...


Can you please explain why the last line is necessary and why you use "{}" at the end?

@all:

As far as I can understand it now, I have two options for publishing or "wiring up" my event to the close button in my user control:

1) override the original click event of the close button
2) call my own event in the click event of the close button

Is this correct?

If so, what version is the better choice in your opinion?


Thanks for caring!



May I suggest to continue the question in another thread?

You helped me very much already and deserve your points, but I still do not get everything together completely...
Hi out there!

I posted a new question summing up what I have now including the line that still causes problems for me. I thought this is better way to start a new thread so that you can earn more points:

https://www.experts-exchange.com/questions/21427290/events-problem-with-delegate.html


I want to accept this question and suggest to split up as follows:

TheLearnedOne: 200 points
b1xml2: 200 points
jdraper3: 100 points

Do you agree?
Sounds good to me.  I have tried to get to the place where points don't matter.  It becomes a lesson in teaching and learning for me.

Bob
Hi Bob!

Sorry, my (German) English is limited in this case. What do you mean by "I have tried to get to the place where points don't matter." ?

Regards,

Thomas


BTW: Is it possible to contact you via email for some freelance job or something comparable?
Ich verstehe.  Mein Deutsch ist rostig, aber ich sagte:  Punkte machen nicht aus.

Bob
My email is TheLearnedOne{@}experts{-}exchange{.}com

Bob