如何使用 DataTemplate 访问列表框中的特定项目?

Posted

技术标签:

【中文标题】如何使用 DataTemplate 访问列表框中的特定项目?【英文标题】:How to access a specific item in a Listbox with DataTemplate? 【发布时间】:2011-07-08 01:17:09 【问题描述】:

我有一个 ListBox,包括一个带有 2 个 StackPanel 的 ItemTemplate。 我要访问的第二个 StackPanel 中有一个 TextBox。 (将其可见性更改为 true 并接受用户输入) 触发器应该是 SelectionChangedEvent。因此,如果用户单击 ListBoxItem,TextBlock 将变得不可见,而 TextBox 变得可见。

XAML 代码:

<ListBox Grid.Row="1" Name="ContactListBox" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="Binding Contacts" Margin="0,36,0,0" SelectionChanged="ContactListBox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
                        <toolkit:ContextMenuService.ContextMenu>
                            <toolkit:ContextMenu>
                                <toolkit:MenuItem Header="Edit Contact" Click="ContactMenuItem_Click"/>
                                <toolkit:MenuItem Header="Delete Contact" Click="ContactMenuItem_Click"/>
                            </toolkit:ContextMenu>
                        </toolkit:ContextMenuService.ContextMenu>

                        <Grid>
                            <Rectangle Fill="StaticResource PhoneAccentBrush"
                                           Width="72" Height="72">
                                <Rectangle.OpacityMask>
                                    <ImageBrush ImageSource="/Images/defaultContactImage.png" Stretch="UniformToFill"/>
                                </Rectangle.OpacityMask>
                            </Rectangle>
                        </Grid>
                        <StackPanel>
                            <TextBox Text="Binding Name" TextWrapping="Wrap" Visibility="Collapsed"/>
                            <TextBlock Text="Binding Name" TextWrapping="Wrap" Style="StaticResource PhoneTextExtraLargeStyle" />
                            <TextBlock Text="Binding Number" TextWrapping="Wrap" Margin="12,-6,12,0" Style="StaticResource PhoneTextAccentStyle"/>
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

我想有几种方法可以解决这个问题,但我没有尝试过。

我目前的做法是这样的

    private void ContactListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    
        ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem;

        DataTemplate listBoxTemplate = listBoxItem.ContentTemplate;

        // How to access the DataTemplate content?

        StackPanel outerStackPanel = listBoxTemplate.XXX as StackPanel;

        StackPanel innerStackPanel = outerStackPanel.Children[1] as StackPanel;

        TextBox nameBox = innerStackPanel.Children[0] as TextBox;
        TextBlock nameBlock = innerStackPanel.Children[1] as TextBlock;


        nameBox.Visibility = System.Windows.Visibility.Visible;
        nameBlock.Visibility = System.Windows.Visibility.Collapsed;

    

【问题讨论】:

我喜欢这个解决方案,但是如果我的文本块很少并且我想让可见/折叠不是第一个而是前。 2号还是3号?换句话说如何进入具有指定名称的列表框控件? @sust86 listBoxTemplate.XXX 中的 XXX 是什么? 如果您想按名称进行迭代,请使用此解决方案:***.com/a/1759923/3934111 这是一篇 MSDN 文章,可能也有助于向仍然想知道的任何人解释这些东西:msdn.microsoft.com/en-us/library/bb613579(v=vs.110).aspx 【参考方案1】:

谢谢你们的帮助!!最后我明白了。解决了 VisualTreeHelper 的问题。好厉害的功能^^

private void ContactListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    
        if (ContactListBox.SelectedIndex == -1)
            return;

        currentSelectedListBoxItem = this.ContactListBox.ItemContainerGenerator.ContainerFromIndex(ContactListBox.SelectedIndex) as ListBoxItem;

        if (currentSelectedListBoxItem == null)
            return;

        // Iterate whole listbox tree and search for this items
        TextBox nameBox = helperClass.FindDescendant<TextBox>(currentSelectedListBoxItem);
        TextBlock nameBlock = helperClass.FindDescendant<TextBlock>(currentSelectedListBoxItem);

helperFunction

public T FindDescendant<T>(DependencyObject obj) where T : DependencyObject
    
        // Check if this object is the specified type
        if (obj is T)
            return obj as T;

        // Check for children
        int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
        if (childrenCount < 1)
            return null;

        // First check all the children
        for (int i = 0; i < childrenCount; i++)
        
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child is T)
                return child as T;
        

        // Then check the childrens children
        for (int i = 0; i < childrenCount; i++)
        
            DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i));
            if (child != null && child is T)
                return child as T;
        

        return null;
    

【讨论】:

哦,它应该在这里:我喜欢这个解决方案,但是如果我的文本块很少并且我想让可见/折叠不是第一个而是前。 2号还是3号?换句话说如何进入具有指定名称的列表框控件?【参考方案2】:

使用此编辑功能,您还可以按名称搜索控件(从 VB.NET 转换而来):

public T FindDescendantByName<T>(DependencyObject obj, string objname) where T : DependencyObject

    string controlneve = "";

    Type tyype = obj.GetType();
    if (tyype.GetProperty("Name") != null) 
        PropertyInfo prop = tyype.GetProperty("Name");
        controlneve = prop.GetValue((object)obj, null);
     else 
        return null;
    

    if (obj is T && objname.ToString().ToLower() == controlneve.ToString().ToLower()) 
        return obj as T;
    

    // Check for children
    int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
    if (childrenCount < 1)
        return null;

    // First check all the children
    for (int i = 0; i <= childrenCount - 1; i++) 
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child is T && objname.ToString().ToLower() == controlneve.ToString().ToLower()) 
            return child as T;
        
    

    // Then check the childrens children
    for (int i = 0; i <= childrenCount - 1; i++) 
        string checkobjname = objname;
        DependencyObject child = FindDescendantByName<T>(VisualTreeHelper.GetChild(obj, i), objname);
        if (child != null && child is T && objname.ToString().ToLower() == checkobjname.ToString().ToLower()) 
            return child as T;
        
    

    return null;

【讨论】:

【参考方案3】:

我不能给你一个完整的答案...

但我认为您可以使用 VisualTreeHelper 来遍历任何控件的子项 http://blogs.msdn.com/b/kmahone/archive/2009/03/29/visualtreehelper.aspx

但是,对于您正在寻找的效果,我认为使用 SelectedItem Style 可能是一个更好的解决方案 - 例如看这篇文章-http://joshsmithonwpf.wordpress.com/2007/07/30/customizing-the-selected-item-in-a-listbox/

【讨论】:

感谢您对 VisualTreeHelper 的提示! :) 感谢我...点赞 :) 我真的认为你应该使用 SelectedItem 样式而不是使用代码来实现效果 - 从长远来看,这是最好的方式(我认为)跨度> 我试图...“投票需要 15 声望”好吧,我肯定会学习如何使用 Blend。但我是 Windows Phone 编程领域的新手,仍然试图弄清楚所有这些东西是如何工作的。我只是需要更多时间=) 啊 - 抱歉 - 你必须在网站上停留更长时间,然后再回来 :)【参考方案4】:

使用ItemContainerGenerator

private void ContactListBox_SelectionChanged
  (object sender, SelectionChangedEventArgs e)

  if (e.AddedItems.Count == 1)
  
    var container = (FrameworkElement)ContactListBox.ItemContainerGenerator.
                      ContainerFromItem(e.AddedItems[0]);

    StackPanel sp = container.FindVisualChild<StackPanel>();
    TextBox tbName = (TextBox) sp.FindName("tbName");
    TextBlock lblName = (TextBlock)sp.FindName("lblName");
    TextBlock lblNumber = (TextBlock)sp.FindName("lblNumber");
  

【讨论】:

太好了,我想使用这个解决方案。但是,显然 FrameworkElement 不包含“FindVisualChild”的定义。也许这是因为我正在为 windows phone 7 使用 silverlight?!仍在试图弄清楚如何解决这个问题。也许 VisualTreeHelper 可以帮助 = ) TY 反正【参考方案5】:

由于 DataTemplate 是可以在代码中多次使用的通用模板,因此无法通过名称 (x:Name="numberTextBox") 访问它。

我通过制作控件集合解决了与此类似的问题 - 在填充列表框时,我将文本框控件添加到集合中。

string text = myCollectionOfTextBoxes[listbox.SelectedIndex].Text; 

直到我找到一个更好的灵魂——标签属性。在您的 ListboxItem 中,您将 Tag 属性绑定到名称

Tag="Binding Name"

以及访问它

ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem;

string name = listBoxItem.Tag.ToString(); 

【讨论】:

我不确定我是否完全理解这种方法。但这仅在我需要知道 TextBox / TextBlock 的文本时才有用。使用此解决方案无法聚焦 TextBox 并接受用户输入,对吧? 那么你想拥有Button的行为吗? forums.create.msdn.com/forums/t/69801.aspx 看看这个。

以上是关于如何使用 DataTemplate 访问列表框中的特定项目?的主要内容,如果未能解决你的问题,请参考以下文章

XAML - 如何从 ControlTemplate 中的子项访问父控件的 DataTemplate 属性?

使用 DataTemplate 时,ListView 仅显示列表中的最后一项

如何在复选框事件上访问嵌套listview datatemplate中的标签名称

如何将 ObservableCollection 绑定到 DataTemplate 中的文本框?

如何访问pyspark数据框中的动态列

如何使 Combobox(自定义 DataTemplate)中的 SelectedItem 仅显示项目的特定属性