ToolStripMenuItem strange behavior on visible property [duplicate] - c#

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);

Related

C# WinForm TabControl Formatting Issue

When programmatically adding controls to a tab control, I have been using the Form_Load event to create and embed things like datagridviews into my UI. I made a class that inherits from DataGridView
class DBDataGridView : DataGridView
{
public DBDataGridView()
{
DoubleBuffered = true;
AllowUserToAddRows = false;
AllowUserToDeleteRows = false;
AllowUserToResizeRows = false;
AllowUserToOrderColumns = false;
AllowUserToResizeColumns = false;
RowHeadersVisible = false;
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
ReadOnly = true;
Dock = DockStyle.Fill;
SelectionMode = DataGridViewSelectionMode.FullRowSelect;
TabStop = false;
}
}
And I call it later in the Form_Load event like so
private void MainDesignerForm_Load(object sender, EventArgs e)
{
DBDataGridView _DGV = new DBDataGridView();
var listOfOverlays = new List<OverlaySelectionList>()
{
new OverlaySelectionList { Description = "Description 1", PartNumber = "123-R1"},
new OverlaySelectionList { Description = "Description 2", PartNumber = "456-R1"}
};
var overlayList = new BindingList<OverlaySelectionList>(listOfOverlays);
_DGV.DataSource = overlayList;
Tab_Overlay.Controls.Add(_DGV);
_DGV.ClearSelection();
}
This gridview is on the THIRD tab of the TabControl, and everything works as expected except the ClearSelection(). No matter where I call it, it does not clear the initial row selection of the DGV. However, if I fire the same code block from a button ON the third tab, the formatting AND the ClearSelection() behave as expected.
What is causing this behavior?
Thanks to 41686d6564 and Jimi for the insight into the specifics on why this was happening.
Reiterating what they said in the comments: Assignment of properties appear to be cached regardless of whether the control they belong to is active or not (Hence why all the sizing and formatting properties were present at run time). However, actions that require a handle, like ClearSelection() require the control to be shown and active for the intended behavior to be observed.
Setting the selected tab to where the DataGridView before calling ClearSelection() was the solution (Or in my case, I had nested tabs, so I had to follow the tab tree to get to the specific tab that the DataGridView was on)
So now, part of the Load_Form logic is to check WHERE the control is located, make that tab active, THEN format and clear selections for each control that is being added. This allowed ClearSelection() to work as intended.

How can I uncheck radiobuttons in C# while still allowing only the first radiobutton to be tabbable?

Not sure if the title makes much sense, so here is the full context:
I'm coding in C#.
I've made an app with several UserControls, each one with many textboxes and radiobuttons.
All radiobuttons are placed in a panel in a set of 2, looking like this:
[ <label> O <radiobutton1text> O <radiobutton2text> ]
(while the first radiobutton have TabStop = true, and the second's TabStop = false)
When tabbing to such panel, only radiobutton1text is focused, and when hitting the LeftArrow key the radiobutton2text is selected. That's the desired outcome.
In order to make a UserControl load faster the second (and above) time, I'm not closing it but rather replacing it with a different UserControl each time the contents need to change.
But this rises an issue: When UserControl X is open, then on top of it I open UserControl Y and then back to X, the textboxes and radiobuttons still have the contents from the first session of when I had UserControl X open for the first time. (I need the contents of textboxes and radiobuttons to be reset after replacing a UserControl).
So I made a function that loops through all controls and empties their contents.
The problem is, when I uncheck the radiobuttons (and restore their TabStop state to true) in this function, the second radiobutton is tabbable after I check either one of them and then invoke the function, whereas it wasn't before going through this function.
The function:
public void BackToMain(object sender, EventArgs e)
{
// Go through all controls and empty each TextBox, RichTextBox, RadioButton or ComboBox.
int parentControlsCount = Controls.Count - 1;
for (int i = parentControlsCount; i >= 0; i--)
{
if (Controls[i].HasChildren == true)
{
int childrenControlsCount = Controls[i].Controls.Count - 1;
for (int j = childrenControlsCount; j >= 0; j--)
{
var controlType = Controls[i].Controls[j].GetType().ToString();
switch (controlType)
{
case "System.Windows.Forms.TextBox":
case "System.Windows.Forms.RichTextBox":
Controls[i].Controls[j].Text = null;
break;
case "System.Windows.Forms.RadioButton":
// Restore both properties to default value
((RadioButton)Controls[i].Controls[j]).Checked = false;
if (j == 1)
((RadioButton)Controls[i].Controls[j]).TabStop = true;
else if (j == 2)
((RadioButton)Controls[i].Controls[j]).TabStop = false;
break;
case "System.Windows.Forms.ComboBox":
((ComboBox)Controls[i].Controls[j]).SelectedIndex = -1;
break;
}
}
}
}
}
What am I doing wrong?
I ended up applying this gross hack- a function on every second radiobutton's CheckedChange:
private void DisableTabStopOnCheckedChange(object sender, EventArgs e)
{
// Assume the following STR:
// 1. In any radiobutton panel, select any radiobutton (without ever invoking BackToMain function in the first post);
// 2. Invoke the BackToMain function;
// 3. In the same radiobutton panel as in step #1, click the second radiobutton.
// Normally, without this function, if the user will now cycle through the controls using the Tab key, both the first and second radiobuttons will be tabbable,
// and that's because in the BackToMain function we reset their Checked and TabStop properies, and that's something that should be handled automatically by the control itself.
// Doing it manually means that for the *first time* selecting the second radiobutton, the first one's TabStop state won't update, which means both radiobuttons
// will have the TabStop state set to true, causing both to be tabbable.
// This is a gross hack to fix this by disabling TabStop on the first radio button if the second one is checked and the first one's TabStop state
// is true (this should happen only after BackToMain has been invoked).
if (((RadioButton)sender).Checked)
{
var firstRadioButton = ((RadioButton)sender).Parent.Controls[1];
if (((RadioButton)firstRadioButton).TabStop == true)
{
((RadioButton)firstRadioButton).TabStop = false;
}
}
}
Not a pretty solution, I know. But it works.

C# Window From Autoselects Text

I have not dealt with WinForms for a long time.
Now I'm stuck with something trivial but cannot figure it out.
I have a Winform and when a Timer Tick happens I want to show a message in a new form message box:
frmMessage frmM = new frmMessage();
frmM.txtMessage.Text = ConfigurationSettings.AppSettings["Message"];
frmM.Show();
It works but the text in the textbox shows as selected(with a blue background).
I tried
txtMessage.SelectionLength = 0;
Did not help.
Also tried to set focus to a different control, did not help either.
for now, as a workaround, I will use a Label.
This is a consequence of the way TextBox Class is implemented. If a selection is not specifically set, all text will be selected when the control gets focus.
From TextBox.OnGotFocus:
Protected override void OnGotFocus(EventArgs e) {
base.OnGotFocus(e);
If (!selectionSet) {
// We get one shot at selecting when we first get focus. If we don't
// do it, we still want to act Like the selection was set.
selectionSet = true;
// If the user didn't provide a selection, force one in.
If (SelectionLength == 0 && Control.MouseButtons == MouseButtons.None) {
SelectAll();
}
}
Additionally due to the way the SelectionLength Property is implemented, setting that property to zero does not set the selectionSet` flag as it is already zero.
Instead, set the TextBox.SelectionStart Property immediately after setting the text as this will set that flag.
txtMessage.SelectionStart = 0;
However, your work-a-round of using a Label to display a message is much more appropriate than using an input control.
This is not the best answer but it works. You can try this
frmMessage frmM = new frmMessage();
frmM.txtMessage.Text = "";
frmM.txtMessage.AppendText(ConfigurationSettings.AppSettings["Message"]);
frmM.Show();

choosing to hide or show tooltips based on bool

so I figured I'm making just a stupid mistake here. In the first of what will be many controls, I need to either show a balloon tooltip when a bool is true or not show them when the bool is false. I know that ShowAlways is not what I need to modify and I've tried various solutions already. Does anyone spot the problem? The bool is set by a checked dropdown item in a Help Menu Strip Item.
It will open with the application with the correct display, but as soon as I check that option to show it, it always shows there after.
public void changeBalloonProperties(bool boolSet)
{
ToolTip helpDeskInfoButtonToolTip = new ToolTip();
if (boolSet)
{
helpDeskInfoButtonToolTip.ToolTipTitle = "HelpDesk Information Button";
helpDeskInfoButtonToolTip.UseFading = true;
helpDeskInfoButtonToolTip.UseAnimation = true;
helpDeskInfoButtonToolTip.IsBalloon = true;
helpDeskInfoButtonToolTip.ShowAlways = true;
helpDeskInfoButtonToolTip.AutoPopDelay = 5000;
helpDeskInfoButtonToolTip.InitialDelay = 1000;
helpDeskInfoButtonToolTip.ReshowDelay = 500;
helpDeskInfoButtonToolTip.SetToolTip(helpDeskButton, "Click to launch HelpDesk user info page in default browser.");
}
else
{
helpDeskInfoButtonToolTip.RemoveAll();
}
}
You are creating a new ToolTip instance each time the changeBalloonProperties is called so the code isn't removing the caption associated with the original ToolTip that was used with the helpDeskButton. Try moving the ToolTip declaration outside of your changeBalloonProperties method so the same ToolTip object is used with RemoveAll().
Also note you can use that same ToolTip object to add captions for multiple controls (as shown in the sample here) and it's probably better to set helpDeskInfoButtonToolTip.Active = false to disable them all at once instead of setting and removing the captions (and other properties) each time you toggle.

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

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;
}
}
}

Categories