views:

515

answers:

2

If you set the Visible property of a Windows Forms control to true, that property still returns false if any of the control's parent windows are hidden. Is there a way to get the true, underlying visibility flag of the control in case the parent window is hidden?

+5  A: 

Well, the regular implementation does check up the control stack, to ensure that all parents are visible. The only way I know to dodge this is to cheat with reflection, and ask for GetState(2), but that is brittle:

    // dodgy; not recommended
    Panel query;
    Form form = new Form
    {
        Controls = {new Panel {
            Visible = false,
            Controls = {
                (query = new Panel {Visible = true})
            }
        }
        }
    };
    form.Show();

    // this is the dodgy bit...
    bool visible = (bool)typeof(Control)
        .GetMethod("GetState", BindingFlags.Instance | BindingFlags.NonPublic)
        .Invoke(query, new object[] { 2 });
Marc Gravell
Thanks, I'll vote up after waiting for more answers.
Qwertie
What is the 2? Is there a constant or enum you can use instead?
Jon B
@Jon, yes, there is - but it is itself internal, so you'd have to use more reflection to get it ;-p But it is STATE_VISIBLE.
Marc Gravell
@Qwertie; up-votes don't usually detract answers; marking as answered might, but to be honest I'm pretty sure there isn't a more elegant way to do this...
Marc Gravell
This is for win forms, do you know if this is also the only way to obtain the "local" visible within asp.net forms as well?
Robin Day
Sorry, no I don't. You might have to walk upwards.
Marc Gravell
A: 

An option that doesn't rely on reflection would be to recurse through the parents of the control hierarchy looking for one with Visible set to false.

EDIT: See comments for significance of code.

var frm2 = new Form {Text = "Form2"};
var lbl = new Label {Visible = true};
frm2.Controls.Add(lbl);
frm2.Show();

var frm1 = new Form {Text = "Form1"};
var lblVis = new Label { Text = lbl.Visible.ToString(), Left = 10, Top = 10};
lbl.VisibleChanged += (sender, args) => MessageBox.Show("Label Visible changed");
var btnShow = new Button {Text = "Show", Left = 10, Top = lblVis.Bottom + 10};
btnShow.Click += (sender, args) =>
                  {
                   frm2.Visible = true;
                   lblVis.Text = lbl.Visible.ToString();
                  };
var btnHide = new Button {Text = "Hide", Left = 10, Top = btnShow.Bottom + 10};
btnHide.Click += (sender, args) =>
                  {
                   frm2.Visible = false;
                   lblVis.Text = lbl.Visible.ToString();
                  };

frm1.Controls.AddRange(new Control[] {lblVis, btnShow, btnHide});

Application.Run(frm1);

Jeremy Wilde
But that doesn't tell you whether the original is true or false; if there are no invisible parents, you can (probably) trust .Visible, but if there *are*, you have no clue whether your control has (locally) Visible=true or Visible=false
Marc Gravell
You would have to look at both, so if currently visible finding any parent that is false would mean it isn't currently.
Jeremy Wilde
But Visible will never return true if any parent is invisible; that is the problem: the framework already checks this. I think the OP wants to know whether the control *locally* thinks it is visible, i.e. if all parents were visible, would it be too? You can't find this just by Visible.
Marc Gravell
Interesting, was not seeing it change because I was basing it off of the VisibleChanged event which was not firing when the parent controls visible property was changing.
Jeremy Wilde
Guess this is a leaky abstraction, will edit post to put some code showing what I mean. When the parent is hidden a childs VisibleChanged is not fired, but when the parent is made Visible it is.
Jeremy Wilde
I'm not sure what this code is useful for, even after running it.
Qwertie