winforms panel hide and show - c#

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

Related

How do I change the properties of a control within a list dynamically (C# Winform)?

I have a list of checkbox controls
public List<CheckBox> _checkBoxes = new List<CheckBox>();
I add a certain amount during runtime using a switch case with the properties such as location and visibility.
This all works fine, but if I want the checkboxes to disappear or become checked at some point the GUI doesn't get updated, the list is updated with the new data but just not visually.
* I ended up doing this*
I created a bunch of checkboxes on the winfrom.Created a method which you can choose which ones are visible. Then created a method to fill a list of type checkbox. Then you can search the panel for control types, once you found the control, it can be manipulated. This isn't elegant and probably isn't the best way, but i am new to custom controls and winforms.
//search panel for checkboxes
foreach (Control c in panel1.Controls)
{
if (c is CustomControls.RoundedCheckBox)
{
CustomControls.RoundedCheckBox checkBox = c as CustomControls.RoundedCheckBox;
//if it is checked add to list
if(checkBox.Checked)
{
_checkBoxes.Add(checkBox);
}
}
}
If you have something like this:
_checkBoxes.Add(new CheckBox());
_checkBoxes[0].Parent = this;
then you should be able to manipulate your checkboxes from the list:
_checkBoxes[0].Checked = false;
But, the problem may occur if you do it in some kind of loop and want to see the results immediately.
In Windows there is something called message loop. Application simply works like that (pseudocode: TL;DR)
while(true)
{
message = GetFirstMessage();
if(message != null)
{
if(message.Id == CloseApplication)
break;
DispatchMessage(message);
RemoveFirstMessage();
}
}
So, application takes message from queue, then process it. A message is everything - button click, mouse move, paint... Everything.
So when a message is dispatched it looks for the control that should receive this message and then it does some work.
So, for example if you have something like that:
foreach(var ch in _checkBoxes)
{
ch.Checked = false;
DoSomeWorkThatTakesTime();
ch.Checked = true;
}
You won't see the change, because you are "trapped" in DispatchMessage. When you set Checked, you really sending a message. But this message cannot be Dispatched right now, because you are inside the foreach loop.
So the only thing you can do here is to tell your application - now, please DO READ message queue. In WinForms it's called "DoEvents", so this will do the work:
foreach(var ch in _checkBoxes)
{
ch.Checked = false;
Application.DoEvents();
DoSomeWorkThatTakesTime();
ch.Checked = true;
Application.DoEvents();
}

Getting list of checked radiobuttons in .net

TLDR; Looking for a method to retrieve all radiobuttons by means of something like this... (psudo)
List<RadioButton> btn = new List<RadioButton>;
btn = stackPanel.getAllRadioButtons()
I am currently building a little quiz application in C#. I have functions that add the required GUI elements to a groupbox. I would like to know if there is any way that I can loop through the created elements (for instance radio buttons) to see which are checked.
Here is one of the functions along with how they are added to the window.
private void tfQ(string questionBody)
{
StackPanel questionPanel = new StackPanel{Orientation = Orientation.Vertical};
questionPanel.Children.Add(new Label { Content = questionBody });
GroupBox group = new GroupBox();
RadioButton trueRadio = new RadioButton();
trueRadio.Content = "True";
RadioButton falseRadio = new RadioButton();
falseRadio.Content = "False";
questionPanel.Children.Add(trueRadio);
questionPanel.Children.Add(falseRadio);
group.Content = questionPanel;
mainStack.Children.Add(group);
}
Constructor:
public quiz()
{
tfQ("This is a true/false question");
Window w = new Window();
w.Content = mainStack;
w.Show();
}
I have found many ways to do it in the C# scripting format
(using the Control function ...)
var checkedButton = container.Controls.OfType<RadioButton>()
.FirstOrDefault(r => r.Checked);
but I have not yet found a "programmatic" way of doing it. I have considered changing the type of void tfQ to a StackPanel, but that only helps me to easier loop through the stack panels, althought that only partly solves me problem - allows me to easier loop through the StackPanels but I still don't know how to get the RadioButtons on a panel.
P.S I am very new to C# - with experience in Java/C/C++/Python
I made use of the following code to cast the content into a new groupbox and stackpanel, respectively.
if (child is GroupBox)
{
if ((child as GroupBox).Content is StackPanel)
{
StackPanel d = (StackPanel)((GroupBox)child).Content;
}
}
This allowed me to cast the content into a local copy of a control. It may not be the best way - I'm sure it is very inefficient to be honest. Solved the problem.

"Pin" control over items in ToolstripMenu

First, a little overview of how my current UI looks like:
Note that other than the stuff in the ToolStripControlHost, everything else is standard WinForms.
In short, I want to have something similar to the ToolStripControlHost but I need it "pinned" to the bottom of the menu, mostly so that when there is a lot of items, it is not scrolled like the rest of the menu items.
After some searching around I came to the conclusion that maybe customizing painting might be the solution, don't know if this is the case though.
Here's some sample code, but I'm not sure how useful it is:
public ToolStripDropDownButtonContainer(ToolStripDropDownButton button)
{
this.UIControl = button.GetCurrentParent();
this.Button = button;
if (this.Button.Tag == null)
{
this.Button.Tag = true;
this.Button.DropDownDirection = ToolStripDropDownDirection.AboveLeft;
ToolStripDropDownMenu menu = (ToolStripDropDownMenu)this.Button.DropDown;
menu.SuspendLayout();
try
{
menu.BackColor = Color.White;
menu.ShowImageMargin = false;
menu.ShowCheckMargin = false;
menu.AutoSize = true;
menu.Margin = Padding.Empty;
menu.Padding = Padding.Empty;
menu.GripMargin = Padding.Empty;
menu.GripStyle = ToolStripGripStyle.Hidden;
menu.MinimumSize = new Size(310, 0);
menu.MaximumSize = menu.MinimumSize;
// TODO pin panel (or some control) to the bottom-side of the menu
}
finally
{
menu.ResumeLayout();
}
}
}
My solution to this problem is to completely avoid using the normal menu control containment system and instead have the menu show one FlowLayoutPanel which instead contains my menu items.
This involved having to add some various trickeries to get the panel to behave nicely with the UI. The added advantage of this approach is more flexibility and control over the system.
On the downside, I noticed a degrade in performance when I have a tonne of sub-items, but I'll investigate this separately.

A Collection *OF* DataGridViews? Manipulating all DataGridViews at Once

I'm continuing to work on a few VSTO's and I was wondering if there was a way to refer to all the datagridviews in a class at once. I can't seem to figure out what the container should be, and I can't seem to add them to arrays/other containers?
The psudo code for what I'm tring to do would be something like:
For Each datagridview in Globals.MyUserControl
'change some datagridview property ie:
datagridview1.ReadOnly = True
Next
I would be hapy in C# or VB.net, or really any explanation of if this can or can't be done. At the moment I'm manually setting it for all the different datagridviews, as that number grows, I would like a way to hit them all at once.
Still trying to work on the solutions below, another way I've tried this that doesn't work:
For Each ctl In Me.Controls
If TypeOf ctl Is DataGridView Then
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
End If
Next
But I don't know why that doesn't work.
You can use a foreach loop:
foreach (DataGridView ctrl in Globals.MyUserControl.Controls)
ctrl.ReadOnly = true;
If you're expecting any non-datagridview controls in the controls collection that you don't want to set to read-only, then instead of a single statement, you can check the type of ctrl.
foreach (Control ctrl in Globals.MyUserControl.Controls)
if(ctrl is DataGridView) ctrl.ReadOnly = true;
Using LINQ, you can do this:
Globals.MyUserControl.Controls.Cast<Control>().ToList().ForEach((ctrl) => { if (ctrl is DataGridView) ((DataGridView)ctrl).ReadOnly = true; });
Or if all your controls are known to be DataGridView controls, then you can do this:
Globals.MyUserControl.Controls.Cast<DataGridView>().ToList().ForEach(ctrl => ctrl.ReadOnly = true);
To find child controls inside other controls, define a recursive method and call that:
private static void FindControlsRecursively(Control.ControlCollection collection)
{
foreach (Control ctrl in collection)
{
if (ctrl is DataGridView)
((Label)ctrl).ReadOnly = true;
else if (ctrl.Controls.Count > 0)
FindControlsRecursively(ctrl.Controls);
}
}
Then call it with the controls of your user control from your user control:
FindControlsRecursively(this.Controls);
Something like this should work:
For Each ctl In Me.Controls.OfType(Of DataGridView)()
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
Next
Or C#
foreach (DataGridView ctrl in this.Controls.OfType<DataGridView>())
{
ctrl.ReadOnly = true;
ctrl.AllowUserToDeleteRows = false;
}
This loops through only the DataGridViews in the form.
Additionally you can add them to a List(Of DataGridView), if necessary
Another option is to declare a class that inherits DataGridView, set the properties you want, and declare new datagridviews of this type to add to your form(s).
I'm not sure if I should put this in the answer, or if Tombola wants to move it to his, but here was my solution. The problem was that all my datagridviews were nested on tab pages in a tab control. To get it to work I used:
For Each tabpg In TabControl1.TabPages()
For Each ctl In tabpg.Controls 'note, was not able to use 'OfType here, but had to drop to the If statement or I ran into runtime errors.
If TypeOf ctl Is DataGridView Then
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
End If
Next
Next

Loop through controls in TabControl

I am looking for a way to loop through controls on a particular tab of a tabcontrol. For example, I have a tabcontrol with the following tabs:
Cars,
Pets,
Admin
On each of these tabs are several controls to display/edit/save data, etc. On the "Save" button, I would like to loop through the controls for that particular tab to check whether all required fields have been filled in.
So, if I am on the Cars tab and click "Save," I want to loop ONLY through the controls on the Cars tab and NOT the Pets or Admin tabs.
How can achieve this result?
As for looping through a TabControl's controls, you need to use the Controls property.
Here's an MSDN article on the TabControl.
Example:
TabPage page = aTabControl.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
//do stuff
}
I feel it's important to note that, in general, you should take a more structured approach to your application. E.g., instead of having all the controls on three tab pages, include exactly one UserControl on each tabpage. A CarUserControl, PetUserControl, and AdminUserControl e.g. Then each user control knows how to create the proper respective data structure so you don't have to manually munge it all together at the same level of abstraction using inter-tab loops and whatnot.
Such a separation of concerns will make it much easier to reason about your program and is good practice for writing maintainable code for your future career.
Example where I wanted to get the DataGridView in a particular tab for an application I wrote.
TabPage pg = tabControl1.SelectedTab;
// Get all the controls here
Control.ControlCollection col = pg.Controls;
// should have only one dgv
foreach (Control myControl in col)
{
if (myControl.ToString() == "System.Windows.Forms.DataGridView")
{
DataGridView tempdgv = (DataGridView)myControl;
tempdgv.SelectAll();
}
}
The Controls property is the way to go...
foreach(Control c in currentTab.Controls)
{
if(c is TextBox)
// check for text change
if(c is CheckBox)
//check for check change
etc...
}
TabControl has a SelectedTab property, so you'd do something like this:
foreach(Control c in tabControl.SelectedTab.Controls)
{
//do checks
}
foreach (Control c in this.tabControl1.SelectedTab.Controls)
{
// Do something
}
I had the need to disable or enable controls of a tab as well. I had to go a bit more generic though. Hope it helps people and I didn't make a mistake
private void toggleControls(Control control, bool state)
{
foreach (Control c in control.Controls)
{
c.Enabled = state;
if (c is Control)
{
toggleControls(c, state);
}
}
}

Categories