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
2008-12-24 17:36:09
Thanks, I'll vote up after waiting for more answers.
Qwertie
2008-12-24 18:00:07
What is the 2? Is there a constant or enum you can use instead?
Jon B
2008-12-24 18:12:13
@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
2008-12-24 18:17:02
@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
2008-12-24 18:18:31
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
2010-01-04 16:22:48
Sorry, no I don't. You might have to walk upwards.
Marc Gravell
2010-01-04 16:30:15
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
2008-12-24 17:48:55
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
2008-12-24 17:51:08
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
2008-12-24 18:03:09
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
2008-12-24 18:06:48
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
2008-12-24 18:27:47
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
2008-12-24 18:33:06