Как получить доступ к TextBlock, который находится внутри DataTemplate моего ListBox (но не Binding) в XAML?

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>

Выше мой список, который заполняется из кода с помощью этого:

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;
    // .......
}

а также

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 = ""
        });
    }
}

Я пытался использовать VisualTreeHelper и другие методы расширения, которые можно найти здесь:

GeekChamp

Блог Фалафель

Но у меня не было успеха. Я почти отказался от этого. У кого-нибудь есть идеи, что можно сделать. Спасибо.

Как вы можете видеть - я могу успешно получить Media Queue, но я хотел бы показать визуальную подсказку в левой части "SelectedItem", как игровой персонаж в TextBlock - tbActive. Надеюсь это поможет!

3 ответа

Решение

Так как <TextBlock> это первая запись в DataTemplate, к которой вы пытаетесь получить доступ, используя предоставленную функцию из учебника GeekChamp.

<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 предложила SelectedItem будет "Песня", и поэтому TextBlock также будет null, И это не сработает.

Вы можете попробовать получить StackPanel из выбранного элемента ListBox с помощью оператора as, а затем использовать свойство Children с индексатором, чтобы добраться до вашего TextBlock.

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

Что именно вы хотите достичь? Может быть, другой Binding + возможный ValueConverter будет лучшим решением...

Другие вопросы по тегам