New to Telerik UI for WinForms? Download free 30-day trial

Synchronize scrollbars in grid hierarchy levels

Environment

Product Version Product Author
2018.3.1016 RadGridView/RadVirtualGrid for WinForms Desislava Yordanova

Description

When you have a hierarchical level in one of the grid controls (RadGridView or RadVirtualGrid), if the width of the respective level is not enough to fit its columns, a horizontal scrollbar is shown. It scrolls only the columns belonging to the respective template. This help article demonstrates a sample approach how to synchronize the scrollbars from the master and child template as it is demonstrated in the animated gif:

synchronize-scrollbars-in-hierarchy-levels

Solution

Subscribe to the TableElement.HScrollBar.ValueChanged event in order to detect when the user scrolls the horizontal scrollbar and store its value. Then, apply the respective ratio to the hierarchical scrollbar by using the formatting event for the related grid control.

The two code snippets below show a sample approach how to achieve the scrollbars' synchronization in RadGridView and RadVirtualGrid respectively:

Synchronize horizontal scrollbars in RadVirtualGrid


        public RadForm1()
        {
            InitializeComponent();
            this.radVirtualGrid1.CellValueNeeded += radVirtualGrid1_CellValueNeeded;
            this.radVirtualGrid1.QueryHasChildRows += radVirtualGrid1_QueryHasChildRows;
            this.radVirtualGrid1.ColumnCount = 10;
            this.radVirtualGrid1.RowCount = 100;
            this.radVirtualGrid1.RowExpanding += radVirtualGrid1_RowExpanding;
            this.radVirtualGrid1.TableElement.HScrollBar.ValueChanged += HScrollBar_ValueChanged;
            this.radVirtualGrid1.CellFormatting += radVirtualGrid1_CellFormatting;
        }

        private void radVirtualGrid1_CellFormatting(object sender, Telerik.WinControls.UI.VirtualGridCellElementEventArgs e)
        {
            VirtualGridHeaderCellElement cell = e.CellElement as VirtualGridHeaderCellElement;
            if (cell != null && cell.ViewInfo.HierarchyLevel > 0)
            {
                RadScrollBarElement sc = cell.TableElement.FindDescendant<RadScrollBarElement>();
                if (sc != null)
                {
                    sc.ScrollParameterChanged -= sc_ScrollParameterChanged;
                    sc.ScrollParameterChanged += sc_ScrollParameterChanged;
                    sc.Value = System.Convert.ToInt32(mainScrollBarRatio * (sc.Maximum - sc.LargeChange + 1));
                }
            }
        }

        private void sc_ScrollParameterChanged(object sender, EventArgs e)
        {
            RadScrollBarElement sc = sender as RadScrollBarElement;
            if (sc != null)
            {
                int expectedValue = System.Convert.ToInt32(mainScrollBarRatio * (sc.Maximum - sc.LargeChange + 1));
                if (sc.Value != expectedValue)
                {
                    sc.Value = expectedValue;
                    Console.WriteLine(sc.Value);
                }
            }
        }

        private double mainScrollBarRatio = 0.0F;
        private double childScrollBarRatio = 0.0F;

        private int hValue = 0;
        private int vValue = 0;
        private bool isRefreshing = false;

        private void HScrollBar_ValueChanged(object sender, EventArgs e)
        {
            mainScrollBarRatio = System.Convert.ToDouble(this.radVirtualGrid1.TableElement.HScrollBar.Value) / (this.radVirtualGrid1.TableElement.HScrollBar.Maximum -
                                                                                                                this.radVirtualGrid1.TableElement.HScrollBar.LargeChange + 1);

            if (!isRefreshing)
            {
                isRefreshing = true;
                hValue = this.radVirtualGrid1.TableElement.HScrollBar.Value;
                vValue = this.radVirtualGrid1.TableElement.VScrollBar.Value;
                this.radVirtualGrid1.TableElement.SynchronizeRows();
                this.radVirtualGrid1.TableElement.HScrollBar.Value = hValue;
                this.radVirtualGrid1.TableElement.VScrollBar.Value = vValue;
                isRefreshing = false;
            }
        }

        private void radVirtualGrid1_RowExpanding(object sender, Telerik.WinControls.UI.VirtualGridRowExpandingEventArgs e)
        {
            e.ChildViewInfo.ColumnCount = 20;
            e.ChildViewInfo.RowCount = 5;
        }

        private void radVirtualGrid1_QueryHasChildRows(object sender, Telerik.WinControls.UI.VirtualGridQueryHasChildRowsEventArgs e)
        {
            if (e.ViewInfo.HierarchyLevel == 0)
            {
                e.HasChildRows = true;
            }
            else
            {
                e.HasChildRows = false;
            }
        }

        private void radVirtualGrid1_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e)
        {
            e.Value = "Data " + e.RowIndex + "." + e.ColumnIndex;
        }

Here is the relevant code for RadGridView:

Synchronize horizontal scrollbars in RadGridView


        private void RadForm1_Load(object sender, EventArgs e)
        {
            this.productsTableAdapter.Fill(this.nwindDataSet.Products);
            this.categoriesTableAdapter.Fill(this.nwindDataSet.Categories);

            radGridView1.AutoGenerateHierarchy = true;
            radGridView1.DataSource = this.nwindDataSet;
            radGridView1.DataMember = "Categories";
            radGridView1.ViewCellFormatting += radGridView1_ViewCellFormatting;
            radGridView1.MasterTemplate.BestFitColumns(BestFitColumnMode.AllCells);
            radGridView1.MasterTemplate.Templates.First().BestFitColumns(BestFitColumnMode.AllCells);
            radGridView1.TableElement.HScrollBar.ValueChanged += HScrollBar_ValueChanged;
        }

        private double mainScrollBarRatio = 0.0F;
        private double childScrollBarRatio = 0.0F;

        private int hValue = 0;
        private int vValue = 0;

        private bool isRefreshing = false;

        private void HScrollBar_ValueChanged(object sender, EventArgs e)
        {
            mainScrollBarRatio = System.Convert.ToDouble(radGridView1.TableElement.HScrollBar.Value) /
                (radGridView1.TableElement.HScrollBar.Maximum - radGridView1.TableElement.HScrollBar.LargeChange + 1);

            if (!isRefreshing)
            {
                isRefreshing = true;
                hValue = radGridView1.TableElement.HScrollBar.Value;
                vValue = radGridView1.TableElement.VScrollBar.Value;
                this.radGridView1.MasterTemplate.Refresh();
                radGridView1.TableElement.HScrollBar.Value = hValue;
                radGridView1.TableElement.VScrollBar.Value = vValue;
                isRefreshing = false;
            }
        }

        private void radGridView1_ViewCellFormatting(object sender, CellFormattingEventArgs e)
        {
            GridDetailViewCellElement detailCell = e.CellElement as GridDetailViewCellElement;
            if (detailCell != null)
            {
                RadScrollBarElement sc = detailCell.Children[0].Children[1] as RadScrollBarElement;
                if (sc != null)
                {
                    sc.ScrollParameterChanged -= sc_ScrollParameterChanged;
                    sc.ScrollParameterChanged += sc_ScrollParameterChanged;

                    sc.ValueChanged -= sc_ValueChanged;
                    sc.ValueChanged += sc_ValueChanged;

                    isRefreshing = true;
                    sc.Value = System.Convert.ToInt32(mainScrollBarRatio * (sc.Maximum - sc.LargeChange + 1));
                    isRefreshing = false;
                    sc.PositionOffset = new SizeF(-10000, 0);
                    sc.Margin = new Padding(0, 0, 0, -20);
                }
            }
        }

        private void sc_ScrollParameterChanged(object sender, EventArgs e)
        {
            RadScrollBarElement sc = sender as RadScrollBarElement;
            if (sc != null)
            {
                sc.Margin = new Padding(0, 0, 0, -20);
                sc.PositionOffset = new SizeF(-10000, 0);
                int expectedValue = System.Convert.ToInt32(mainScrollBarRatio * (sc.Maximum - sc.LargeChange + 1));
                if (sc.Value != expectedValue)
                {
                    isRefreshing = true;
                    sc.Value = expectedValue;
                    isRefreshing = false;
                }
            }
        }

        private void sc_ValueChanged(object sender, EventArgs e)
        {
            RadScrollBarElement sc = sender as RadScrollBarElement;
            double ratio = System.Convert.ToDouble(sc.Value) / (sc.Maximum - sc.LargeChange + 1);

            if (!isRefreshing)
            {
                this.radGridView1.MasterTemplate.EventDispatcher.SuspendNotifications();
                radGridView1.TableElement.HScrollBar.Value = (int)((radGridView1.TableElement.HScrollBar.Maximum -
                                                                    radGridView1.TableElement.HScrollBar.LargeChange + 1) * ratio);
                this.radGridView1.MasterTemplate.EventDispatcher.ResumeNotifications();
            }
        }

Since we force refreshing the grid control when scrolling, it may affect the scrolling performance depending on the number of columns that you have.

In this article