I am working on a very simple table layout application for getting started with learning C#. I am doing everything programmatic ally ( not through design editor)
I am trying to add scrolling onto the application. It seems to work fine, but it does not seem to start at the top of the horizontal range by default. I tried adding things like Max/min size, autoscroll margins etc., but nothing seems to have the desired effect. I am sure there is something simple I am missing.
Here is my current code as it relates to the problem.
layout = new TableLayoutPanel();
layout.Height = 1075;
layout.Width = 704;
layout.Name = "masterLayout";
layout.Dock = DockStyle.Fill;
layout.AutoScroll = true;
int i = 0;
foreach (Race r in ELECTION_DATA.races.OrderBy(o => o.race_id)) {
layout.Controls.Add(new Label { AutoSize = true, Text =r.race_id, Name=r.race_id, Width=300}, i, 0 );
layout.Controls.Add(new TreeView { AutoSize = true, Text = r.race_id, Name = r.race_id, Height = 1000, Width = 300 }, i,1);
i += 1;
}
Controls.Add(layout);
Here is an image, The Label Control Is not visible because the scroll is offset to the beginning of the tree view.
How can I ensure the scroll always starts at the very top?
The ScrollLayoutPanel has a method called ScrollControlIntoView that will move a specific control inside the panel into view. If you just scroll your first control into the view after you are done filling your panels, then that should ensure that the top is visible. In other words:
// do your loop first...
foreach (...)
{
layout.Controls.Add(...);
}
// then if any controls exist, scroll the first control into view
if (layout.Controls.Count > 0)
{
layout.ScrollControlIntoView(layout.Controls[0]);
}
Related
I am looking for some assistance in fixing an issue. At the moment I have developed some code that adds a user control to a panel. It adds multiple user controls to the panel and does this based on the .top feature. However, once the panel that I am adding the user controls to is scrolled down the user controls seem to be placed strangely.
I have already tried to adjust the .top value but I am not sure how to do it in relation to the scroll of the panel.
int i = 0;
foreach(memberInformation mi in pnlMembers.Controls.OfType<memberInformation>())
{
try
{
if (UserInformation.isPartyLeader)
{
mi.canUserEdit = "true";
}
else
{
mi.canUserEdit = "false";
}
mi.playerName = downloadInfo.Split(':')[i].Split(',')[0];
mi.playerRole = downloadInfo.Split(':')[i].Split(',')[1];
}
catch
{
pnlMembers.Controls.Remove(mi);
}
i++;
}
VIDEO to show what is happening: https://gyazo.com/985566afb7e4bab464dd06da191a0710
https://gyazo.com/4b0514cbdb310ea8abc46a397458130c
The correct way in order to position panels inside another panel is to use flowlayoutpanel.
Thanks
I'm working on a Excel add-in project that will require me to procedurally generate some controls in a windows task pane. While experimenting, I ran into an issue where this button keeps having its width set to 0, and I don't understand why.
If I don't use any anchoring or docking then the button shows up, but at its default width and height. I am trying to get it to span the width of the layout panel, and it was my understanding you could accomplish this by using AnchorStyles Left and Right, or with DockStyle Fill. However, as soon as I add these properties the width gets set to 0 (as seen from the debugger). I checked the width of the root control (this) and the button's parent control FlowLayoutPanel, and they are both the default non-zero size.
What am I doing wrong?
public MyUserControl()
{
FlowPanel = new FlowLayoutPanel
{
Name = "My Flow Panel",
TabIndex = 0,
FlowDirection = FlowDirection.TopDown,
};
Button button1 = new Button
{
Name = "button1",
Text = this.Width.ToString(),
FlatStyle = FlatStyle.Flat,
Padding = new Padding
{
Left = 10
},
Parent = FlowPanel,
Anchor = (AnchorStyles.Left | AnchorStyles.Right)
};
FlowPanel.Controls.Add(button1);
this.Controls.Add(FlowPanel);
}
You can't anchor like that in FlowLayoutPanels. Instead, subscribe to the SizeChanged event and modify the button width there. You'll probably also need to set the width when you create the button, so below I've just created a method you can call from both places.
FlowPanel.SizeChanged += new System.EventHandler(this.FlowPanel_SizeChanged);
private void FlowPanel_SizeChanged(object sender, EventArgs e)
{
SetButtonWidth();
}
void SetButtonWidth()
{
button1.Width = FlowPanel.Width - FlowPanel.Padding.Horizontal - button1.Margin.Horizontal;
}
As we dynamically add controls to a web page, its location is automatically adjusted, one control will not place over another.
Can we do the same in windows Form Application.
I have to add dynamic label, textbox and buttons at last of an existing win form.
Take a look at the FlowLayoutPanel control.
Represents a panel that dynamically lays out its contents horizontally or vertically.
When you drop controls on the FlowLayoutPanel, it will take care of automatically spacing one from another. If you dock it to the form, such that it resizes with the form, then it will also handle moving controls to the next row / column as needed, so they don't become hidden beyond the boundaries of the form.
If you want to group some controls together, place them inside a Panel and then use that inside the FlowLayoutPanel. Here's some code to demonstrate:
for (var i = 0; i < 5; i++)
{
var panel = new Panel { BorderStyle = BorderStyle.FixedSingle, Width = 100, BackColor = Color.LightBlue };
panel.Controls.AddRange(
new Control[]
{
new Label { Text = "Title", Location = new Point(0, 0) },
new Label { Text = "Subtitle", Location = new Point(0, 25) }
});
flowLayoutPanel1.Controls.Add(panel);
}
I have a form with a scrollable panel and two controls sitting right on top of each other - one visible one not. Based on a certain condition when that form is activated I might swap the visible properties of the two controls. These controls are at the bottom of the scrollable panel. If when I leave that form I leave it scrolled to the bottom, go change the condition that will cause the controls' visibility to swap and go back to that form the visible control will have dropped about 200px down the page leaving a large gap. Anyone know what could be causing this? I tried resetting the scrollbar position to the top on form close but that just causes a smaller gap and sometimes the control to move higher into other controls. Any ideas?
Here is an example that reproduces the problem. If the mouse is moved over the red label, the visibility of button2 is changed to true which causes the scroll jumps back up to Button1.
public class Form123456 : Form {
public Form123456() {
Controls.Add(new UC1());
}
public class UC1 : UserControl {
Button b1 = new Button { Text = "Button1" };
Label lb = new Label { Text = "_", AutoSize = true, BackColor = Color.Red };
Button b2 = new Button { Text = "Button2", Visible = false };
Button b2b = new Button { Text = "x" };
Button b3 = new Button { Text = "Button3" };
public UC1() {
AutoScroll = true;
Dock = DockStyle.Fill;
b1.Location = new Point(0, 200);
b2.Location = new Point(0, 600);
lb.Location = new Point(70, 600);
b2b.Location = new Point(90, 600);
b3.Location = new Point(0, 800);
Controls.Add(b1);
Controls.Add(b2);
Controls.Add(lb);
Controls.Add(b2b);
Controls.Add(b3);
lb.MouseEnter += delegate {
b2.Visible = true;
};
lb.MouseLeave += delegate {
b2.Visible = false;
};
}
}
}
To fix it, one solution is to add this code:
protected override Point ScrollToControl(Control activeControl) {
return this.AutoScrollPosition;
}
Solution from:
Why does clicking in a text box cause an AutoScroll panel to scroll back to the top?
No repro. Sounds to me that you are doing more than just changing the Visible property. Whenever you assign the Location property, you have to add the AutoScrollPosition to compensate for the scroll state. Post code if this doesn't help.
Have you verified the order that you change visibility of the two controls?
The scroll bars on a container with auto scroll set to true will appear and disappear depending on the position of controls that are outside of the visible area of the control. Controls that are invisible do not count.
So in your case if you make both controls invisible at anytime, the scroll bars will disappear. They will come back when one control is made visible. So to make sure you don't have a jump in scroll bar position and controls position you should make sure that at no time are both controls invisible. Another solution is to have a pseudo-visible control on the container. That is a control that has its visibility set to true but it is not actually visible for the user (for example a dot of the color of the background, a label with no text ...). Position this control in the furthest position x,y and the scroll bars will never disappear..
How can you detect exactly when the scroll bar appears in a UserControl? Is there an event for this?
They can only appear when the control is resized or the amount of data in the control increases. Since you get notifications of resize, and adding data is up to you. It's easy to add code to test for the scrollbar in the few places where their visibility can change. There's really no need to have a special notification.
I ended up using the Layout event, and checking if the scrollbars were currently shown or not. A Layout event is sent when the scroll bar visibility changes.
This is more reliable than listening to the size of the window, because the size of the window is not the only thing that can cause the scrollbars to appear.
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.layout(v=vs.110).aspx
Scroll bars are finicky
Working with scroll bars is often arduous. The Layout event solution is correct but I want to add my additional research to the knowledgebase.
I'm attempting to automatically change the width of multiple UserControl inside a custom UserControl that inherits from FlowLayoutPanel. I want a vertical scroll bar to appear only when the list is longer than the panel size. No horizontal scroll bar ever. Your implementation may differ slightly but the bulk of code will be similar and face similar issues.
ScrollableControl
In order for a UserControl to have a scroll bar appear, it must inherit from ScrollableControl. Both Panel and ContainerControl fit this criteria.
ScrollableControl only contains the Scroll event. This event occurs when scrolling but not upon appearance of the scroll bar.
Rather, the Layout Event found inside Control will occur when a control should reposition its child controls. This includes resizing, child resizing, and parent resizing. I would recommend using this event rather than manually checking for resize to avoid unwanted and inconsistent behavior.
Detecting a scroll bar
To detect when the scroll bar should appears, I count the number of controls in the FlowLayoutPanel and compare it against the number of "visible controls". Visible controls are those which intersect the border area of the panel.
private void RichFlowPanel_Layout(object sender, LayoutEventArgs e)
{
var controls = Controls.Cast<Control>().OrderBy(x => x.Top);
var visibles = controls.Where(l => ClientRectangle.IntersectsWith(l.Bounds));
if (visibles.Count() <= Controls.Count)
{
// A scrollbar exists
}
else
{
// A scrollbar does not exist
}
}
Derived from this answer.
Controlling a scroll bar
A scroll bar can automatically show/hide by setting AutoScroll=true. This will also display the horizontal scrollbar if there isn't space for the vertical scrollbar. AutoScroll opens a Pandora's box of scrollbar issues & bugs. In order to keep the horizontal scrollbar hidden, AutoScroll must be false. This answer outlines a work around for keeping the horizontal hidden.
Specifically
panel.HorizontalScroll.Maximum = 0;
HScroll = false;
panel.VerticalScroll.Visible = false;
will hide the horizontal scroll bar.
The usage and odd behavior of HScroll is covered in this answer.
Combining what we've learned
The following event method is attached to the Layout event inside my custom user control extending FlowLayoutPanel.
Point prevPosition;
private void RichFlowPanel_Layout(object sender, LayoutEventArgs e)
{
var controls = Controls.Cast<Control>().OrderBy(x => x.Top);
var visibles = controls.Where(l => ClientRectangle.IntersectsWith(l.Bounds));
prevPosition = AutoScrollPosition;
if (visibles.Count() <= Controls.Count)
{
Console.WriteLine("showing scroll bar" + " V: " + visibles.Count() + " C: " + Controls.Count);
VerticalScroll.Visible = true;
// Insert method here to tell children controls to resize
HorizontalScroll.Maximum = 0;
HScroll = false;
HorizontalScroll.Visible = false;
}
else
{
Console.WriteLine("hiding scroll bar" + " V: " + visibles.Count() + " C: " + Controls.Count);
VScroll = false;
VerticalScroll.Visible = false;
// Insert method here to tell children controls to resize
HorizontalScroll.Maximum = 0;
HScroll = false;
HorizontalScroll.Visible = false;
}
AutoScrollPosition = new Point(Math.Abs(AutoScrollPosition.X), Math.Abs(prevPosition.Y));
}
Additionally, InitalializeComponents() of the FlowLayoutPanel for completeness and because docking, AutoSize, etc. can often bring confusing behavior.
this.SuspendLayout();
//
// RichFlowPanelUserControl
//
this.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.AutoSize = true;
this.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.Padding = new System.Windows.Forms.Padding(3);
this.WrapContents = false;
this.Layout += new System.Windows.Forms.LayoutEventHandler(this.RichFlowPanel_Layout);
this.ResumeLayout(false);
I understand this is slightly off-topic but the combination of this information should help users on their scroll bar adventures.
Why not use the "ClientSizeChanged" event?
This event gets fired if the client size has changed, which is the case if a scrollbar is added.