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

发布于 2024-10-20 06:22:08 字数 3110 浏览 2 评论 0原文

我有一个 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;

    }

I have a ListBox including an ItemTemplate with 2 StackPanels.
There is a TextBox in the second StackPanel i want to access.
(Change it's visibility to true and accept user input)
The trigger should be the SelectionChangedEvent. So, if a user clicks on an ListBoxItem, the TextBlock gets invisible and the TextBox gets visible.

XAML CODE:

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

I guess there are several ways to solve this, but nothing I tried worked.

My current approach looks like this

    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;

    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

最美的太阳 2024-10-27 06:22:08

谢谢你们的帮助!终于我明白了。解决了 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;
    }

Thank you for your help guys!! Finally i got it. Solved the problem with the VisualTreeHelper. What a great function ^^

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;
    }
绮筵 2024-10-27 06:22:08

使用此编辑功能,您还可以按名称搜索控件(从 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;
}

With this edited function you can also search for control by name (its converted from 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;
}
究竟谁懂我的在乎 2024-10-27 06:22:08

我无法给你一个完整的答案...

但我认为你可以使用 VisualTreeHelper 来迭代任何控件的子级
http://blogs.msdn.com/b/kmahone /archive/2009/03/29/visualtreehelper.aspx

但是,对于您正在寻找的效果,那么我认为使用 SelectedItem 样式可能是一个更好的解决方案 - 例如,请参阅这篇文章 - http://joshsmithonwpf.wordpress.com/2007/07/ 30/自定义列表框中选定的项目/

I can't give you a complete answer...

But I think you can use the VisualTreeHelper to iterate through the children of any control
http://blogs.msdn.com/b/kmahone/archive/2009/03/29/visualtreehelper.aspx

However, for the effect you are looking for, then I think using the SelectedItem Style might be a better solution - e.g. see this article - http://joshsmithonwpf.wordpress.com/2007/07/30/customizing-the-selected-item-in-a-listbox/

陈年往事 2024-10-27 06:22:08

使用 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");
  }
}

Use 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");
  }
}
虐人心 2024-10-27 06:22:08

由于 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(); 

Since DataTemplate is a generic template that could be used many times in the code, there is no way to access it by name (x:Name="numberTextBox").

I solved similar problem to this by making a collection of Controls - while Listbox was populating I add Textbox control to the collection.

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

Till I found a better soultion - Tag property. In your ListboxItem you bind Tag property to the name

Tag="{Binding Name}"

and the to access it

ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem;

string name = listBoxItem.Tag.ToString(); 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文