why does my usercontrol keep resetting it's visible property to false? - c#

Ok, I have a usercontrol on my page.
On the page, the visible property is set to false.
On the OnPreRender event, I set the visible property to true.
It runs the line of code, but does not actually change anything. (so visible remains at false)
This exact same method works across every other control, and there is nothing special about this control.
Any ideas??

Check for the visible property on any controls containing this control.
Setting Visible=True does not mean that Visible==True, it will still return False if a parent control is False.
Other than that though, you may need to post some examples of your code in order for anyone to help track down what the problem may be.

I faced the same problem and... yes the problem is the parents not be visible. So I made this little peace of code to solve the problem:
public static void ForceVisibleState(Control control, bool visible)
{
if (!visible)
{
control.Visible = false;
}
else
{
// Must set all parents to 'visible = true'
List<Control> parents = new List<Control>();
while (control != null &&
!control.Visible)
{
parents.Insert(0, control);
control = control.Parent;
}
foreach(Control parent in parents)
{
parent.Visible = true;
}
}
}

Related

ToolStripMenuItem strange behavior on visible property [duplicate]

This question already has an answer here:
How to set a ToolStripMenuItem Visible in code?
(1 answer)
Closed 1 year ago.
I have encounter that issue a long time ago and again now. I cannot figure out what special thing had to be done to edit the ToolStripMenuItem control a Visible property while inside a form load.
in designer.cs
private System.Windows.Forms.ToolStripMenuItem mnuExport;
//
// mnuExport
//
this.mnuExport.Name = "mnuExport";
this.mnuExport.Size = new System.Drawing.Size(116, 22);
this.mnuExport.Text = "Export";
this.mnuExport.Visible = false;
this.mnuExport.Click += new System.EventHandler(this.mnuExport_Click);
in the form code
public partial class MainBuilder : Form
{
private void MainBuilder_Load(object sender, EventArgs e)
{
mnuExport.Visible = true;
}
}
In the code above export is a menu item of type ToolStripMenuItem which trough the property grid in the form design mode I have modified it's Visible property to false. So in the form load I want to switch to the visible state to true. I thought it be easy so I coded all the logic around and it failed. So I decided to remove all my code and simply hardcode the set to true and to my surprise this does not work.
When I put the breakpoint on the line in the form_load I clearly see it's false and if it let the line run the value is still false.
I recall seeing this issue in the past but cannot find anything about it. I also tried with 4-5 other menu item in that window and they all show the same behavior.
EDIT
just tried to put visible = true in the designer.cs instead and in the form_load still tells me the value is false. There is some major issues here.
As mentioned in the comments the property Visible getter does not reflect the actual inner property value until later in the lifecycle. It is still unavailable in the form_loaded event. My main problem is that one menu visibility state was based on if all sub menus are not visible then it should not either. To do so I was iterating on it's child and checking the Visible property. The problem is them having Visible to false due to the way it works the parent set it's own Visible to false as well.
I don't like the solution but that's the only way to make it work
// get all sub menus
var subMenus = mnuExport.DropDownItems.Cast<ToolStripItem>().ToList();
// create a list of bool that will store the visible state I want the controls to have. put them to true by default
var menusVisibleStates = Enumerable.Repeat(true, menus.Count).ToList();
// set the visibility state of each sub menus
for (int i = 0; i < menus.Count; i++)
{
// some logic is here that choose true or false but is irrelevant here
var visibleStateIWant = true;
// set the visible state stored
menusVisibleStates[i] = false;
// set the actual control value that will change later on
menus[i].Visible = false;
}
/// now here I use the state I have specified and NOT the menu object property
mnuExport.Visible = menusVisibleStates.Any(o => o);

Controls in WPF popup sometimes cannot receive input

I have a WPF CheckBox inside a Popup, and I'm finding if it is inside the item template of a TreeView, then the CheckBox does not respond to user input. If it is outside of the TreeView, then there are no problems.
I have created a relatively minimal mock-up here:
https://github.com/logiclrd/TestControlsInPopupsNotWorking
Does anyone know why the CheckBox controls popped up from within the TreeView cannot be checked?
I think this is an oversight in the design of the TreeView. Take a look at this:
Note: Some code excerpts were tidied up to avoid wrapping.
// This method is called when MouseButonDown on TreeViewItem and also listen
// for handled events too. The purpose is to restore focus on TreeView when
// mouse is clicked and focus was outside the TreeView. Focus goes either to
// selected item (if any) or treeview itself
internal void HandleMouseButtonDown()
{
if (!this.IsKeyboardFocusWithin)
{
if (_selectedContainer != null)
{
if (!_selectedContainer.IsKeyboardFocused)
_selectedContainer.Focus();
}
else
{
// If we don't have a selection - just focus the TreeView
this.Focus();
}
}
}
This method is called from TreeViewItem.OnMouseButtonDown, which we can see is a class-level handler that's configured to receive handled events too:
EventManager.RegisterClassHandler(
typeof(TreeViewItem),
Mouse.MouseDownEvent,
new MouseButtonEventHandler(OnMouseButtonDown),
/* handledEventsToo: */ true);
I have verified with the debugger that Handled is set to true by the time the event makes it to the TreeViewItem.
When you press down on the left mouse button over the CheckBox, the CheckBox begins a speculative 'click' operation and marks the event as handled. Normally, an ancestor element wouldn't see a handled event bubble up, but in this case it explicitly asked for them.
The TreeView sees that this.IsKeyboardFocusWithin resolves to false because the focused element is in another visual tree (the popup). It then gives focus back to the TreeViewItem.
Now, if you look in ButtonBase:
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
if (ClickMode == ClickMode.Hover)
{
// Ignore when in hover-click mode.
return;
}
if (e.OriginalSource == this)
{
if (IsPressed)
{
SetIsPressed(false);
}
if (IsMouseCaptured)
ReleaseMouseCapture();
IsSpaceKeyDown = false;
}
}
We see that IsPressed is set to false when focus is lost. If we then go to OnMouseLeftButtonUp, we see this:
bool shouldClick = !IsSpaceKeyDown && IsPressed && ClickMode == ClickMode.Release;
With IsPressed now false, the click operation never completes, all because the TreeViewItem stole focus away from you when you tried to click the button.
As a work-around, I have had success so far with using the NuGet library Ryder (which looks like a freely-usable open-source (MIT license) version of Microsoft Detours) to intercept the HandleMouseButtonDown method in TreeView.
The Ryder library can be found in the NuGet library, and the code behind it can be found here:
https://github.com/6A/Ryder
Hooking the HandleMouseButtonDown method is pretty simple:
var realMethod = typeof(System.Windows.Controls.TreeView).GetMethod("HandleMouseButtonDown", BindingFlags.Instance | BindingFlags.NonPublic);
var replacementMethod = typeof(Program).GetMethod(nameof(TreeView_HandleMouseButtonDown_shim), BindingFlags.Static | BindingFlags.NonPublic);
Redirection.Redirect(realMethod, replacementMethod);
The shim that replaces the method can basically do what the real method does but with a fix that detects the cross-visual-tree focus situation:
static void TreeView_HandleMouseButtonDown_shim(TreeView #this)
{
// Fix as seen in: https://developercommunity.visualstudio.com/content/problem/190202/button-controls-hosted-in-popup-windows-do-not-wor.html
if (!#this.IsKeyboardFocusWithin)
{
// BEGIN NEW LINES OF CODE
var keyboardFocusedControl = Keyboard.FocusedElement;
var focusPathTrace = keyboardFocusedControl as DependencyObject;
while (focusPathTrace != null)
{
if (ReferenceEquals(#this, focusPathTrace))
return;
focusPathTrace = VisualTreeHelper.GetParent(focusPathTrace) ?? LogicalTreeHelper.GetParent(focusPathTrace);
}
// END NEW LINES OF CODE
var selectedContainer = (System.Windows.Controls.TreeViewItem)TreeView_selectedContainer_field.GetValue(#this);
if (selectedContainer != null)
{
if (!selectedContainer.IsKeyboardFocused)
selectedContainer.Focus();
}
else
{
// If we don't have a selection - just focus the treeview
#this.Focus();
}
}
}
Some reflection is needed since this interacts with a private field that is not otherwise exposed from the TreeView class, but as work-arounds go, this is a lot less invasive than what I tried at first, which was importing the entirety of the TreeView class (and related types) from Reference Source into my project in order to alter the one member. :-)

To focus or not to focus

I have a Panel that has AutoScroll = true.
In that Panel is a series of TextBoxes. I should note that the TextBoxes aren't on the panel directly but are nested several levels (about 4-5).
Now, the scrolling with my mouse wheel works only if the panel has focus, naturally. I can use Focus() within mouseEnter event to make sure the panel has focus.
However, the TextBoxes I mentioned earlier rely heavily on focus. Only the user should be able to remove focus from the TextBox by clicking somewhere else.
The TextBoxes are created dynamically and would make for a very messy code to keep an array of them, or any type of reference to check if they have focus. Not to mention that there could be a lot of them.
How do I give the focus to the Panel, but only if none of the TextBoxes is focused?
You don't need to keep an array of the dynamically created Textboxes, you can get the array using:
bool anyTextBoxFocused = false;
foreach (Control x in this.Controls)
{
if (x is TextBox && x.Focused)
{
anyTextBoxFocused = true;
break;
}
}
if (!anyTextBoxFocused)
{
//give focus to your panel
}
Edit
Based on How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?, even nested controls can be obtained using:
public IEnumerable<Control> GetAll(Control control,Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl,type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
then use it with:
var c = GetAll(this,typeof(TextBox));

winforms panel hide and show

I have two panels occupying the same space in one side of a splitviewcontainer.
I want to dynamically show or hide one panel or the other.
Approaches I've tried so far are:
if (treeView1.SelectedNode.Name.Contains("cat")) {
menuItemPanel.SendToBack();
foreach (Control control in menuItemPanel.Controls)
control.SendToBack();
menuItemPanel.Visible = false;
categoryPanel.Hide();
categoryPanel.Visible = true;
categoryPanel.BringToFront();
foreach (Control control in categoryPanel.Controls)
control.BringToFront();
categoryPanel.Show();
// ...
}
and the converse for the else case.
None of these seem to work. That is the categoryPanel is shown when a category node is selected, but the screen shows blank space when a menuItem node is selected.
What am I doing wrong?
The code doesn't make much sense, re-ordering the controls in the panels is not what you want to do. Also sounds that the code in the else clause is failing, code you didn't post. Do it like this instead (a guess):
bool categorySelected = treeView1.SelectedNode.Name.Contains("cat");
menuItemPanel.Visible = !categorySelected;
categoryPanel.Visible = categorySelected;
try this
if (treeView1.SelectedNode.Name.Contains("cat"))
{
menuItemPanel.Visible = false;
categoryPanel.Visible = true;
}

Iterating through tooltips

Guys, I have a windows form with a panel control and inside the panel control are several other controls with a System.Windows.Forms.Tooltip attached to them. How can I iterate through each tooltip and set the Active property of the tooltip to false? Tooltips, unlike other controls, are not actually controls. So I had this:
foreach (System.Windows.Forms.Control ctrl in this.pnlControl.Controls)
{
if (ctrl.Name.StartsWith("tt")) // since all my tooltip names start with 'tt'
{
System.Windows.Forms.ToolTip TipControl=(System.Windows.Forms.ToolTip)ctrl;
TipControl.Active=false;
}
}
This does not work though. It gets an error because the ToolTip control is not inherited from System.Windows.Forms.Control. Any ideas?
EDIT:
Okay Guys. I probably didn't go into enough detail to get the answer I needed. My problem is, I'm taking all the controls in my panel and moving them to a different panel. Once they are switched over, the tooltips are still attached to the controls, which is what I want. However I have no way to deactive or reactivate them once I move them since the form and the original panel no longer exist. However, I found a solution which I will post here.
How to add tool tips for two buttons? The correct way is NOT creating two instances of ToolTip in this way:
ToolTip tt1 = new ToolTip(); //or you can create one in the designer
tt1.ToolTipTitle = "test";
tt1.SetToolTip(button1, "caption1");
ToolTip tt2 = new ToolTip();
tt2.ToolTipTitle = "test2";
tt2.SetToolTip(button2, "caption2");
Remember that a ToolTip instance and a control are not one-on-one related. The right way for this example is:
ToolTip tt1 = new ToolTip(); //or you can create one in the designer
tt1.ToolTipTitle = "test";
tt1.SetToolTip(button1, "caption1");
tt1.SetToolTip(button2, "caption2");
To remove the tooltip of button2, use:
tt1.SetToolTip(button2,string.Empty);
For your case,we can use
foreach(Control c in this.Controls)
{
tt.SetToolTip(c,string.Empty);
}
Typically, you have a single ToolTip instance that handles the displaying of tool tips for all of your controls. That single ToolTip instance is just a regular member of your form. Simply set it's Active property to false.
Edit: OK, scrap my previous answer. Yes, ToolTip is a Component, not a Control, so it's not actually in the Panel at all. From your question, it sounds like you have one ToolTip instance and you use it for controls inside this Panel as well as for other controls, right? In that case the solution is simple: create a separate ToolTip instance and use that one for controls in the Panel, then just refer to it directly to deactivate it, eg.
ttPanel.Active = false;
Okay what I did was create a new class that is inherited from Control, like so:
public class TooltipMaster : System.Windows.Forms.Control
{
private System.Windows.Forms.ToolTip m_tooltip1;
private System.Windows.Forms.ToolTip m_tooltip2;
private System.Windows.Forms.ToolTip m_tooltip3;
private System.Windows.Forms.ToolTip m_tooltip4;
public System.Windows.Forms.ToolTip ToolTip1 {
get { return m_tooltip1; }
set { m_tooltip1 = value; }
}
public System.Windows.Forms.ToolTip ToolTip2 {
get { return m_tooltip2; }
set { m_tooltip2 = value; }
}
public System.Windows.Forms.ToolTip ToolTip3 {
get { return m_tooltip3; }
set { m_tooltip3 = value; }
}
public System.Windows.Forms.ToolTip ToolTip4 {
get { return m_tooltip4; }
set { m_tooltip4 = value; }
}
}
Then what I did was create an instance of this class inside my main form's Load event. Then I just assigned each of my 4 tooltips to the 4 tooltips in this class. Finally, I added this control to my panel. After doing all that, I could access the tooltips later by iterating through each control and looking for the TooltipMaster control.
Hope this makes sense!

Categories