简体   繁体   English

如何获取ListBox的子控件?

[英]How do I get the child controls of a ListBox?

I have a ListBox in xaml which has a sub-ListBox inside the top-level ListBox's item template. 我在xaml中有一个ListBox,它在顶级ListBox的项模板中有一个子ListBox。 Because the sub-ListBox is multi-select, and I can't bind the SelectedItems of the sub-ListBox to a viewmodel property for some reason, I'm trying to do a lot of this in the view code-behind. 因为子ListBox是多选的,并且由于某种原因我无法将子ListBox的SelectedItems绑定到viewmodel属性,所以我试图在视图代码隐藏中执行大量此操作。

I have everything working, except for one snag: I want to select all items in each sub-ListBox by default. 我有一切工作,除了一个障碍:我想默认选择每个子ListBox中的所有项目。 Since the SelectedItems aren't data-bound, I'm trying to do it manually in code whenever a SelectionChanged event fires on the top-level ListBox. 由于SelectedItems不是数据绑定的,所以每当SelectionChanged事件在顶级ListBox上触发时,我都会尝试在代码中手动执行。 The problem is that I don't know how to get from the top-level ListBox to the sub-ListBox of the top-level selected item. 问题是我不知道如何从顶级ListBox到顶级选定项的子ListBox。 I think I need to use the visual tree, but I don't know how to even get the dependency object that corresponds to the selected item. 我想我需要使用可视化树,但我不知道如何获得与所选项对应的依赖项对象。

Here's the code: 这是代码:

<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>

The converter ensures that only the selected top-level item has a visible sub-ListBox, and that's working. 转换器确保只有选定的顶级项具有可见的子ListBox,并且这是有效的。

I need to implement the following method: 我需要实现以下方法:

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

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

I tried doing stuffListBox.ItemContainerGenerator.ContainerFromItem(stuff) , but that always returns null. 我尝试做stuffListBox.ItemContainerGenerator.ContainerFromItem(stuff) ,但总是返回null。 Even stuffListBox.ItemContainerGenerator.ContainerFromIndex(0) always returns null. 即使stuffListBox.ItemContainerGenerator.ContainerFromIndex(0)始终返回null。

I also get strange behaviour from the selection changed method. 我也从选择改变的方法得到奇怪的行为。 ' e.AddedItems will contain items, but stuffListBox.SelectedItem is always null. ' e.AddedItems将包含项,但stuffListBox.SelectedItem始终为null。 Am I missing something? 我错过了什么吗?

From what I've read, my problem is coming from the fact that the containers haven't been generated at the time that I'm getting a selection change event. 从我读过的内容来看,我的问题来自于在我收到选择更改事件时尚未生成容器这一事实。 I've seen workarounds that involve listening for a the item container generator's status changed event, but I'm working in Silverlight and don't have access to that event. 我见过涉及监听项容器生成器状态更改事件的变通方法,但我在Silverlight中工作并且无法访问该事件。 Is what I'm doing just not possible in Silverlight due to the oversight of making SelectedItems on a ListBox read-only? 由于忽略了将ListBox上的SelectedItems设为只读,我在Silverlight中所做的事情是不可能的吗?

Like you say this is probably best done in the ViewModel but you can select all the sub list items in code behind using VisualTreeHelper. 就像你说的那样,最好在ViewModel中完成,但你可以使用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();
}

FindVisualChild Method as per MSDN 根据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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM