1
\$\begingroup\$
private void rect_ManipulationStarting(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        startingColumn = Grid.GetColumn(e.OriginalSource as Windows.UI.Xaml.Shapes.Rectangle);

        gLValue = headerGrid.ColumnDefinitions[startingColumn].Width.Value + e.Delta.Translation.X;
        if (gLValue < 5)
            return;
        else
        {
            headerGrid.ColumnDefinitions[startingColumn].Width = new GridLength(gLValue);
            cycleThroughColumns(libraryGrid, startingColumn, gLValue);
            cycleThroughColumns(playlistGrid, startingColumn, gLValue);
        }            
    }

   private void cycleThroughColumns(Grid anyGrid, int anyStartingColumn, double anyGLValue)
   {
       if (anyGrid.Children.Count > 0)
       {
           if ((anyGrid.Children[0] as Grid).ColumnDefinitions.Count > 0)
           {
               for (int index = 0; index < anyGrid.Children.Count; index++)
               {
                   (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
               }
           }
       }
   }

In the header there is a rectangle on each column divider, that when moved calls the event handler. Within libraryGrid and playlistGrid are grids for each row. (In each of these grids is a textblock). It's just too slow when resizing 884 columns and I was wondering if there was a way to optimize the code to speed it up. Only about 20 rows are visible to the user at one time.

Here are changes I made using a poor-man's virtualization technique, I took the height of the scrollviewer and divided by the height of the items to get the number of items, added 2 for good measure, and now I loop through about 10-15 items first (the visible ones) before I loop through the second set, both are asynchronous, although it still has some lag it's much, much more responsive. Any further optimization ideas are also appreciated.

private void rect_ManipulationStarting(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        startingColumn = Grid.GetColumn(e.OriginalSource as Windows.UI.Xaml.Shapes.Rectangle);

        gLValue = headerGrid.ColumnDefinitions[startingColumn].Width.Value + e.Delta.Translation.X;
        if (gLValue < 5)
            return;
        else
        {
            headerGrid.ColumnDefinitions[startingColumn].Width = new GridLength(gLValue);

            cycleThroughColumns(libraryGrid, startingColumn, gLValue);
            cycleThroughColumns(playlistGrid, startingColumn, gLValue);
        }            
    }

    private void cycleThroughColumnsAsync(Grid anyGrid, int anyStartingColumn, double anyGLValue, int anyVertStart, int anyVertEnd)
    {
        if (anyGrid.Children.Count > 0)
        {
            if ((anyGrid.Children[0] as Grid).ColumnDefinitions.Count > 0)
            {
                for (int index = anyVertStart; index < anyVertEnd; index++)
                {
                    if (anyGrid.Children[index] as Grid != null)
                    {
                        (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }

    async private void cycleThroughColumns(Grid anyGrid, int anyStartingColumn, double anyGLValue)
    {
        int vertStart = getVertStart(anyGrid);
        int vertEnd = getVertEnd(vertStart, anyGrid);
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => cycleThroughColumnsAsync(anyGrid, anyStartingColumn, anyGLValue, vertStart, vertEnd));
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () => cycleThroughColumnsLeftOverAsync(anyGrid, anyStartingColumn, anyGLValue, vertStart, vertEnd));
    }

    private void cycleThroughColumnsLeftOverAsync(Grid anyGrid, int anyStartingColumn, double anyGLValue, int anyVertStart, int anyVertEnd){
        for (int index = 0; index < anyVertStart; index++)
        {
            if (anyGrid.Children[index] as Grid != null)
            {
                (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
            }
            else
            {
                break;
            }
        }

        for (int index = anyVertEnd; index < anyGrid.Children.Count; index++)
        {
            if (anyGrid.Children[index] as Grid != null)
            {
                (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
            }
            else
            {
                break;
            }
        }
    }

    private int getVertStart(Grid anyGrid)
    {
        return (int)(anyGrid.Parent as ScrollViewer).VerticalOffset / 30;
    }

    private int getVertEnd(int anyVertStart, Grid anyGrid)
    {
        double numberOfRowsInView = (anyGrid.Parent as ScrollViewer).RenderSize.Height / 30;
        return (int)(anyVertStart + numberOfRowsInView) + 2;

    }
\$\endgroup\$
6
  • 2
    \$\begingroup\$ A lot more information is need - Winforms or WPF? Is column virtualization enabled? Are columns Auto width or fixed width? TemplateColumn or builtin column (WPF)? Would it perhaps be reasonable to turn the table upside down, make columns to rows and rows to columns? (that might work better) If you are using WPF, then are you aware that WPF DataGrid is SLOW full stop. Asking it to handle 884 columns might be close to impossible depending on the scenario... Winforms DataGrid can certainly be faster in this case I think... \$\endgroup\$ – Marko Aug 25 '13 at 13:21
  • \$\begingroup\$ Sorry I didn't specify, it's winrt for a metro app. Columns are fixed width at the moment. I'm just using the basic Grid control, has 884 rows, each row has a grid with 7 columns. The virtualization sounds like it might help, not sure how to do that or if it's possible in the grid control. Also possibly of note, this is all from code behind, no xaml populating the rows and columns, although the parent grids are declared in xaml. \$\endgroup\$ – Zev Aug 25 '13 at 20:08
  • \$\begingroup\$ I'm trying to find information on virtualizingstackpanel, seems the internet is devoid of information relating to winrt, almost all of it is for wpf. Would placing the grid structure in a virtualizingstackpanel or placing the inner grids in many of them work? I keep getting the error "VirtualizingStackPanel can only be used to display items within an ItemsControl," so I put the grids in an itemscontrol but it still is not working, I will keep trying but it's confusing. \$\endgroup\$ – Zev Aug 26 '13 at 2:06
  • \$\begingroup\$ winrt is still a relatively new technology and many developers are avoiding it like the plague, thus the small amount of information. Am I correct to assume you are then developing a Win 8 Metro application? VirtualizingStackPanel is items based, not layout based which a simple Grid is. I don't know how to implement your own control from scratch to specifically use VirtualizingStackPanel, you'll have to Google it. This could perhaps be a start - link. Otherwise GridView you can use. \$\endgroup\$ – Marko Aug 26 '13 at 6:25
  • 1
    \$\begingroup\$ glad to hear you got to improve your code. You should post an answer yourself and describe the code changes you made so that others could learn from this as well. \$\endgroup\$ – Marko Aug 27 '13 at 5:59
1
\$\begingroup\$

Here is the final code without any lag!

    //event handler for when the rectangles are dragged left or right (resizing the columns)
    private void rect_ManipulationStarting(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        startingColumn = Grid.GetColumn(e.OriginalSource as Windows.UI.Xaml.Shapes.Rectangle);

        gLValue = headerGrid.ColumnDefinitions[startingColumn].Width.Value + e.Delta.Translation.X;
        if (gLValue < 5)
            return;
        else
        {
            headerGrid.ColumnDefinitions[startingColumn].Width = new GridLength(gLValue);

            cycleThroughColumns(libraryGrid, startingColumn, gLValue);
            cycleThroughColumns(playlistGrid, startingColumn, gLValue);
        }            
    }

    //event handler called when the resizing of the columns is completed
    private void rect_ManipulationEnded(object sender, ManipulationCompletedRoutedEventArgs e)
    {
        if (gLValue < 5)
            return;
        else
        {
            cycleThroughColumns2(libraryGrid, startingColumn, gLValue);
            cycleThroughColumns2(playlistGrid, startingColumn, gLValue);
        }
    }

    //cycles through the columns in view
    private void cycleThroughColumns(Grid anyGrid, int anyStartingColumn, double anyGLValue)
    {
        int vertStart = getVertStart(anyGrid);
        int vertEnd = getVertEnd(vertStart, anyGrid);
        cycleThroughColumns(anyGrid, anyStartingColumn, anyGLValue, vertStart, vertEnd); //not async even though the method has it in its name
    }

    //cycles through the columns not in view, asynchrounously
    async private void cycleThroughColumns2(Grid anyGrid, int anyStartingColumn, double anyGLValue)
    {
        int vertStart = getVertStart(anyGrid);
        int vertEnd = getVertEnd(vertStart, anyGrid);
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () => cycleThroughColumnsLeftOverAsync(anyGrid, anyStartingColumn, anyGLValue, vertStart, vertEnd));
    }

    //does the work to cycle through the visible rows
    private void cycleThroughColumns(Grid anyGrid, int anyStartingColumn, double anyGLValue, int anyVertStart, int anyVertEnd)
    {
        if (anyGrid.Children.Count > 0)
        {
            if ((anyGrid.Children[0] as Grid).ColumnDefinitions.Count > 0)
            {
                for (int index = anyVertStart; index < anyVertEnd; index++)
                {
                    if (anyGrid.Children[index] as Grid != null)
                    {
                        (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }

    //does the work to cycle through the columns from 0 to vertstart, and vertend to the end of the list
    private void cycleThroughColumnsLeftOverAsync(Grid anyGrid, int anyStartingColumn, double anyGLValue, int anyVertStart, int anyVertEnd){
        for (int index = 0; index < anyVertStart; index++)
        {
            if (anyGrid.Children[index] as Grid != null)
            {
                (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
            }
            else
            {
                break;
            }
        }

        for (int index = anyVertEnd; index < anyGrid.Children.Count; index++)
        {
            if (anyGrid.Children[index] as Grid != null)
            {
                (anyGrid.Children[index] as Grid).ColumnDefinitions[anyStartingColumn].Width = new GridLength(anyGLValue);
            }
            else
            {
                break;
            }
        }
    }

    //returns the starting row that is in view
    private int getVertStart(Grid anyGrid)
    {
        return (int)(anyGrid.Parent as ScrollViewer).VerticalOffset / ROW_HEIGHT;
    }

    //returns the last row that can be in view
    private int getVertEnd(int anyVertStart, Grid anyGrid)
    {
        double numberOfRowsInView = (anyGrid.Parent as ScrollViewer).RenderSize.Height / 30;
        return (int)(anyVertStart + numberOfRowsInView) + 2;

    }

I separated the cycling of the visible columns and the ones offscreen into the manipulationStarting and manipulationEnded event handlers, this sped up the performance to the point where there is effectively no lag.

The only bug is if the user scrolls down with the mousewheel while dragging the column resizing rectangles, but I think it's acceptable, as the user only has to drag the rectangles again to fix the view.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.