4

XAML

<ListBox x:Name="lsbQueue" Margin="0,0,0,10" Grid.RowSpan="2" Loaded="lsbQueue_Loaded" SelectionChanged="lsbQueue_SelectionChanged" ItemContainerStyle="{StaticResource ListBoxItemStyle1}" ItemsSource="{Binding}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel x:Name="stk" Orientation="Vertical">
                <!-- This is the bugger which I need to access behind the scenes-->
                <TextBlock x:Name="tbActive" FontSize="35" FontFamily="Segoe UI Symbol" Text="" Height="115" Margin="0,0,0,-110" Tag="Active"/>
                <!-- -->
                <TextBlock Text="{Binding Path=SongName}" FontSize="35" Width="388" FontWeight="Normal" Margin="60,0,0,0"/>
                <TextBlock Width="390" FontWeight="Thin" Margin="60,-5,0,10" Opacity="0.55">
                            <Run Text="{Binding Artist}" />
                            <Run Text=", " /> <!-- space -->
                            <Run Text="{Binding Album}" />
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

The above is my Listbox which is the populated from code behind with the help of this:

C#

void GetQueue()
{
    var songs = new List<song>();

    for (int i = 0; i < MediaPlayer.Queue.Count; i++)
    {
        songs.Add(new song {
            SongName = MediaPlayer.Queue[i].Name.ToString(),
            Album = MediaPlayer.Queue[i].Album.Name.ToString(),
            Artist = MediaPlayer.Queue[i].Artist.ToString()
        });

    }
    lsbQueue.ItemsSource = songs.ToList();
    //lsbQueue.SelectedValue.ToString();
    GlobalVars._song = MediaPlayer.Queue.ActiveSongIndex;
    lsbQueue.SelectedIndex = GlobalVars._song;
    // .......
}

and

public class song
{
    public string SongName { get; set; }
    public string Album { get; set; }
    public string Artist { get; set; }
}

public class Song : List<song>
{
    public Song()
    {
        Add(new song { 
            SongName = "", 
            Album = "",
            Artist = ""
        });
    }
}

I have tried using VisualTreeHelper and other extension methods which can be found here:

GeekChamp

Falafel Blog

But I've had no success. I've almost given up on this. Does anyone have any ideas what can be done. Thank you.

enter image description here

As you can see - I can successfully get the Media Queue but I would like to show a visual hint on the left hand side of the "SelectedItem" like the playing character in the TextBlock - tbActive. Hope this helps!

  • You need to follow GeekChamp's tutorial to the letter. See solution. – Chubosaurus Software Sep 23 '14 at 01:16
  • Hi. Sorry If I haven't been able to let you know what I need. Actually, I have been able to successfully get the Media Queue (the songs currently in the queue). But, I would like to show a now playing symbol - next to the "Active Song", and the `tbActive` is exactly that! I was looking forward to get access to tbActive in a hope to change its `Text` property - but only of the `SelectedItem` or `SelectedIndex`. Thank you. – CriticalError Sep 23 '14 at 09:03
  • Yeah, the solution with my name on it does exactly what you want. :) – Chubosaurus Software Sep 23 '14 at 09:19

3 Answers3

1

Since the <TextBlock> is the first entry in the DataTemplate that you're trying to access use the provided function from the GeekChamp's tutorial.

<ListBox x:Name="lb" SelectionChanged="lb_SelectionChanged"/>

// namespaces
using System.Windows.Media;

private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
    var count = VisualTreeHelper.GetChildrenCount(parentElement);
    if (count == 0)
        return null;

    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(parentElement, i);

        if (child != null && child is T)
        {
            return (T)child;
        }
        else
        {
            var result = FindFirstElementInVisualTree<T>(child);
            if (result != null)
                return result;
        }
    }
    return null;
}

private void lb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // get the ListBoxItem by SelectedIndex OR index number
    //ListBoxItem lbi = (ListBoxItem) this.lb.ItemContainerGenerator.ContainerFromIndex(lb.SelectedIndex);

    // get the ListBoxItem by SelectedItem or object in your ViewModel
    ListBoxItem lbi = (ListBoxItem)this.lb.ItemContainerGenerator.ContainerFromItem(lb.SelectedItem);

    // get your textbox that you want
    TextBlock tbActive= FindFirstElementInVisualTree<TextBlock>(lbi);
}
Chubosaurus Software
  • 8,133
  • 2
  • 20
  • 26
  • :( It didn't work. I set a Breakpoint and `lbi` is null and therefore even the TextBlock is null :( – CriticalError Sep 23 '14 at 10:21
  • And what number is the selected index? Also if you use selected item, what does it equal to at the breakpoint? – Chubosaurus Software Sep 23 '14 at 10:31
  • Selected Index is the currently playing song which I get from `MediaPlayer.Queue.ActiveSongIndex`. Please have a look at the Image which I've just added into my question. Thanks. – CriticalError Sep 23 '14 at 10:46
  • Ok we are getting there. Index and selected item must come from the collection you assign as the ItemsSource. Try a constant 0 value, does it at least get the textblock of the first entry of the list box? – Chubosaurus Software Sep 23 '14 at 11:17
  • It did get the TextBlock! But why does it work when I set the Index explicitly and not when it is specified by the MediaPlayer? :( – CriticalError Sep 23 '14 at 11:19
  • 1
    Because the SelectedIndex that you must input must be the index valued of ItemsSource...in your case, your `songs.ToList()` and not some random list you made after the fact. Your ItemsSource determines the output of the listbox, so when you want anything from it you got to use the same list. – Chubosaurus Software Sep 23 '14 at 11:23
  • So if you store the index number of the song in the `songs.ToList()`, you can past that as the value. You can also pass the actual `song` class as well using the other function. But the song has to be also in the ItemsSource. – Chubosaurus Software Sep 23 '14 at 11:27
  • So how do I display the now playing icon in the ListBoxItem? I was using the logic of getting the `ActiveSongIndex` as the `SelectedIndex` and then getting access to `tbActive` and displaying the character. Now that it's not possivle. What option do I have? – CriticalError Sep 23 '14 at 11:29
  • Like my answer said, just past the YOUR_Listbox.SelectedIndex to the function, it will always reference which one has focused. You will get the TextBlock of the item that has focused at that point. – Chubosaurus Software Sep 23 '14 at 11:31
  • Thanks for helping. I do understand what you mean. But getting the Index to be the Active Song still results in the `lbi` being null unless explicitly stated or selected by the user. Which is not really what is my requirement :( – CriticalError Sep 23 '14 at 11:34
  • Yes, then if you want to do some queue type system. You need to store the index value of the ItemsSource, not the index value of your queue. For example, if the user picks item 2 of total 10. Your queue needs to store the values 2,3,4,...9. Then you can past those values to the function. So first pick 2, after that song plays, pick 3.. etc. The way you're doing it now, there must be a mismatch of values between the two list. Basically your queue either references an index out of range of 0-9 in the example case. – Chubosaurus Software Sep 23 '14 at 11:41
1

The above answer will throw an Exception - just like Chubosaurus Software suggested the SelectedItem will be a 'Song' and thefore the TextBlock will also be a null. And it won't work.

0

You can try get StackPanel from ListBox's Selected Item using as operator and then use Children property with indexer to get to your TextBlock.

StackPanel temp = lsbQueue.SelectedItem as StackPanel;
var textBlock = temp.Children[0] as TextBlock;

What exactly do you want to accomplish? Maybe another Binding + possible ValueConverter would be way better solution...

Filip
  • 1,824
  • 4
  • 18
  • 37