Add vertical scroll bar to panel - c#

I am trying to make a Panel scrollable, but only vertically (so AutoScroll won't work because the child controls go past the left edge and must).
So how is this done?

Try this instead for 'only' scrolling vertical.
(auto scroll needs to be false before it will accept changes)
mypanel.AutoScroll = false;
mypanel.HorizontalScroll.Enabled = false;
mypanel.HorizontalScroll.Visible = false;
mypanel.HorizontalScroll.Maximum = 0;
mypanel.AutoScroll = true;

Assuming you're using winforms, default panel components does not offer you a way to disable the horizontal scrolling components. A workaround of this is to disable the auto scrolling and add a scrollbar yourself:
ScrollBar vScrollBar1 = new VScrollBar();
vScrollBar1.Dock = DockStyle.Right;
vScrollBar1.Scroll += (sender, e) => { panel1.VerticalScroll.Value = vScrollBar1.Value; };
panel1.Controls.Add(vScrollBar1);
Detailed discussion here.

Panel has an AutoScroll property. Just set that property to True and the panel will automatically add a scroll bar when needed.

AutoScroll is really the solution!
You just have to set AutoScrollMargin to 0, 1000 or something like this, then use it to scroll down and add buttons and items there!

Below is the code that implements custom vertical scrollbar. The important detail here is to know when scrollbar is needed by calculating how much space is consumed by the controls that you add to the panel.
panelUserInput.SuspendLayout();
panelUserInput.Controls.Clear();
panelUserInput.AutoScroll = false;
panelUserInput.VerticalScroll.Visible = false;
// here you'd be adding controls
int x = 20, y = 20, height = 0;
for (int inx = 0; inx < numControls; inx++ )
{
// this example uses textbox control
TextBox txt = new TextBox();
txt.Location = new System.Drawing.Point(x, y);
// add whatever details you need for this control
// before adding it to the panel
panelUserInput.Controls.Add(txt);
height = y + txt.Height;
y += 25;
}
if (height > panelUserInput.Height)
{
VScrollBar bar = new VScrollBar();
bar.Dock = DockStyle.Right;
bar.Scroll += (sender, e) => { panelUserInput.VerticalScroll.Value = bar.Value; };
bar.Top = 0;
bar.Left = panelUserInput.Width - bar.Width;
bar.Height = panelUserInput.Height;
bar.Visible = true;
panelUserInput.Controls.Add(bar);
}
panelUserInput.ResumeLayout();
// then update the form
this.PerformLayout();

3 steps:
1- just set AutoScroll property to true
2- in Form load()add the following:
my Panel Vertical Scroll Maximum = 10000
3- after my Panel controls Add(item) add the following:
Invalidate();
Done!

Add to your panel's style code something like this:
<asp:Panel ID="myPanel" runat="Server" CssClass="myPanelCSS" style="overflow-y:auto; overflow-x:hidden"></asp:Panel>

Related

Location of panel not correct when using different anchor style

this is for Winforms / C# btw.
So I am making my own ComboBox, just playing around.
The item List is basically a Panel, which is located under or above the ComboBox control, depending on its height and location.
This works as intended, as long as I use AnchorStyle Top / Left for my usercontrol.
As soon as I switch to Top / Right Style, the location of my listpanel breaks. Okay, makes sense, the location point of my usercontrol changes with the different anchorstyle, so how about I adjust my panel to a new location using my usercontrol.LocationChanged event? Doesen't work. Maybe because of the order of the events? Or does the origin of locations change depending on the given AnchorStyles?
Anyway, I am a bit lost. Unfortunately I couldn't find a similar case here, therefore this question.
Here's my DropDownPanel (effectively my "list"):
private Panel DropDownPanel()
{
Panel DropDownPanel = new Panel() {
Width = this.Width,
Height = 200,
BackColor = _skin.TextBoxBackColor,
BorderStyle = BorderStyle.FixedSingle,
ForeColor = _skin.TextBoxForeColor,
Visible = false,
AutoScroll = false
};
DropDownPanel.HorizontalScroll.Enabled = false;
DropDownPanel.HorizontalScroll.Visible = false;
DropDownPanel.HorizontalScroll.Maximum = 0;
DropDownPanel.AutoScroll = true;
DropDownPanel.Leave += DropDownPanel_Leave;
return DropDownPanel;
}
Here's my userControl Load Event:
private void GbComboBox_Load(object sender, EventArgs e)
{
_dropDownPanel = DropDownPanel();
this.Parent.Parent.Controls.Add(_dropDownPanel);
RelocateDropDownPanel();
_dropDownPanel.BringToFront();
ApplyItems(_items);
}
And my calculation for the location:
private void RelocateDropDownPanel()
{
if (_dropDownPanel != null)
{
int initLocY = this.Parent.Location.Y + this.Location.Y + this.Height + _dropDownPanel.Height;
int fullHeight = this.FindForm().Height;
Point p = new Point();
if (initLocY < fullHeight)
{
p = new Point(this.Parent.Location.X + this.Location.X, initLocY - _dropDownPanel.Height);
}
else
{
p = new Point(this.Parent.Location.X + this.Location.X, initLocY - _dropDownPanel.Height - this.Height);
}
_dropDownPanel.Location = p;
}
}
So I figured out a "solution".
I simply inherited the Anchor of my Panel from the Usercontrol it was associated to.
But I wanna mention, that still, this whole thing is kind of a crappy way of dealing with custom combobox desings. As #Jimi stated in his first comment, the correct way of doing this would be to ownerdraw the combobox. I haven't done this because of specific reasons, which didn't let me do this.
A little example on how this could be made:
Change ComboBox Border Color in Windows Forms

Always Scroll to bottom in vertical scroll bar

I have a flowlayoutpanel in my winform in which the images are added dynamically. I want the vertical scroll bar to always be at the bottom showing the last image added. How can i do that?
I have
AutoScroll = true
FLow Direction = Top Down
Wrap Content = False
Scrollable container controls, like FlowLayoutPanel, automatically keep the control with the focus in view. But PictureBox is special, it cannot receive the focus. So you have to help by explicitly asking the FLP to make the added control visible, use its ScrollControlIntoView() method. Like this:
var pic = new PictureBox();
//...
flowLayoutPanel1.Controls.Add(pic);
flowLayoutPanel1.ScrollControlIntoView(pic);
With the strong advantage that this works for any layout setting you applied to the FLP. You can also tinker with the AutoScrollPosition property, but it is harder to get that right:
flowLayoutPanel1.AutoScrollPosition = new Point(
pic.Right - flowLayoutPanel1.AutoScrollPosition.X,
pic.Bottom - flowLayoutPanel1.AutoScrollPosition.Y);
Try this:
scrollBar.Value=scrollBar.Maximum;
here scrollBar is your ScrollBar control in winform.
For more detail, check this.
Here is a way to force the last control into view.
flowLayoutPanel.ScrollControlIntoView(Control_To_Add); // Control_To_Add is the control we want to scroll to
Button TempButton = new Button();
TempButton.Width = _Panel.ClientRectangle.Width - 6; // Make the last control in the _Panel
flowLayoutPanel.Controls.Add(TempButton); // We add this TempButton so we can scroll to the bottom of the _Panel.Controls
flowLayoutPanel.ScrollControlIntoView(b); // We scroll to TempButton at the bottom of the _Panel.Controls
flowLayoutPanel.Controls.Remove(b); // We remove TempButton
b.Dispose(); // clean up
Forcing A FlowLayoutPanel to scroll to and display all of a control.
Code Correction:
flowLayoutPanel.ScrollControlIntoView(Control_To_Add); // Control_To_Add is the control we want to scroll to
Button TempButton = new Button();
TempButton.Width = _Panel.ClientRectangle.Width - 6; // Make the last control in the _Panel
flowLayoutPanel.Controls.Add(TempButton); // We add this TempButton so we can scroll to the bottom of the _Panel.Controls
flowLayoutPanel.ScrollControlIntoView(TempButton); // We scroll to TempButton at the bottom of the _Panel.Controls
flowLayoutPanel.Controls.Remove(TempButton); // We remove TempButton
b.Dispose(); // clean up
This is the correct way:
MyControl uct = new MyControl();
uct.Parent = flowLayoutPanel;
this.ActiveControl = uct;
if (flowLayoutPanel.VerticalScroll.Visible)
{
flowLayoutPanel.ScrollControlIntoView(uct);
}

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.

Groupbox with a flowlayout panel inside and autosize = true shrinks like it is empty

I have a groupbox that holds a flowlayout panel and the flowlayout panel holds a bunch of controls. I set the flowlayout panel to dock with the parent. Since I don't know how many controls will be in the panel, I set the group box autosize to true and autosizemode to grow and shrink. When I do this the groupbox shrinks as if it is empty. I need the caption so I can't remove the groupbox. Anyone know why this is happening?
There's nothing that stops the FlowLayoutPanel from shrinking to nothing. You'll at least have to set its AutoSize property to True as well.
I was trying to do the same thing today. Below is the solution i came up with, which is to dock the FlowLayoutPanel inside of the GroupBox and then use the Resize and ControlAdded events of the FlowLayoutPanel to trigger resizing the parent GroupBox.
The resize handler finds the bottom of the last controls in the FlowLayoutPanel, and resizes the GroupBox with enough space to hold the bottom-most control(s) in the FlowLayoutPanel.
I tried using the AutoSize=true on the FlowLayoutPanel and the GroupPanel. But unfortunately this allows the FlowLayoutPanel to grow horizontally.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
int numGroupBoxes = 4;
for (int groupBoxIndex=0; groupBoxIndex<numGroupBoxes; groupBoxIndex++ )
{
GroupBox groupBox = new GroupBox();
groupBox.Text = "Group " + groupBoxIndex;
groupBox.Size = new Size(this.Width, 0);
groupBox.Dock = DockStyle.Top;
this.Controls.Add(groupBox);
FlowLayoutPanel groupBoxFlowLayout = new FlowLayoutPanel();
groupBoxFlowLayout.Dock = DockStyle.Fill;
groupBox.Controls.Add(groupBoxFlowLayout);
int extraSpace = 25; // the difference in height between the groupbox and the contents inside of it
MethodInvoker resizeGroupBox = (() =>
{
int numControls = groupBoxFlowLayout.Controls.Count;
if ( numControls > 0 )
{
Control lastControl = groupBoxFlowLayout.Controls[numControls - 1];
int bottom = lastControl.Bounds.Bottom;
groupBox.Size = new Size(groupBox.Width, bottom + extraSpace);
groupBoxFlowLayout.Size = new Size(groupBoxFlowLayout.Width, bottom);
}
});
groupBoxFlowLayout.Resize += ((s, e) => resizeGroupBox());
groupBoxFlowLayout.ControlAdded += ((s, e) => resizeGroupBox());
// Populate each flow panel with a different number of buttons
int numButtonsInGroupBox = 3 * (groupBoxIndex+1);
for (int buttonIndex = 0; buttonIndex < numButtonsInGroupBox; buttonIndex++)
{
Button button = new Button();
button.Margin = new Padding(0, 0, 0, 0);
string buttonText = buttonIndex.ToString();
button.Text = buttonText;
button.Size = new Size(0,0);
button.AutoSize = true;
groupBoxFlowLayout.Controls.Add(button);
}
}
}
}
Here are three screenshots of the control resized to various different widths:
You state "I don't know how many controls will be in the panel". Do you have any controls in the FlowLayoutPanel at design time? If you don't, this sounds like expected behavior. The Panel has nothing so its desired size is zero, so the GroupBox's desired size is zero.
If this is the case, then it should all hopefully size up when you actually add controls at runtime.
You set properties Anchor: Top, Bottom, Left, Right for groupBox.

Hide and show a cell of the TableLayoutPanel

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

Categories