3

Example 1:

<asp:Panel Visible="false" runat="server">
    <asp:TextBox ID="textbox" runat="server" />
</asp:Panel>

Here, textbox.Visible returns false in code (even though TextBox.Visible was not set explicitly; it seem to "inherit" the property from its invisible parent).

Example 2:

<asp:DataGrid ID="grid" runat="server" AutoGenerateColumns="false">
    <Columns>
        <asp:TemplateColumn Visible="False">
            <ItemTemplate>
                <asp:TextBox ID="textbox" runat="server" />
            </ItemTemplate>
        </asp:TemplateColumn>
    </Columns>
    ...
</asp:DataGrid>

Here, ((TextBox)grid.Items[0].FindControl("textbox")).Visible returns true (under the assumption that the DataGrid has at least one row).


Question: Is this inconsistent behaviour by design? In both cases, the TextBox is not rendered because some parent element is invisible.

(Granted, in the second case the textbox is inside a template, but I'm not querying an abstract TextBox in the template definition, I'm querying the specific, concrete TextBox in row number 0.)

Background: TextBox is a smart control: It only saves its Text property in the ViewState if it's invisible. That makes sense: If the TextBox is visible, it's rendered as a HTML <input> control and its current Text value is submitted on a postback - no need to submit it again via the ViewState. Of course, if the TextBox is invisible, it is not rendered, and, thus, any changes to the Text property would be lost.

Now, Example 2 is giving us some trouble. textbox thinks that it's being rendered (according to IL spy, it checks its own Visible property in TextBox.SaveTextViewState), so it doesn't use the ViewState and all changes to textbox.Text done in code are lost. I'm now wondering whether this is bug or not.


Related question: How to get the set/real value of the Visible property in Asp.Net.


EDIT: I've created a Microsoft Connect Bug Report on this:

Community
  • 1
  • 1
Heinzi
  • 167,459
  • 57
  • 363
  • 519

1 Answers1

5

TextBox and Panel both inherit from WebControl, which inherits from Control. Control has the following definition for the Visible property:

public virtual bool Visible
{
    get
    {
        return !this.flags[16] &&
               (this._parent == null || this.DesignMode || this._parent.Visible);
    }
    set
    {
        if (this.flags[2])
        {
            bool flag = !this.flags[16];
            if (flag != value)
            {
                this.flags.Set(32);
            }
        }
        if (!value)
        {
            this.flags.Set(16);
            return;
        }
        this.flags.Clear(16);
    }
}

Since Visible is not implemented directly on TextBox, it will always return false if the Visible property of the parent is false (at runtime and if there is a parent specified). So to answer the question asked in the title, the Visible property is not automatically set to false at any point in time, it's just evaluated when it is accessed. In the case of the GridView where TextBox.Visible returns true, it stands to reason that the parent control of the TextBox is not the TemplateColumn, or at least the parent does not have its Visible property set to false.

EDIT
So, using your example grid in the question, if you walk the chain of controls up, you'll see that all of the parent controls for the TextBox are Visible. I used the code below, and here's the output:

TextBox (ClientID = grid_textbox_0, Visible = True)
TableCell (ClientID = grid_ctl00_0, Visible = True)
DataGridItem (ClientID = grid, Visible = True)
ChildTable (ClientID = grid_ctl00, Visible = True)
DataGrid (ClientID = grid, Visible = True)
HtmlForm (ClientID = form1, Visible = True)

Code:

protected void btnSubmit_Click(object sender, EventArgs e)
{
    var control = grid.Items[0].FindControl("textbox");
    while(control != this && control != null)
    {
        Response.Write(string.Format("{0} (ClientID = {1}, Visible = {2})",
            control.GetType().Name, control.ClientID, control.Visible));
        Response.Write("<br />");
        control = control.Parent;
    }
}

It seems to me that the DataGrid and the Visisble properties are working as expected.

rsbarro
  • 27,021
  • 9
  • 71
  • 75
  • +1, thanks for the explanation. I'll wait a bit before accepting it as an answer, since I'm still curious about why exactly this fails to work in Example 2. After all, the code you show recursively calls `_parent.Visible`, so it should suffice if there's *any* hidden control up the parent tree, not just the direct parent. – Heinzi Jul 14 '11 at 05:54
  • @Heinzi I'll try to take a closer look at example 2 later today. Maybe GridView has it's own Visible implementation or something. – rsbarro Jul 14 '11 at 16:51
  • @Heinzi I updated my answer to show why the TextBox is showing as Visible in your second example. One thing I'm confused about though, how is the value of the TextBox changing if the TemplateColumn is not visible? Are you saying that you are setting the TextBox to a value when the grid is databound and then you can't read it back out on PostBack? – rsbarro Jul 20 '11 at 01:07
  • Thanks, that's very interesting! I don't think that the TableCell should have Visible=True, though, since it is definitely not rendered (so I guess the "bug" is in there). About the TextBox thing: Yes, I'm changing the value of the `Text` property in code. Since the Textbox thinks that it's visible, it does not persist the value into the ViewState, thus, the value is lost on the next postback. – Heinzi Jul 20 '11 at 06:15
  • @Heinzi I agree, you can definitely make the argument that the `TableCell` should have `Visible=false`. A workaround for this problem is to use a different control to store the value, like `asp:Label` (or the `CommandArgument` attribute if there is a button handy). The `Label` should have its value persisted on postback. – rsbarro Jul 20 '11 at 10:45
  • @Heinzi My mistake in the previous comment, I think an `asp:Literal` control would be the one to use, not `asp:Label`. – rsbarro Jul 20 '11 at 14:57