Friday, November 30, 2007

Recursively finding controls - where to start?

I love hearing about bugs and problems in components I have authored.  Most people hate hearing about bugs (I assume because they like to think they are perfect), but I like it because it lets me know that people are actually using components I have written, which is always rewarding to know.

I had a report on the CodeProject in response to the article I authored, Persisting the Scroll Position of child DIV's using MS AJAX about a client-side InvalidOperationException during page initialization.   The particular user was embedding my control in a user-control placing two of the controls on the page.  Because my control is an "extender"-type control, one of its properties is the control ID of the other control.  I then use a recursive FindControl method to locate the desired control:

protected virtual Control FindControlRecursive(Control root, string id)
        {
            if(root.ID == id)
            {
                return root;
            }

            foreach(Control c in root.Controls)
            {
                Control t = FindControlRecursive(c, id);
                if(t != null)
                {
                    return t;
                }
            }

            return null;
        }

 

The call to FindControlRecursive looks like:

protected internal Control Control
        {
            get
            {
                if(_control == null)
                {
                    Page page = Page;
                    if(page == null)
                        throw new InvalidOperationException("Page cannot be null");

                    _control = (Control)FindControlRecursive(Page, ControlToPersist);
                    if(_control == null)
                        throw new InvalidOperationException("Could not located the specified control");
                }

                return _control;
            }
        }

Both DIV's in the user-control were named "div1" - can you spot the problem?

_control = (Control)FindControlRecursive(Page, ControlToPersist);

How about now? The problem was I was starting to scan from the Page and all of its children controls - since both DIV's were named "div1", it was always picking up the first control! To resolve this issue I changed Page to Parent:

_control = (Control)FindControlRecursive(Parent, ControlToPersist);

In this case, the Parent is the web-control (or page) hosting the extender control. Lesson Learned: Always start scanning controls from your parent control, not anything higher up in the chain.

No comments: