Home » VB.NetRSS

Sorting a bunch of Controls in a FlowLayoutPanel - Having Problems

Good day to all,

apart from it being my birthday, found some quick time to post this question on here.


I have a flowlayoutpanel, and a bunch of controls inside it.

I would like to actually SORT the controls, depending on some values.


For Example:
The FlowLayoutPanel has 4 Controls:

Each control has: Name (string), Maker (string) and Rated (integer) values.

So what I thought of was:


 s AsNew
ForEach c As MyOwnControl In FlowLayoutPanel1.Controls
s.Add(c.title.Text, c)
ForEach c As CustomControl In s.Values


That would arrange the controls depending on the title.text each one has. Now this works but its not very optimal and it will dont work for DESCENDING or work properly with the Rated integer.


Now I had a search around forums and came across Icomparable, and decided to have a go at it but I can't really get it to work:


Implements IComparable
Private m_title AsString
Private m_maker AsString
Private m_rated AsInteger

PublicSubNew (ByVal thetitle AsString , ByVal themaker AsString , ByVal therated AsInteger )
m_title = thetitle
m_maker = themaker
m_rated = therated
PublicFunction CompareTo(ByVal AnEmployee AsObject ) AsIntegerImplements IComparable.CompareTo

IfCType (AnEmployee, Employee).Rated < Me .Rated Then
Return -1
ElseIfCType (AnEmployee, Employee).Rated = Me .Rated Then
Return 0
ElseIfCType (AnEmployee, Employee).Rated > Me .Rated Then
Return 1

PublicReadOnlyProperty title() AsString
Return m_title
PublicReadOnlyProperty Maker() AsString
ReturnMyClass .m_maker
PublicReadOnlyProperty Rated() AsInteger
ReturnMyClass .m_rated


' AND TO CALL IT is where I came across where I can't go on...:

Dim A AsNew List(Of String )
Dim theEmployees() As Employee
ForEach c As MyOwnControl In FlowLayoutPanel1.Controls
Dim x AsNew Employee(c.title.Text, c.maker.Text, c.rating.text)
'didnt know how to add A to theEmployees (most of the code was grabbed from Microsoft's forums, and didn't fully understand it.

Array.Sort(theEmployees) 'call the CompareTo
Dim aEmployee As Employee
ForEach aEmployee In theEmployees


So as you can see I basiclly messed everything up..

All I wanted was a A..Z (Ascending) sorting option for either the Title, Maker or Rated, and a Z...A (Descending) sorting for the same.

If anyone can put me on the right track it would be great :) Thanks!


6 Answers Found


Answer 1

I've implemented custom IComparable exactly twice myself, and I don't recommend it. It's a painfully long way around in code.

The stock FlowLayoutPanel is a strange animal.  The order in which control items contained in it are sorted (meaning the order they're presented in the flow direction) in the control is totally dependent on the Z-order, or the order in which they were added to the control collection.  There is no direct Z-order accessor, but you can use the BringToFront and SendToBack methods on each individual control; BringToFront immediately plants the control at the FRONT of the display and SendToBack immediately plants it at the END.

There are a couple of ways to make this work to your advantage.

First, you already seem to have an array of custom controls that would be added to it (demonstrated in your first codeblock). What's handy about this is that you can iterate through that array to retrieve JUST the Name, JUST the Maker, or JUST the Rated values. You load those values into an array of the appropriate type (string or integer) and then you just call System.Array.Sort(MyArray) - it sorts from A(a)-Z(z) and sorts numerics starting at 0 and climbing. Why this ascending-only sort is handy for you is because there's also a System.Array.Reverse(MyArray) which instantly turns your neatly ascending sort into a perfect descending sort.

At this point you can iterate back through your original array of controls; match the sorted array value against the appropriate value in each control, and when you hit a match you just call SendToBack or BringToFront on that control and move on to the next one. The thing to watch out for is that you ONLY send it to the back or the front once - if the FIRST element you call should be the FIRST in the display, you need to use SendToBack (which places it at the end of the display) and then it moves up to the front as the other controls are called; if you use BringToFront then the first element you move ends up at the end of the display (which is also handy if you have a clear understanding of what's going on, since you can SKIP the whole array reversal after it's sorted once if you know what you're doing with Z-order manipulation).


Answer 2

Thanks for your reply Andrew,

After reading your post thourougly, I found it quite usefull and had a play around my code.

So speaking about the Array part you were talking about, as I am not very good with them at the moment, I had a go at it but no luck.

It came down to the fact in

Dim myarray as array

I couldn't seem to find a way to actually add "values" to the array. I was trying stuff like:

For Each c As MyOwnControl In FlowLayoutPanel1.Controls
            myarray.Add(c.rated.text, c)


But that doesnt work with Arrays, only with ArrayLists, but then with an array list I can't basiccly have 2 values in the List (one for the rated, and one for the control)


I am not sure what to do as arraylists dont seem to allow multiple values, therefore i wouldn't know which control is the highest one...

Btw, Just reposting the code above as for some reason its all spaced out for some reason:

 Dim s As New SortedList
    For Each c As MyOwnControl In FlowLayoutPanel1.Controls
      s.Add(c.rated.text, c)
    For Each c As MyOwnControl In s.Values

Thanks again for your time, and for helping me!


Answer 3

Dim IntArray(-1) As System.Int32
Dim StringArray(-1) As System.String

This creates 2 arrays; one for Integers and one for Strings.  If you're only sorting by 1 property at a time, this works fine no matter how many properties are Strings or how many are Integers.

'Using this For signature allows you to iterate by index'rather than by object - provides more control.'I can't actually remember the last time I went by object,'but I'm pretty positive that at some point I had to go back to'rewrite the code to go by indexFor c_idx As System.Int32 = 0 to ubound(FlowLayoutPanel1.Controls)
      'This is my 'best practice' recommendation'It allows you to have multiple control types in 'your FlowLayoutPanel, and only muck around with'the specific type that is yours.Dim ThisControl As System.Windows.Forms.Control = FlowLayoutPanel1.Controls(c_idx)
      IfTypeOf ThisControl Is MyOwnControl ThenRedimPreserve IntArray(c_idx)
         IntArray(c_idx) = CType(ThisControl,MyOwnControl).rated
         'I'm assuming here that rated is actually an Integer value'If Rated is instead a nested object with a Text Property'(as your last code post suggests) then you can use'the StringArray instead of the IntArray.EndIfNext c_idx
If there's absolutely nothing but MyOwnControl types in the FlowLayoutPanel.ControlCollection, then this will always produce an array of Rated values that is exactly the same size (and before you call System.Array.Sort(IntArray) on it, exactly the same order) as the ControlCollection itself.


Answer 4

Thanks for helping, ive learnt a lot of your code there.


Error    1    Value of type 'System.Windows.Forms.Control.ControlCollection' cannot be converted to 'System.Array'.

Thats the only problem i had, was the Ubound. So I changed it to For c_idx As System.Int32 = 0 To FlowLayoutPanel1.Controls.Count


And how would I continue by making that sorted array into controls?


Answer 5

I'm not providing any more code, sorry.

But as I outlined in previous posts, your next step is to take your known value and iterate back through the already existing controls to match the sorted known values against the contents of those existing controls.


Answer 6

Oh well, seems Ill have to forget sorting them as I have no clue thats why I asked :/


Thanks anyway for your help


<< Previous      Next >>

Microsoft   |   Windows   |   Visual Studio   |   Follow us on Twitter