4

I have a list of hyperlinks that are displayed through an ItemsControl, something like this:

 <ItemsControl x:Name="SubMenu" Visibility="Collapsed">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <HyperlinkButton Content="{Binding Name}"
                                 NavigateUri="{Binding Url}"
                                 TargetName="ContentFrame"
                                 Style="{StaticResource LinkStyle}"
                                 />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Style="{StaticResource LinksStackPanelStyle}"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

what I need to do is enumerate the actual hyperlinks in the submenu, something like this:

    foreach (UIElement child in SubMenu.Items) // this does not work!
    {
        HyperlinkButton hb = child as HyperlinkButton;
        if (hb != null && hb.NavigateUri != null)
        {
            if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
            {
                VisualStateManager.GoToState(hb, "ActiveLink", true);
            }
            else
            {
                VisualStateManager.GoToState(hb, "InactiveLink", true);
            }
        }
    }

The problem is that I can´t seem to find a way to enumerate the actual UI elements in the ItemsCollection.Items.

Anyone know how to do this or a possible workaround?

I can mention that what I´m trying to do is build a menu and submenu that display the hyperlinks clicked as a sort of breadcrumb.

UPDATE: The best thing would be if I could get to that stackpanel somehow because this code seems to work:

    foreach (UIElement child in LinksStackPanel.Children)
    {
        HyperlinkButton hb = child as HyperlinkButton;
        if (hb != null && hb.NavigateUri != null)
        {
            if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
            {
                VisualStateManager.GoToState(hb, "ActiveLink", true);
            }
            else
            {
                VisualStateManager.GoToState(hb, "InactiveLink", true);
            }
        }
    }
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Johan Leino
  • 3,473
  • 1
  • 26
  • 27

4 Answers4

8

The solution looks like this:

foreach (var item in SubMenu.Items)
{
    var hb = SubMenu.ItemContainerGenerator.ContainerFromItem(item).FindVisualChild<HyperlinkButton>();

    if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
    {
        VisualStateManager.GoToState(hb, "ActiveLink", true);
    }
    else
    {
        VisualStateManager.GoToState(hb, "InactiveLink", true);
    }
}

The extension method FindVisualChild:

public static T FindVisualChild<T>(this DependencyObject instance) where T : DependencyObject
{
    T control = default(T);

    if (instance != null)
    {

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(instance); i++)
        {
            if ((control = VisualTreeHelper.GetChild(instance, i) as T) != null)
            {
                break;
            }

            control = FindVisualChild<T>(VisualTreeHelper.GetChild(instance, i));
        }
    }

    return control;

}
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Johan Leino
  • 3,473
  • 1
  • 26
  • 27
2

FindVisualChild from Johan Leino answer has bug: traversing of lower levels in control hierarchy does not have any effect because he don't check result of recursive call.

That is fixed version.

public static T FindVisualChild<T>(this DependencyObject instance) where T : DependencyObject
{
    T control = default(T);

    if (instance != null)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(instance); i++)
        {
            if ((control = VisualTreeHelper.GetChild(instance, i) as T) != null)
            {
                break;
            }

            if ((control = FindVisualChild<T>(VisualTreeHelper.GetChild(instance, i))) != null)
            {
                break;
            }
        }
    }

    return control;
}
Hinidu
  • 31
  • 1
2

Try using the ItemContainerGenerator.ContainerFromItem method

foreach (var item in SubMenu.Items)
{ 
    var child = SubMenu.ItemContainerGenerator.ContainerFromItem(item);
    HyperlinkButton hb = child as HyperlinkButton; 
    // use hb
}
bendewey
  • 39,709
  • 13
  • 100
  • 125
  • +1 You were just about right and helped me along the way. I have solved it a little bit differently. See my own answer to the question. Thanks! – Johan Leino Feb 26 '10 at 08:36
1

Try this:

foreach (UIElement child in SubMenu.Items.OfType<UIElement>())

This is using the Enumerable.OfType<TResult> extension method that filters the collection down to only those items that are of the specified type.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • No, sorry that does not work. It seems that elements in Items are not the actual UI elements. Items contains typeof(MenuItem) which is the business object that I use to bind to the collection. – Johan Leino Feb 25 '10 at 19:55