Joe White’s Blog

Life, .NET, and Cats


Control.Visible… ugh.

Control.Visible lies.

I vaguely recalled this, actually, from some sessions in Reflector a few months ago. But it just bit me today. Not in a major way, but enough to be irritating.

I put my search tool’s search options into two expandable/collapsible panels, one for selecting the directories, the other for the search options. I’m not offhand aware of any good collapsible panels for WinForms, so I just rolled my own, with one control for the header and then a panel for the contents. And I showed a little “+” or “-” glyph on the header, so it would look familiar to anyone who’s used a treeview. I updated that glyph based on the Visible property of the panel, and had hooks in various places to make sure the glyph was up-to-date.

Well, this morning I noticed that, when the program came up, those glyphs were wrong. The first time I do anything to update them, they correct themselves and stay correct for the rest of the program run.

Set breakpoints. The method that updates the glyphs gets called several times during startup, and each time, the panels’ Visible property is true… up until the last time, when it’s false.

Now, I know I’m not changing the panels’ Visible property during startup. And I know that the panels are visible once the window is displayed. So… what the heck is the deal?

At least WinForms gives me a VisibleChanged event that I can hook. So I hook it, and put in a breakpoint. Lo and behold, it does indeed fire when the panels’ Visible is mysteriously changing to false. (Oddly enough, it does not fire again when their Visible is mysteriously changing back to true.) I check the stack trace, and among the code on the stack is Control.WmShowWindow.

That’s when it clicks in my head: Aha, that’s right. The value you set into Visible is not necessarily the value you get back out.

The MSDN docs have this to say:

Gets or sets a value indicating whether the control is displayed.

Wow, that’s helpful. I can tell you from looking in Reflector, however, that what happens when you read Visible is this: it returns this.Visible && Parent.Visible && Parent.Parent.Visible && Parent.Parent.Parent.Visible && …

That’s not quite how they implement it, but it makes Visible nearly useless for nearly everything. It certainly makes it useless for what I was using it for, because there are times when it returns random values (i.e., when the window is in the process of being shown, and events are getting fired, but Form.Visible is not yet true so all of its children lie).

This is not how properties are supposed to work. I want a property to return the value I set into it. Bad WinForms! No biscuit!

Once again, this is an area where Delphi got it right. You can set TControl.Visible to True, and it will return True, even if it’s on a form that’s not visible yet. They have a separate property, called Showing, that has the other “is it actually visible all the way up the chain?” behavior, for those rare occasions when that’s what you want. This seems much more in keeping with common sense.

Sigh. Not a big deal, once I identified the problem; I just added a couple more properties to my model object (ExpandDirectoryOptions and ExpandSearchOptions), and used them instead of reading the Visible property. Voila; problem solved.

Lesson learned: Consider the WinForms Control.Visible property to be write-only.

8 Responses to “Control.Visible… ugh.”

  1. Frans Says:

    I was about to try the exact same thing. Thank you for saving me the work!

  2. Bartek Says:

    Exactly what I needed, after running into similar problems. Thanks.

  3. Steven Says:

    And offcourse the same for System.Web.UI.Webcontrols … too bad

  4. Paulustrious Says:

    Thanks. I have a hole in my head from scratching it. You were near the top of a Google for Control.Visible. I am fresh from Powerbuilder which works like Delphi and you are right - I can write off Control.Visible

  5. Paulustrious Says:

    Got it.

    You needed Control.Available

  6. James Crisp Says:

    If you want to know if the control is set to visible or not, you can check it using reflection:

    static bool GetControlVisible(Control control)

    {

    return (bool) GetStateMethodInfo.Invoke(control, new object[] { 2 });

    }

    static MethodInfo GetStateMethodInfo

    {

    get

    {

    if (getStateInfo == null)

    {

    getStateInfo = typeof(Control).GetMethod("GetState", BindingFlags.Instance | BindingFlags.NonPublic);

    }

    return getStateInfo;

    }

    }

    static MethodInfo getStateInfo;

  7. Joe White Says:

    You could if you had to, I suppose, but that’s going to be really brittle. I’d rather stick to the documented APIs that Microsoft won’t break without notice…

  8. James Crisp Says:

    Agreed, if it’s possible to do with public API, that’s much better. But there are times, especially as a framework developer, that you really need functionality that Microsoft did not make public. We’ve moved from .net 1 to .net 2 with a very large code base (~4 million lines of code), including some reflection like the above in our framework. Our saviour has been unit testing. If the code is broken, you find out right away.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Joe White's Blog copyright © 2004-2008. Portions of the site layout use Yahoo! YUI Reset, Fonts, and Grids.
Proudly powered by WordPress. Entries (RSS) and Comments (RSS).