Enumerate ItemsControl.Items as UIElements
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 Link开发者_如何学运维Style}"
/>
</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);
}
}
}
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;
}
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
}
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;
}
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.
精彩评论