Linking Zune media items with LinQ, Part 2 (Matt Gertz)

In this post, I’ll continue on with coding the new playlist shuffler.  If you haven’t read part 1 yet, I highly recommend it so that this post will make more sense. J

Code for the controls (continued)

The Title TextBox

When the title changes, we’ll want to indicate that the playlist has changed, and we’ll want to cache the change and update the menus.  This is pretty simple:

    Private Sub edtTitle_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles edtTitle.TextChanged

        playlistTitle = edtTitle.Text

        FileChanged = True

        ResetMenus()

    End Sub

The ListBox

The listbox has only two events that we need to handle.  The first is just handling whenever the selection changes – we’ll have to update the buttons in such a case:

    Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles ShuffleListBox.SelectedIndexChanged

        ResetButtons()

    End Sub

 

The other event is the DrawItem event, which is called whenever a listbox element needs to be draw.  Normally, this event is handled by the base class, but for this application, we’ll handle it so that we can decorate items which are linked.  (We set this up by setting the DrawMode property of the listbox to OwnerDrawFixed.) Now, it even gets called when the listbox is empty, in order to draw the focus rectangle or whatever else should go into an empty list.  Drawing isn’t too difficult.  First, I’ll let it draw the background as usual:

    Private Sub ShuffleListBox_DrawItem(ByVal sender As Object, _

ByVal e As System.Windows.Forms.DrawItemEventArgs) _

Handles ShuffleListBox.DrawItem

        e.DrawBackground()

 

Then, assuming that the index is non-zero and we actually have some text, we’ll check to see if the item is linked.  Anything linked with be written in bold, with the header node written in green, the tail node written in red, and anything in-between written in blue.  The default, however, will be black text:

        If e.Index >= 0 Then

            Dim myBrush As Brush = Brushes.Black

 

which we will use if there are no links involved:

            If elem.NextLinkedElement Is Nothing AndAlso elem.PrevLinkedElement Is Nothing Then

                e.Graphics.DrawString(ShuffleListBox.Items(e.Index).ToString(), _

                    e.Font, myBrush, e.Bounds, StringFormat.GenericDefault)

 

but if there are links, we’ll have to use a bold font:

            Else

                Dim linkFont As Font = New Font(e.Font.Name, e.Font.Size, _

                   FontStyle.Bold, e.Font.Unit)

 

and set the colors appropriately:

                If elem.NextLinkedElement IsNot Nothing _

AndAlso elem.PrevLinkedElement Is Nothing Then

                    myBrush = Brushes.Green ‘ Starting items are green

                ElseIf elem.NextLinkedElement IsNot Nothing _

AndAlso elem.PrevLinkedElement IsNot Nothing Then

                    myBrush = Brushes.Blue ‘ Middle items are blue

                ElseIf elem.NextLinkedElement Is Nothing _

AndAlso elem.PrevLinkedElement IsNot Nothing Then

                    myBrush = Brushes.Red ‘ End items are red

                End If

 

                e.Graphics.DrawString(ShuffleListBox.Items(e.Index).ToString(), _

                    linkFont, myBrush, e.Bounds, StringFormat.GenericDefault)

            End If

        End If

 

Finally, regardless of what we drew, we’ll need to draw the focus rectangle (and dotted line around any selections:

        e.DrawFocusRectangle()

    End Sub

The Link and Unlink buttons

I’ve been chattering a lot about linked files, but without walking through the process of actually linking them, it may be hard to see how they work in practice.  When Link is chosen, we’ll take all of the selected files, move them together, and point them at each other as a doubly-linked list.  We can then use the existence of these links to change the behavior the program when drawing the items (as we have already done), shuffling them, or even saving them (more on that later).

To link the files, first we need to know how many:

    Private Sub LinkBtn_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles LinkBtn.Click

        Dim numToLink As Integer = ShuffleListBox.SelectedItems.Count

 

Then, we need to iterate through each of those and link them.:

        For index As Integer = 0 To numToLink – 1

            Dim elem As xmlMediaEntry = ShuffleListBox.SelectedItems(index)

 

            elem.NextLinkedElement = If(index <> numToLink – 1,_

 CType(ShuffleListBox.SelectedItems(index + 1), xmlMediaEntry), _

Nothing)

 

            elem.PrevLinkedElement = If(index <> 0, _

CType(ShuffleListBox.SelectedItems(index – 1), _

xmlMediaEntry), Nothing)

 

Note that I am using the If() ternary function above, which takes three arguments.  The first is the condition to test, the second is code to run if the condition is true, and the third is code to run if the condition is false.  In this case, I’m checking to see if we are at the first or last of the selected items, and linking accordingly.  Since header nodes shouldn’t have a “previous” and tail nodes shouldn’t have a “next”, I set these to Nothing under those conditions – otherwise, they are set to reference the adjacent node in the appropriate direction.

Now, I want to physically move the linked tracks together so that they will actually play together.  I’ll get the first one where it is, and move the others up right behind it.  I do this by removing them from the listbox and adding them back in at the right spot, and then remind the listbox that they should still be selected:

            If index <> 0 Then

                ShuffleListBox.Items.RemoveAt(ShuffleListBox.SelectedIndices(index))

                ShuffleListBox.Items.Insert(ShuffleListBox.SelectedIndices(index – 1) + 1, elem)

                ShuffleListBox.SetSelected(ShuffleListBox.SelectedIndices(index – 1) + 1, True)

            End If

        Next

 

Be careful here; we are making two assumptions in all of this.  First, we will be assuming that the list of selected indices is ordered from lowest to highest, and also that the list of selected objects is in the same order as the selected indices (although we could work around the latter point).  These are safe assumptions to make, as near as I can tell.  Second, we need to remember that the contents of SelectedIndices are indices into the Items() collection – that is, the contents of the 0th index of the SelectedIndices might refer to the 4th entry of Items() – that can get confusing.

Anyway, once we’ve moved them together and linked them, we need to tell the listbox to repaint so that it redraws the linked items appropriately.  To do this, we invalidate the control, and then tell it to update the invalidated regions:

        ShuffleListBox.Invalidate()