Hide and show a cell of the TableLayoutPanel - c#

My tablelayout panel has one column and three rows. (one docked to Fill panel in each cell.)
Now I would like to be able to hide/show the rows . I want only one row to be visible at any time ( based on a user selection of some radio buttons) and I want to to get resized so it fills all the area of the TableLayoutPanel.
How can I do that? Any thoughts?

If rows in your TableLayoutPanel is autosized then hiding content panel will hide cell where panel placed too.

I would suggest setting the other rows heights to 0 is the easiest way:
Row one:
this.tableLayoutPanel1.RowStyles[1].Height = 0;

Try this
TableLayoutPanel1.ColumnStyles[1].SizeType = SizeType.Absolute;
TableLayoutPanel1.ColumnStyles[1].Width = 0;

So why did you use a TableLayoutPanel?
Just put three Panels on your form, fill in everyone the content of each row and set the Dock property of all three panels to Fill. Set two panels Visible = false and one to true.
If you like to see another panel, just make it visible and hide the other two (based on your radio button settings).

My scenario is similar. I needed a TableLayoutPanel with 4 rows each of which needed to be visible according to a checkbox selection. So instead of only showing one row at a time, I can show 1 - 4.
After designing the layout with 1 column and 4 rows, the controls were added and Dock set to Fill for each one.
Then in a single CheckedChanged event handler for the checkboxes, I coded as shown below. It's kind of a brute force method, but, Hey...it works!
private void checkBox_CheckedChanged(object sender, EventArgs e)
{
this.SuspendLayout();
int seldCount = checkBox1.Checked ? 1 : 0;
seldCount += checkBox2.Checked ? 1 : 0;
seldCount += checkBox3.Checked ? 1 : 0;
seldCount += checkBox4.Checked ? 1 : 0;
float pcnt = 0;
if (seldCount == 1)
pcnt = 1;
if (seldCount == 2)
pcnt = 0.5f;
if (seldCount == 3)
pcnt = 0.33f;
if (seldCount == 4)
pcnt = 0.25f;
int newHeight = (int)(tableLayoutPanel1.Height * pcnt);
if (checkBox1.Checked)
{
tableLayoutPanel1.RowStyles[0].SizeType = SizeType.Percent;
tableLayoutPanel1.RowStyles[0].Height = newHeight;
}
else
{
tableLayoutPanel1.RowStyles[0].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[0].Height = 0;
}
if (checkBox2.Checked)
{
tableLayoutPanel1.RowStyles[1].SizeType = SizeType.Percent;
tableLayoutPanel1.RowStyles[1].Height = newHeight;
}
else
{
tableLayoutPanel1.RowStyles[1].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[1].Height = 0;
}
if (checkBox3.Checked)
{
tableLayoutPanel1.RowStyles[2].SizeType = SizeType.Percent;
tableLayoutPanel1.RowStyles[2].Height = newHeight;
}
else
{
tableLayoutPanel1.RowStyles[2].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[2].Height = 0;
}
if (checkBox4.Checked)
{
tableLayoutPanel1.RowStyles[3].SizeType = SizeType.Percent;
tableLayoutPanel1.RowStyles[3].Height = newHeight;
}
else
{
tableLayoutPanel1.RowStyles[3].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[3].Height = 0;
}
this.ResumeLayout();
}

To hide row try this!!
tableLayoutPanel1.RowStyles[1].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[1].Height = 0;

I had similar task to do and my solution is following:
Add a TableLayoutPanel to your form (or any container).
Set TableLayoutPanel's columns and rows count to 1 and size to 100%.
Set Dock to Fill.
Set GrowStyle to fixedSize.
Set AutoSize to true.
Then programmatically add all of three forms/controls, one of which you have to show depending on radio button choice. Be sure that only one of them is visible. That could be done with initial FirstControl.Show(); and then on each RadioButton event hide the current one and show another. you may "remember" in local variable (say: "currentlyVisibleControl" the reference which is currently visible)
note: if you will .Show() more than one at time. then TableLayoutPanel wil fire the exception that it is full and can't add any more item.
P.S. In My own example I have TableLayoutPanel in MDI window and three forms which substitute each other on button clicks on them so I think copying my source code will complicate the "verbal" example.
P.P.S. From my experience Visual Studio does some weird things in design mode sometimes. I had to remove and re-add the TableLayoutPanel to set properties correctly and get the results both in designer and in runtime. So if either autosize or absolute/percent values are not depicted on designer screen it may be designers problem rather that yours. JUST DELETE IT AND RETRY.

I tried fooling around with the Height and SizeType properties, but it was giving me odd results. For example, the Labels on the target row were being hidden, but the TextBoxes were not.
Here is an extension class that I came up with using #arbiter's suggestion of hiding the children Controls of the row.
// these methods only works on rows that are set to AutoSize
public static class TableLayoutPanelExtensions
{
public static void HideRows(this TableLayoutPanel panel, params int[] rowNumbers)
{
foreach (Control c in panel.Controls)
{
if (rowNumbers.Contains(panel.GetRow(c)))
c.Visible = false;
}
}
public static void ShowRows(this TableLayoutPanel panel, params int[] rowNumbers)
{
foreach (Control c in panel.Controls)
{
if (rowNumbers.Contains(panel.GetRow(c)))
c.Visible = true;
}
}
}

Related

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.

TableLayoutPanel displays vertical scroll

I have TableLayoutPanel for dynamic creation of controls with AutoScroll = true. It's work fine when I add new controls. But when I remove and all controls are visible, vertical scroll is visible.
Some screenshots here:
Expected/correct scroll visibility:
Incorrect visibility:
Any ideas?
Update:
Here is some code
tableLayoutPanel1.SuspendLayout();
tableLayoutPanel1.RowCount = 0;
tableLayoutPanel1.RowStyles.Clear();
tableLayoutPanel1.AutoScroll = true;
tableLayoutPanel1.Padding = new Padding(0, 0, SystemInformation.VerticalScrollBarWidth, 0);
foreach (var item in objects)
{
tableLayoutPanel1.RowCount++;
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tableLayoutPanel1.Controls.Add(CreateNewItem(item));
}
tableLayoutPanel1.RowCount++;
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tableLayoutPanel1.Controls.Add(CreateAddButton());
tableLayoutPanel1.ResumeLayout();
and code for deleting
tableLayoutPanel1.SuspendLayout();
tableLayoutPanel1.Controls.Remove(item);
tableLayoutPanel1.RowStyles.RemoveAt(0);
tableLayoutPanel1.RowCount--;
tableLayoutPanel1.ResumeLayout();
AutoSize is true, AutoSizeMode is GrowAndShrink
The problem concerns TableLayoutPanel scrolling.
You have to use a Panel for scrolling instead of TableLayoutPanel.
Here is an example to solve this problem (for vertical scrolling) :
Set your TableLayoutPanel properties as follow :
Dock = DockStyle.Top
AutoSize = true
AutoSizeMode = AutoSizeMode.GrowAndShrink
AutoScroll = false.
Put your TableLayoutPanel into a Panel with properties :
Dock = DockStyle.Fill
AutoScroll = true
AutoSize = false.
when you remove the dynamic controls, you need to remove the extra rows that was inserted during the addition and re-size the table layout panel height to smaller than scroll container height.
During the addition the table layout panel height would have increased, which handled by the scroll container; but when you remove the controls, the table layout panel height doesn't reduce it's height to fit the scroll container.
One way to do this is to give fixed height to the rows and set the table layout panel seize set to "Auto".
One of the easiest and funniest solution is to just disable and enable tableLayoutPanel1.AutoScroll
In your Deleting procedure code add at the end these codes :
tableLayoutPanel1.AutoScroll = False
tableLayoutPanel1.AutoScroll = True
I inserted tableLayoutPanel to XtraScrollableControl(Devexpress control). tableLayoutPanel.Dock set to Top and XtraScrollableControl.Dock to Fill. This solution did not solves this problem, but I got behavior that I need.
I counted the number of rows in my TableLayoutPanel to see how many would fit. Below the amount that fit I set AutoScroll = false for the add and delete methods. The scroll will appear for large sets and disappear on small sets.
if (tableLayoutPanel.RowCount < 15)
{
panel1.AutoScroll = false;
}
else
{
panel1.AutoScroll = true;
}
I had a TableLayoutPanel on a UserControl, docked in Fill mode, with all rows on the TableLayoutPanel set to AutoSize. This UserControl would then dynamically get put on a panel, again in Fill mode, to show it to the user when needed. I put the UserControl on AutoScroll, but that alone did not solve it.
In the end, I solved it by going over all controls in the TableLayoutPanel, storing the extremities, and baking that into a Size to put in my UserControl's AutoScrollMinSize:
private void AdjustPanelSize(ScrollableControl panel, TableLayoutPanel tableLayoutPanel)
{
int maxX = 0;
int maxY = 0;
foreach (Control c in tableLayoutPanel.Controls)
{
maxX = Math.Max(maxX, c.Location.X + c.Width);
maxY = Math.Max(maxY, c.Location.Y + c.Height);
}
panel.AutoScrollMinSize = new Size(maxX, maxY);
}
This worked, and it also has the advantage that it can be called if there would ever be controls dynamically added or removed from the TableLayoutPanel.

Centering text on a button in a WinForms application

I have a simple Windows Forms application with a tabControl. I have 3 panels on the tabControl, each having 5 buttons. The text on first set of buttons is hard-coded, but the next set populates when you click one from the first group, and then the same thing happens again for the last group when you click one of the buttons from the second group. In the [Design] view I manually set the TextAlign property of each button to MiddleCenter. However, when I run the application the text on the middle set of buttons is never centered. It is always TopLeft aligned. I've tried changing the font size and even explicitly setting the TextAlign property every time I set button text programmatically, as follows:
private void setButtons(List<string> labels, Button[] buttons)
{
for (int i = 0; i < buttons.Count(); i++)
{
if (i < labels.Count)
{
buttons[i].Text = labels.ElementAt(i);
buttons[i].TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
buttons[i].Enabled = true;
}
else
{
buttons[i].Text = "";
buttons[i].Enabled = false;
}
}
}
This image shows the result:
Does anyone have any ideas for what I'm missing?
Trim text which you are assign to button. Also you can refer label by index, without calling ElementAt
private void setButtons(List<string> labels, Button[] buttons)
{
for (int i = 0; i < buttons.Count(); i++)
{
Button button = buttons[i];
if (i < labels.Count)
{
button.Text = labels[i].Trim(); // trim text here
// button.TextAlign = ContentAlignment.MiddleCenter;
button.Enabled = true;
}
else
{
button.Text = "";
button.Enabled = false;
}
}
}
You can set the UseCompatibleTextRendering property to true, then use the TextAlign property.
The strings in the SQL table that were assigned to the middle column were actually nchar(50), not nvarchar(50), which explains the problem. I added .Trim() to the Text assignment and it looks great now.
You can use the TextAlign from the Properties Menu and set it to MiddleCenter ...
If this does not work then the text you have for your button is larger than the actual button itself... to which you should either rescale your Font Size to a lower base size or a percent size of the actual button by using
btnFunction.Font = new Font(btnFunction.Font.Name, Convert.ToInt32(btnFunction.Height * 0.3333333333333333));
This would cause the button's font to be one third of the height of the button....

.NET TableLayoutPanel

Basically I have a tablelayoutpanel , it its currently being used for POS System.
When I call SetColumnSpan on a button control , the tablelayoutpanel adds an extra row, and messes up my screen layout.
Has anybody come across this before ?
Each free space in the panel is assigned a blank button, when the screen is in edit mode , they can add/edit and delete buttons.
Below is the code to apply button changes.
Edit cleaned up code a bit
void button_MouseUp(object sender, EventArgs e)
{
try
{
TableLayoutPanelCellPosition pos = tableLayoutPanel1.GetCellPosition((Control) sender);
POSButton productButton = GetProductButton(sender);
tableLayoutPanel1.SuspendLayout();
if (productButton == null)
{
DeleteButton(sender, pos);
return;
}
productButton.Dock = DockStyle.Fill;
EditModeHookButton(productButton);
tableLayoutPanel1.Controls.Remove((Control) sender);
tableLayoutPanel1.Controls.Add(productButton, pos.Column, pos.Row);
if (productButton.TableRowSpan > 0)
tableLayoutPanel1.SetRowSpan(productButton, productButton.TableRowSpan);
if (productButton.TableColumnSpan > 0)
tableLayoutPanel1.SetColumnSpan(productButton, productButton.TableColumnSpan);
buttonManager.Save(tableLayoutPanel1);
tableLayoutPanel1.ResumeLayout();
}
catch(OperationCanceledException)
{
}
}
Here is the button Manager function that serializes the button layout.
public void Save(ScreenTabkeLayoutPanel panel)
{
List<ButtonSaveInfo> buttons = new List<ButtonSaveInfo>();
foreach (Control control in panel.Controls)
{
TableLayoutPanelCellPosition pos = panel.GetCellPosition(control);
ButtonSaveInfo info;
if (control is POSButton)
info = ((POSButton)control).ConvertToButtonInfo(pos);
else
info = control.ConvertToButtonInfo(pos);
buttons.Add(info);
}
AppDataSerializer.SaveBinary(buttons,buttonPath);
}
Here is the code that loads/populates the screen with the buttons
private void LoadButtonsFromFile(ScreenTabkeLayoutPanel panel)
{
List<ButtonSaveInfo> buttons = AppDataSerializer.LoadBinary<List<ButtonSaveInfo>>(buttonPath);
panel.SuspendLayout();
foreach (ButtonSaveInfo info in buttons)
{
switch (info.ButtonType)
{
case (int) ButtonType.PRODUCT:
POSButton productButton = info.ConvertToPosButton();
wireButtonEvents(productButton);
panel.Controls.Add(productButton, info.ColumnIndex, info.RowIndex);
if (productButton.TableRowSpan > 0)
panel.SetRowSpan(productButton, productButton.TableRowSpan);
if (productButton.TableColumnSpan > 0)
panel.SetColumnSpan(productButton, productButton.TableColumnSpan);
break;
default:
Control control = BuildBlankButton();
wireButtonEvents(control);
panel.Controls.Add(control, info.ColumnIndex, info.RowIndex);
break;
}
}
FillEmptySpacesWillBlankButtons(panel);
panel.ResumeLayout();
}
Thanks in advanced.
Make sure you don't have a control in a spanned cell.
If you set column span to 2 on cell 0,0 and put a control in 1,0 this will confuse the layout engine. Since you specified in your question that you added blank buttons to all cells, this might be what is happening here.
Make sure you remove any control from a cell you are planning to span over.
Also, there are some situation in which the table layout just gives up, especially if you span cells with auto sizing.
Are you setting the RowSpan to a value greater than the number of rows in the table? This might cause an extra row to be rendered. Other than that you will need to provide more information/code for us to figure it out :)

WinForms ComboBox problem

In a Windows Form Application, I have a ComboBox1 which gets initialized in InitializeComponent() function. I add the values into it in a different function.
snippet:
public form1()
{
InitializeComponent();
addDataToDropDowns();
}
The problem I have is that, the rows loaded into the ComboBox1 have many characters(/length) and are not to be seen completely width wise.
Is it possible to have a horizontal scrollbar built into the ComboBox1 so that I can see the hidden part of the row too...??
Any ideas/inputs will be appreciated!
Thanks,
Ivar
There is actually a DropDownWidth property that controls how wide the drop down area is. This way you can have a narrow control that doesn't take up too much space on the form, but a larger drop down area that could extend over as much of the screen as you want.
http://www.codeproject.com/KB/combobox/ComboBoxAutoWidth.aspx
That has code sample that shows how to capture the event and widen the box.
OR, you could have it as a seperate function you manually call, like this:
http://weblogs.asp.net/eporter/archive/2004/09/27/234773.aspx
Combining the links in Caladain's answer, here is the code. It works for both strings and data bound objects. The method cbSample_DropDown() is linked to the DropDown event of the ComboBox.
private void AdjustWidthComboBox(ComboBox comboBox)
{
int width = comboBox.DropDownWidth;
using (Graphics g = comboBox.CreateGraphics())
{
Font font = comboBox.Font;
int vertScrollBarWidth =
(comboBox.Items.Count > comboBox.MaxDropDownItems)
? SystemInformation.VerticalScrollBarWidth : 0;
foreach (object item in comboBox.Items)
{
string valueToMeasure = comboBox.GetItemText(item);
int newWidth = (int)g.MeasureString(valueToMeasure, font).Width + vertScrollBarWidth;
if (width < newWidth)
{
width = newWidth;
}
}
}
comboBox.DropDownWidth = width;
}
private void cbSample_DropDown(object sender, EventArgs e)
{
AdjustWidthComboBox(sender as ComboBox);
}

Categories