WPF - How to autoscroll scrollviewer when it need to scroll? - c#

Let me try to explain what my requirement is, first of all, here's a form with 50 fields, at the start, cursor is in the first field TextBox:
When I filled 10 fields, cursor now will be in Field11:
Now, I want the scroll will autoscroll to a location like this when I focused in Field11 for more view:
So if anybody understand what I'm talking about, would you please help me to solve this? Thanks!

You can use ScrollChangedEventArgs.ExtentHeightChange to know if a ScrollChanged is due to a change in the content or to a user action... When the content is unchanged, the ScrollBar position sets or unsets the autoscroll mode. When the content havs changed you can apply autoscrolling.
Code behind:
private Boolean AutoScroll = true;
private void ScrollViewer_ScrollChanged(Object sender, ScrollChangedEventArgs e)
{
// User scroll event : set or unset autoscroll mode
if (e.ExtentHeightChange == 0)
{ // Content unchanged : user scroll event
if (ScrollViewer.VerticalOffset == ScrollViewer.ScrollableHeight)
{ // Scroll bar is in bottom
// Set autoscroll mode
AutoScroll = true;
}
else
{ // Scroll bar isn't in bottom
// Unset autoscroll mode
AutoScroll = false;
}
}
// Content scroll event : autoscroll eventually
if (AutoScroll && e.ExtentHeightChange != 0)
{ // Content changed and autoscroll mode set
// Autoscroll
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ExtentHeight);
}
}

Related

What's the use of HScroll/VScroll in ScrollableControl?

The ScrollableControl class has 2 protected boolean properties: HScroll and VScroll.
As the document says:
Gets or sets a value indicating whether the horizontal scroll bar is visible.
And
AutoScroll maintains the visibility of the scrollbars automatically. Therefore, setting the HScroll or VScroll properties to true have no effect when AutoScroll is enabled.
So I use them like this, but the scrollbar isn't showed:
class MyScrollableControl : ScrollableControl {
public MyScrollableControl() {
this.AutoScroll = false;
this.HScroll = true;
}
}
If I use the following code, it works:
this.HorizontalScroll.Visible = true;
When I put them torgether, the scrollbar is still invisible, and the values of HScroll and HorizontalScroll.Visible keep False.
this.AutoScroll = false;
this.HScroll = true;
this.HorizontalScroll.Visible = true;
So what is the real use of HScroll and VScroll ?
Update
My code and test
HScroll property does not affect scroll visibility directly, but it prevent Scroll to be hidden with HorizontalScroll.Visible value
In case when HorizontalScroll.Visible is set to true than HScroll will also get a value true (see 2nd line in the table)
In case when AutoScroll is set to true HorizontalScroll.Visible always stays true and HScroll doesnot have any influense (see last 2 lines)
Make an app with Control that contains 3 buttons with next handler code, and play with it to see what exactly happening there:
private void button1_Click(object sender, EventArgs e)
{
AutoScroll = !AutoScroll;
SetValues();
}
private void button3_Click(object sender, EventArgs e)
{
HScroll = !HScroll;
SetValues();
}
private void SetValues()
{
button1.Text = $"Auto: {(AutoScroll ? "On" : "Off")}";
button3.Text = $"HScroll: {(HScroll ? "On" : "Off")}";
button2.Text = $"Visible: {(HorizontalScroll.Visible ? "On" : "Off")}";
}
private void button2_Click(object sender, EventArgs e)
{
HorizontalScroll.Visible = !HorizontalScroll.Visible;
SetValues();
}
Usage (AutoScroll = false)
To Manually show Scroll set HorizontalScroll.Visible to true
To Manually hide Scroll set HScroll to false and than HorizontalScroll.Visible to false
Usage (AutoScroll = true)
HorizontalScroll.Visible is always true and cannot be changed
HScroll doesnot affects anything
So what is the real use of HScroll and VScroll ?
You set them to true when you have the intention of showing the scrollbars. But that is not enough, you also have to state what they should display. A scrollbar needs to know the thumb size, minimum and maximum position and current position.
You are doing battle with the internal ApplyScrollbarChanges() method. One thing it does is hide the scrollbars, even if HScroll or VScroll is set to true, if it does not have sufficient info to configure the bars. The code of the method is too large to fit here, in a nutshell it derives this info from:
The value of the AutoScrollMinWidth property.
If the control has non-default layout then it allows the layout engine of that control to determine the scroll bounds. This is only the case for the FlowLayoutPanel and TableLayoutPanel controls.
If the control has default layout then it iterates the child controls to look at their bounds.
Item 2 is an attractive customization angle, but they made the LayoutEngine class internal so you can't derive your own. Item 3 is not fundamentally different from what AutoLayout = true already does. It does work however, just add a control in the constructor, override OnClientSizeChanged() to call AdjustFormScrollbars(true) and you'll now see the scrollbar.
Which leaves item 1 to control the scrollbars. The property setter looks like this. Yup, it sneakily sets the AutoScroll property back to true :)
Simply set the AutoScrollMinSize property to control the scrollbars.
HScroll will show the horizontal scroll bar is the AutoScroll property is not defined.
In all your examples where the scroll bars do not appear, it is because you have the AutoScroll set to false, hiding the display of any scroll bars.

Winform DataGRidView Scroll Bar not showing until needed [duplicate]

I have a winform in vs2008 that contains a DataGridView. The datagrid contains a list with several columns. These are fixed width, exept one that I have set up to take whatever space is left and fill the width of the view. The winform is resizeable in all directions.
The issue I am trying to solve is that when I increase the vertical size of the window the scrollbar disappears and the columns snap to the right to fill the extra space. What I would like to happen is that the vertical scrollBar never disappears. Setting ScrollBars to vertical in the properties of the DataGridView does not do this.
Is this at all possible to achieve? And, if so, how do I get the vertical scrollbar to always be visible?
Try subclassing the DataGridView and handling the VerticalScrollBar's VisibleChanged event. You should be able to set the Visible property to True in there, overriding the default behaviour.
Something like this, I think...
public class SubclassedDataGridView : DataGridView
{
public SubclassedDataGridView (): base()
{
VerticalScrollBar.VisibleChanged += new EventHandler(VerticalScrollBar_VisibleChanged);
}
void VerticalScrollBar_VisibleChanged(object sender, EventArgs e)
{
VerticalScrollBar.Visible = true;
}
}
In my case, (re)sorting the grid helped. Try sth like this:
if (gridName.SortedColumn == null)
gridName.Sort(gridNameColumns[columnName],ListSortDirection.Ascending);
else
{
ListSortDirection dir;
if (gridName.SortOrder == SortOrder.Descending)
dir = ListSortDirection.Descending;
else dir = ListSortDirection.Ascending;
gridName.Sort(gridName.SortedColumn, dir);
}
One of the possibility is to trigger the event of when the scrollbar is disapearing so you can prevent the event and stop it.

Unable to scroll to the bottom of WinForm

I am using visual studio 2012 for this. Basically I have a WinForm that I want to expand.
Inside the form designer, I am able to see that my form has a scroll bar, but when I compile the program, the scroll bar does not appear. The controls that are beyond my screen size are clipped off, as opposed to having a scrollbar.
Are there any settings that I have missed out? Currently I set my AutoScroll = true.
Scrollbars show up when a parent control has the AutoScroll set to true and a child control has a MinimumSize such that the client area of the child control is larger than the client area of the parent control.
E.g.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var sampleForm = new Form() { AutoScroll = true };
Panel panel = new Panel() { BackColor = Color.Red, AutoSizeMode = AutoSizeMode.GrowAndShrink, AutoSize = true };
Button btn = new Button { Text = "Toggle MinSize", AutoSize = true };
panel.Controls.Add(btn);
btn.Click += delegate {
if (panel.MinimumSize == Size.Empty)
panel.MinimumSize = new Size(600,600);
else
panel.MinimumSize = Size.Empty;
};
sampleForm.Controls.Add(panel);
Application.Run(sampleForm);
}
If your child panel correctly calculates its preferred size, then you can override the MinimumSize property and return the PreferredSize.
AutoScroll = true is enough to display scroll on form no other setting is required.
just try other thing add panel in form and set panels AutoScroll = true and then add control to it and check that scroll is working or not ?
Take a look at the properties of the controls within the container for which you want autoscroll to work. One possibility is that you set one or more of those controls Anchor property to Right or something, which can reverse the autoscroll setting behind the scenes to effectively turn it off. Also check the RightToLeft property of the container, and try setting that to the default "no"
Make sure you have set Dock.Fill i.e. Dock property to Fill
Set property AutoScroll = true , AutoSize = true, AutoSizeMode = GrowOnly ,you can also do this by adding a panel to the form and set panel AutoScroll = true.
compare your issue with example here

C# WinForms: Make panel scrollbar invisible

I have a panel1 with AutoScroll = true.I have to make panel1 scroll with btnUp and btnDown. So far I've made what I was asked for
private void btnUpClicked(Object sender, EventArgs e)
{
if (panel1.VerticalScroll.Value - 55 > 0)
panel1.VerticalScroll.Value -= 55;
else panel1.VerticalScroll.Value = 0;
}
private void btnDownClicked(Object sender, EventArgs e)
{
panel1.VerticalScroll.Value += 55;
}
But now I need to hide Scrollbar or make it invisible. I tried
panel1.VerticalScroll.Visible = false;
but it doesn't work. Any ideas guys?
Ok, I've done the working example of this for you. All you have to do is to change the max value depending on the total size of all the items inside your panel.
Form code:
public partial class Form1 : Form
{
private int location = 0;
public Form1()
{
InitializeComponent();
// Set position on top of your panel
pnlPanel.AutoScrollPosition = new Point(0, 0);
// Set maximum position of your panel beyond the point your panel items reach.
// You'll have to change this size depending on the total size of items for your case.
pnlPanel.VerticalScroll.Maximum = 280;
}
private void btnUp_Click(object sender, EventArgs e)
{
if (location - 20 > 0)
{
location -= 20;
pnlPanel.VerticalScroll.Value = location;
}
else
{
// If scroll position is below 0 set the position to 0 (MIN)
location = 0;
pnlPanel.AutoScrollPosition = new Point(0, location);
}
}
private void btnDown_Click(object sender, EventArgs e)
{
if (location + 20 < pnlPanel.VerticalScroll.Maximum)
{
location += 20;
pnlPanel.VerticalScroll.Value = location;
}
else
{
// If scroll position is above 280 set the position to 280 (MAX)
location = pnlPanel.VerticalScroll.Maximum;
pnlPanel.AutoScrollPosition = new Point(0, location);
}
}
}
Picture example:
You have to set AutoScroll option to False on your panel. I hope you understand what I've done and will get your panel running the way you want. Feel free to ask if you have any questions.
The Panel control takes on the duty you gave it by setting AutoScroll to true pretty serious. This always includes displaying the scrollbar gadget if it is necessary. So what you tried cannot work, hiding the vertical scrollbar forces Panel to recalculate layout since doing so altered the client area. It will of course discover that the scrollbar is required and promptly make it visible again.
The code that does this, Panel inherits it from ScrollableControl, is internal and cannot be overridden. This was intentional.
So using AutoScroll isn't going to get you anywhere. As an alternative, do keep in mind what you really want to accomplish. You simply want to move controls up and down. Easy to do, just change their Location property. That in turn is easiest to do if you put the controls on another panel, big enough to contain them. Set its AutoSize property to True. And implement you buttons' Click event handlers by simply changing that panel's Location property:
private const int ScrollIncrement = 10;
private void ScrollUpButton_Click(object sender, EventArgs e) {
int limit = 0;
panel2.Location = new Point(0,
Math.Min(limit, panel2.Location.Y + ScrollIncrement));
}
private void ScrollDownButton_Click(object sender, EventArgs e) {
int limit = panel1.ClientSize.Height - panel2.Height;
panel2.Location = new Point(0,
Math.Max(limit, panel2.Location.Y - ScrollIncrement));
}
Where panel1 is the outer panel and panel2 is the inner one that contains the controls. Be careful when you use the designer to put controls on it, it has a knack for giving them the wrong Parent. Be sure to use the View + Other Windows + Document Layout helper window so you can see this going wrong. After you filled it, set its AutoSizeMode property to GrowAndShrink so it snaps to its minimum size.
Try this:
panel.AutoScroll = true;
panel.VerticalScroll.Enabled = false;
panel.VerticalScroll.Visible = false;
Edit:
Actually when AutoScroll = true; It will take care of hscroll and vscroll automatically and you wont be able to change it. I found this on Panel.AutoScroll Property on MSDN
AutoScroll maintains the visibility of the scrollbars automatically. Therefore, setting the HScroll or VScroll property to true has no effect when AutoScroll is enabled.
You may try this to workaround this problem, I have copied it from this Link.
Behavior Observations 1:
If AutoScroll is set to true, you can't modify anything in VerticalScroll or HorizontalScroll. AutoScroll means AutoScroll; the control decides when scrollbars are visible, what the min/max is, etc. and you can't change a thing.
So if you want to customize the scrolling (e.g. hide scrollbars), you must set AutoScroll to false.
Looking at the source code for the ScrollableControl with Lutz Roeder's .NET Reflecter, you can see that if AutoScroll is set to true, it ignores your attempts to change property values within the VerticalScroll or HorizontalScroll properties such as MinValue, MaxValue, Visible etc.
Behavior Observations 2:
With AutoScroll set to false, you can change VerticalScroll.Minimum, VerticalScroll.Maximum, VerticalScroll.Visible values.
However, you cannot change VerticalScroll.Value!!! Wtf! If you set it to a non-zero value, it resets itself to zero.
Instead, you must set AutoScrollPosition = new Point( 0, desired_vertical_scroll_value );
And finally, SURPRISE, when you assign positive values, it flips them to negative values, so if you check AutoScrollPosition.X, it will be negative! Assign it positive, it comes back negative.
So yeah, if you want custom scrolling, set AutoScroll to false. Then set the VerticalScroll and HorizontalScroll properties (except Value). Then to change the scroll value, you need to set AutoScrollPosition, even though you aren't using auto scrolling! Finally, when you set the AutoScrollPosition, it will take on the opposite (i.e. negative) value that you assign to it, so if you want to retrieve the current AutoScrollPosition later, for example if you want to offset the scroll value by dragging the mouse to pan, then you need to remember to negate the value returned by AutoScrollPosition before reassigning it to AutoScrollPosition with some offset. WOW. Wtf.
One other thing, if you are trying to pan with the mouse, use the values of Cursor.Position rather than any mouse locations returned by the mouse events parameters. Scrolling the control will cause the event parameter values to be offset as well, which will cause it to start firing mouse move events complete with undesired values. Just use Cursor.Position, because it will use mouse screen coordinates as a fixed frame of reference, which is what you want when you're trying to pan/offset the scroll value.

smooth scrolling .net forms

Hi I am using forms in .net and i am adding lots of linked labels dynamically during runtime,
I am adding these linklabels to panel and adding that panel to the winform. When the no of linklabels increases the form puts out an auto scrollbar(vertical)...
Now when i scroll down using that autoscroll the form is not updating its view as i scroll, the form gets refreshed only when i stop scrolling...
Also when it refresh it looks too bad.. i can see how it draws slowly....
Has anyone dealt with this before??
I tried form.refresh() in scroll event handler but that doesn't seem to help..
Any clues?
Pop this into your class (UserControl, Panel, etc) , then it will work with thumb drag.
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
protected override void WndProc (ref Message m)
{
if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
&& (((int)m.WParam & 0xFFFF) == 5))
{
// Change SB_THUMBTRACK to SB_THUMBPOSITION
m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
}
base.WndProc (ref m);
}
If you don't want to use WinAPI calls, you can do this:
// Add event handler to an existing panel
MyPanel.Scroll += new EventHandler(MyPanelScroll_Handler);
// Enables immediate scrolling of contents
private void MyPanelScroll_Handler(System.Object sender, System.Windows.Forms.ScrollEventArgs e)
{
Panel p = sender As Panel;
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll) {
p.HorizontalScroll.Value = e.NewValue;
} else if (e.ScrollOrientation == ScrollOrientation.VerticalScroll) {
p.VerticalScroll.Value = e.NewValue;
}
}
Try setting your form's DoubleBuffered property to True.
Update: actually, that probably won't do anything since your controls are on a Panel on your Form. The built-in Panel control doesn't have an exposed DoubleBuffered property, so the way to do it is to add a UserControl name DBPanel to your project, and change the code so that it inherits from Panel instead of UserControl (you can change this manually in the CS file after you add it). When you add the UserControl, the code will look like this:
public partial class DBPanel : UserControl
{
public DBPanel()
{
InitializeComponent();
}
}
Edit it so that it looks like this (change UserControl to Panel and add the "this.DoubleBuffered = true;" line to the constructor):
public partial class DBPanel : Panel
{
public DBPanel()
{
InitializeComponent();
this.DoubleBuffered = true;
}
}
When you build the project, the compiler will barf on a line that begins with "this.AutoScaleMode ... ". Delete this line and rebuild.
You can now use the DBPanel control on your form in place of a regular Panel, and this should take care of your flicker problem.
Update 2: sorry, I didn't read your question closely enough. You're right, the Panel doesn't redraw itself until you let go of the scrollbar's thumb. I think to achieve this effect you'll just have to create your own UserControl.
Basically you'd just have a UserControl with a VScrollBar docked on the right, and a Panel with AutoScroll = false docked on the left taking up the remainder of the space. The Scroll and ValueChanged events of the VScrollBar fire as you move the thumb up and down, so after adding a bunch of LinkLabels to the inner Panel you can use these events to change the Top position of the Panel, and thus achieve the dynamic scrolling effect you're looking for.
It's kind of irritating that the Panel doesn't work this way by default, or even have a setting that enables it.
The simplest way is to refresh the panel during the scroll event.
private void panel1_Scroll(object sender, ScrollEventArgs e)
{
panel1.Refresh();
}

Categories

Resources