3
\$\begingroup\$

I'm trying to make a simple UI to better understand how interfaces and the System.Windows.Forms Controls behave.

Like the above namespace, I will be able to get and set the Focus on a particular control, with a helper method to drill down to see which Control (or child Control) actually has the focus. I'm pretty rubbish for recusion.

Here's what I have so far:

public class Control
{
    public bool CanFocus { get; set; }
    public bool HasFocus { get; set; }

    public Control GetFocus()
    {
        if (HasFocus)
            return this;

        foreach (Control c in Children)
        {
            if (c.HasFocus)
                c.GetFocus();
        }

        return null;
    }

}

My thought process is that it should return the immediate control if it has focus. If not, it will recurse through each of its children. If that child has the focus, it will return that child, and if not, will find the next one, repeating until a default case (null) is reached.

I had thought about a ContainsFocus field as well, but it "sort of" overlaps with what I'm doing here. I considered a helper method with the signature bool FindFocus(out Control result), but it felt unwieldy. Also, I am concerned with invoking foreach within a recursive function.

I appreciate any insight into this.

\$\endgroup\$
1
\$\begingroup\$

Shouldn't need any recursion or iteration if you set all the controls to the same Enter event handler. Simply set a class level control object to sender whenever the event handler fires.

\$\endgroup\$
  • \$\begingroup\$ This answer gets the checkmark since it solves my problem perfectly. Rather than having to muck with dependencies and recursion, I can just raise an event on the manager class, and have everything update accordingly. @ChrisW's answer is fantastic however, because it explains everything in depth. \$\endgroup\$ – Kyle Baran Jan 15 '14 at 0:35
4
\$\begingroup\$

Here's what I have so far

The code you wrote doesn't quite work properly; because, depending on the behaviour (which isn't clearly defined) of your HasFocus property:

  • Either it will return too early (it returns immediately, if this.HasFocus is true when a child has focus)
  • Or it will return null (it doesn't descend, if child.HasFocus is false when a grandchild has the focus).

I don't quite understand your question: Control is a System class, so how are you supplying source code for it? I'm guessing you're using it as an example and writing pseudo-code.

To avoid confusion, I'm going to:

  • Assume you're writing code for the standard System.Windows.Forms.Control class
  • Assume that Control has the standard ContainsFocus and Focused properties
  • Write a class which defines GetFocus has a new extension method

I suggest:

public static class ControlExtensions
{
    public static Control GetFocus(this Control control)
    {
        if (control.Focussed)
            return this;

        foreach (Control child in Children)
        {
            if (!child.ContainsFocus)
                continue;
            return GetFocus(child); // recurses
        }

        return null;
    }
}

Or, assuming that it is your own Control class as you wrote it in the OP, I would implement its recursive GetFocus as follows:

public class Control
{
    public bool CanFocus { get; set; }
    public bool HasFocus { get; set; }

    public Control GetFocus()
    {
        if (HasFocus)
            return this;

        foreach (Control child in Children)
        {
            Control found = child.GetFocus();
            if (found != null)
                return found;
        }

        return null;
    }

    public bool ContainsFocus
    {
        get { return GetFocus() != null; }
    }
}

In this second implementation, the GetFocus implementation doesn't call ContainsFocus:

  • Because ContainsFocus calls GetFocus
  • Because ContainsFocus descends the tree of children in order to decide whether to return true, so GetFocus might as well do the same (descend the tree of children) but return the actual Control instead of just returning bool.

Beware that your HasFocus implementation (as coded above) isn't good enough: because you must ensure that only one Control has the focus (so if you set the focus, then you need to remove focus from whichever control previously had the focus, if there is one).

I considered a helper method with the signature bool FindFocus(out Control result), but it felt unwieldy.

It's enough to return null if there is no focus.

You might want to return bool if you want to return additional information; for example:

Customer customer;
bool success = FetchCustomer(customerId, out customer);
if (!success)
{
    // fetch failed because database or network is down
}
else
{
    if (customer == null)
    {
        // fetch successfully says that there is no such customer
    }
    else
    {
        // fetch successfully returned the requested customer
    }
}

In your example however the GetFocus algorithm/implementation cannot 'fail', so returning Control is enough (instead of returning bool as well as having an out parameter).

Having an out parameter is necessary if null is a valid value: for example the Dictionary.TryGetValue method (because a Dictionary can contains nulls, so it needs a bool to say whether it's in the dictionary at all).

Having a bool with an out parameter is also necessary if the type in question is a struct instead of a class: because a struct cannot be null. In this case though, Customer is a class not a struct.

Also, I am concerned with invoking foreach within a recursive function.

Why? It's not a problem per se.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.