如何获得 ListBox 的子控件?

How do I get the child controls of a ListBox?

我在 xaml 中有一个 ListBox,它在顶级 ListBox 的项目模板中有一个子 ListBox。因为子列表框是多 select,而且出于某种原因我无法将子列表框的 SelectedItems 绑定到视图模型 属性,我正在尝试做很多这样的事情视图代码隐藏。

我一切正常,除了一个障碍:我想 select 默认情况下每个子列表框中的所有项目。由于 SelectedItems 不是数据绑定的,因此每当在顶级 ListBox 上触发 SelectionChanged 事件时,我都尝试在代码中手动执行此操作。问题是我不知道如何从顶级 ListBox 到顶级 selected 项目的子 ListBox。我想我需要使用可视化树,但我什至不知道如何获得对应于 selected 项目的依赖对象。

代码如下:

<ListBox ItemsSource="{Binding Path=Stuff}" SelectionChanged="StuffListBox_SelectionChanged" SelectedItem="{Binding Path=SelectedStuff, Mode=TwoWay}" telerik:RadDockPanel.Dock="Bottom">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <telerik:RadDockPanel>
            <TextBlock Text="{Binding Path=Name}" telerik:RadDockPanel.Dock="Top" />
                <ListBox ItemsSource="{Binding Path=SubStuff}" SelectionMode="Multiple" SelectionChanged="SubStuffListBox_SelectionChanged" Visibility="{Binding Converter={StaticResource StuffToSubStuffVisibilityConverter}}" telerik:RadDockPanel.Dock="Bottom">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Name}" />
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </telerik:RadDockPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

转换器确保只有 selected 的顶级项目有一个可见的子列表框,并且这是有效的。

我需要实现以下方法:

private void StuffListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ListBox stuffListBox = (ListBox)sender;

    foreach (Stuff stuff in e.AddedItems)
    {
        ...
        subStuffListBox.SelectAll();
    }
}

我试过 stuffListBox.ItemContainerGenerator.ContainerFromItem(stuff),但总是 returns 无效。即使 stuffListBox.ItemContainerGenerator.ContainerFromIndex(0) 总是 returns null.

我也从 selection changed 方法中得到奇怪的行为。 'e.AddedItems 将包含项目,但 stuffListBox.SelectedItem 始终为空。我错过了什么吗?

据我了解,我的问题是在我收到 selection 更改事件时容器尚未生成。我已经看到涉及侦听项目容器生成器的状态更改事件的解决方法,但我在 Silverlight 中工作并且无权访问该事件。由于疏忽了将 ListBox 上的 SelectedItems 设置为只读,我在 Silverlight 中所做的事情是不可能的吗?

就像你说的,这可能最好在 ViewModel 中完成,但你可以 select 使用 VisualTreeHelper 隐藏代码中的所有子列表项。

private void StuffListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   var stuffListBox = (ListBox)sender;
        ListBoxItem item = (ListBoxItem)stuffListBox.ContainerFromItem(stuffListBox.SelectedItem);
        ListBox sublist = FindVisualChild<ListBox>(item);
        sublist.SelectAll();
}

根据 MSDN

的 FindVisualChild 方法
private childItem FindVisualChild<childItem>(DependencyObject obj)
    where childItem : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
       DependencyObject child = VisualTreeHelper.GetChild(obj, i);
       if (child != null && child is childItem)
          return (childItem)child;
       else
       {
          childItem childOfChild = FindVisualChild<childItem>(child);
          if (childOfChild != null)
            return childOfChild;
       }
 }
 return null;
}