Now where did I put that member?

Got this Ladybug report from a customer concerned that intellisense was broken because he wasn't seeing all the valid members in a drop down list when he did this:

        public void DoSomethingOfRelevance()

        {

            PrintPreviewDialog ppDialog;

            ppDialog<dot>

        }

Specifically, items in the list that were missing were properties like "WindowState", "PrintPreviewControl" and others. Oddly enough I'd just debugged an incredibly similar matter with Daigo concerning System.Windows.Forms.CheckedListBox and the DataSource property. He was incredibly frustrated and came to me to tell me that Intellisense was broken and that he should be seeing this member in the list. At first I thought it must be something wrong with our metadata reader. If we can't understand something from metadata we don't read it in and proceed to the next thing in the assembly (this is actually pretty common as many languages out there produce things we can't understand (like properties with multiple arguments (whee! nested parenthesis are fun!))). However, in this case it turns out that we were reading it in just fine. So what could be the problem? Anyone out there have an idea?

*chirp*

Well, I could wait for ideas, but I should just tell you. As it turns out the member was marked in metadata as having the following property: 

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

From the docs you can see that the attribute is used to: "Specifies that a property or method is viewable in an editor" and that the value passed in states: "The property or method is never browsable from within an editor". And, if you look at the DataSource property you'll see that it has these docs: "This member supports the .NET Framework infrastructure and is not intended to be used directly from your code." Note: there is also a state called "Advanced" and one can view items marked as advanced if they toggle

 Tools | Options | Text Editor | All Languages | Statement Completion | Hide Advanced Members.

Now, the reason we read it in from metadata is that we need to make sure that we know about that member so that it will appropriately hide members. Using Daigo's example, if we were to forget about that member then we'd place ListControl.DataSource in the list (ListControl is a base type of CheckedListBox) which would be really bad as you'd think that that was the datasource you were accessing when in actuallity your compiled code would access the datasource in the derived type.

Personally, I find this behavior incredibly confusing, frustrating and scary. As a developer one rightly gets quite confused when the docs tell you one thing and intellisense seems to tell you something else. One also wonders why in this design one would make these properties public but then not expect others to use them. It's scary because I don't think that intellisense should be filteringout legal things without making the user aware (see this previous post on that topic). I was thinking about doing something different so that this would be a lot clearer. My ideal solution would be to have the element in the completion list, but with a strikethrough through it. The tooltip would also say something along the lines of "Warning. The author of this component did not intend for it to be used externally" followed by the actual documentation on it. Unfortunately, it does not look like one can have that much control over the font in the completion list in VS2005. So, as an alternative, I could grey out the text and have the tooltip updated as well.

How do you feel about this? I might be personally biased here, but this "feature" really rubs me the wrong was and I definitely think we should do better. If we don't, I think we'll have a log of confused and irate developers on our hands.