Search code examples
c#winformsscrollbar

C# Winforms Panel ScrollBar Jumping when focus changes


I have come across a couple similar questions however the solutions posted did not work for my case.

Some detail:

I am creating a Dashboard, I have a Main Content Panel (Scrollable) that is populated with a UserControl based on the page selected.

On the user control I have multiple different graphs and tables for dashboarding purposes. I have a custom tool tip that appears next to the cursor when hovering. It works perfectly until I click on any of the graphs. Then the whenever the tool tip is moved to the cursor position the scroll bar jumps to a seemingly random position near the top of the UserControl.

I was able to conclude the jumping occurs during the tool tip moving process: Tool Tip Code:

 private void ytdSalesChart_GetToolTipText(object sender, ToolTipEventArgs e)
 {

     Chart control = sender as Chart;

     Panel parent = this.Parent as Panel;

     int ScrollPosStart = parent.VerticalScroll.Value;

     Debug.WriteLine($"Moving Tooltip! Scroll Pos: {ScrollPosStart}");
     if (e.HitTestResult.ChartElementType == ChartElementType.DataPoint)
     {
         int i = e.HitTestResult.PointIndex;
         int seriesI = control.Series.IndexOf(e.HitTestResult.Series);
         DataPoint dp = e.HitTestResult.Series.Points[i];

         if (!chartTip.Visible)
         {
             chartTip.Visible = true;
         }
         chartTip.SetTitle(e.HitTestResult.Series.Name.Trim());
         chartTip.SetValLbl("Value: " + FormatNumber(dp.YValues[0]));
         chartTip.SetSeries("Branch: " + dp.AxisLabel);

         chartTip.SetSeriesColor(control.PaletteCustomColors[seriesI]);
         chartTip.Location = new Point(control.Parent.Location.X + e.X + 10, control.Parent.Location.Y + e.Y + 10);
         chartTip.BringToFront();
         Debug.WriteLine($"Finished Moving Tooltip! Scroll Pos: {parent.VerticalScroll.Value}");
         parent.VerticalScroll.Value = ScrollPosStart;
         this.ActiveControl = null; This helped but did not fix the issue also added to the on_click for the charts (helped more cases)
     }
     else
     {
         chartTip.Visible = false;
     }

 }

The output I get when the bug is occurring is (Keep in mind this is spamming my console for testing and all occurs in a fraction of a second when I hover the chart for the tool tip):

It does different things depending on conditions: When I am scrolled to the top (0) it does the below

Moving Tooltip! Scroll Pos: 0
Finished Moving Tooltip! Scroll Pos: 194
Moving Tooltip! Scroll Pos: 87 

The third line is the second run of the ytdSalesChart_GetToolTipText because the scroll bug triggers again from the the moving where the mouse is touching.

Any information/insight on what is going on will be helpful.

---UPDATE--- After some testing and adjusting I have found that changing the form from Fullscreen to Windowed and back again will stop the bug.

As @Jimi commented it is not a bug but more of an unintended consequence of default behavior.

I am still unclear on how to avoid this behavior but I suspect looking into how the focus changes when interacting with maximize button / a component outside of the User Control (Panel) will lead to a solution.

If I figure it out I will post my fix thanks for reading.

-- SOLVED -- Thanks to @Jimi pointed me in the right direction (comments)



Solution

  • The fix is to change whatever Panel you are using as the scrolling panel to a custom UserControl and override the ScrollToControl function.

    As stated in the other posts @Jimi commented, it is a "Feature" that will scroll to the active control. To get around this you can use this code below and for your container instead...

    internal class CustomFlowLayoutPanel : System.Windows.Forms.FlowLayoutPanel
    {
        protected override System.Drawing.Point ScrollToControl(System.Windows.Forms.Control activeControl)
        {
            // Returning the current location prevents the panel from
            // scrolling to the active control when the panel loses and regains focus
            return this.DisplayRectangle.Location;
        }
    }