Search code examples
vb.netparallel.foreach

VB.NET How to get a control collection to cast to the enumerable in Parallel.ForEach


Pretty simple, I need to resize a few hundred pictureboxes inside a FlowLayoutPanel. In an effort to speed it up, I'm experimenting with Parallel.ForEach

I'm trying to turn this for each loop

For Each p As Control In Me.PanelMain.Controls
            p.Size = New Size(GWidth, GHeight)
Next

Into a Parallel.ForEach loop. I'm struggling with the syntax, and online documentation is particularly cryptic this time around. I've gotten this far:

Parallel.ForEach(Of Control)(Me.PanelMain.Controls, Sub(resize)
                                                    resize.Size = New Size(GWidth, GHeight)
                                                End Sub
                                    )

This gives an error on compile "System.InvalidCastException: 'Unable to cast object of type 'ControlCollection' to type 'System.Collections.Generic.IEnumerable`1[System.Windows.Forms.Control]'.'" And I really just don't know where to go from here.


Solution

  • ControlCollection implements IEnumerable, but not IEnumerable<T>. IEnumerable<T> is a type which Parallel.ForEach expecting.

    To cast controls collection to IEnumerable(Of Control) you can use

    Dim controls = Me.PanelMain.Controls.Cast(Of Control)()
    
    Parallel.ForEach(controls, Sub(control) control.Size = New Size(GWidth, GHeight))
    

    Note that UI controls can be updated only on the thread they has been created on. So splitting resizing between multiple threads will not work as you expect.

    I would reconsider having hundreds of pictureboxes on the form, can user see all pictureboxes at once?

    That said, you can try to speed up updating controls by suspending form redrawing after every control resize.

    Me.PanelMain.SuspendLayout();
    
    Dim newSize As New Size(GWidth, GHeight)
    For Each p As Control In Me.PanelMain.Controls
        p.Size = newSize
    Next
    
    Me.PanelMain.ResumeLayout();