从列表框中获取Listboxitem

时间:2014-03-19 15:21:13

标签: c# wpf listbox

嗨,这应该是简单的,但我不知道我做错了什么。我一直在网上看到人们都在做这项工作,即使按照MSDN上的教程,我仍然没有任何工作。

我想迭代一个ListBox,并获取ListBoxItems,这样我就可以找到我添加到它的DataTemplate。

这是我背后的代码。

private void SetListBoxDataTemplate(ListBox MyListBox)
{
  try
  {
    foreach (CustomDataTemplateObject dataobject in MyListBox.Items)
    {
      ListBoxItem lbi = (ListBoxItem)(MyListBox.ItemContainerGenerator.ContainerFromItem(dataobject));
      ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(lbi);
      DataTemplate dt = myContentPresenter.ContentTemplate;
      TextBlock tb = (TextBlock)dt.FindName("ListBoxItemTextBlock1", myContentPresenter);
      ComboBox cb = (ComboBox)dt.FindName("ListBoxItemComboBox1", myContentPresenter);

      tb.Text = dataobject.Text;
      cb.ItemsSource = dataobject.ListColors;
    }
  }
  catch (Exception ex)
  {
    MessageBox.Show(""+ex);
  }
}

XAML看起来像这样:

 <DataTemplate x:Key="ListBoxItemDataTemplate1">
        <StackPanel Orientation="Horizontal">
            <Border BorderBrush="Black" BorderThickness="1 1 0 1" MinWidth="50">
                <TextBlock Name="ListBoxItemTextBlock1" Background="{Binding ElementName=ListBoxItemComboBox1, Path=SelectedValue}" >
                </TextBlock>
            </Border>
            <ComboBox Name="ListBoxItemComboBox1" />
        </StackPanel>
    </DataTemplate>*

 <StackPanel>
    <ListBox Name="ListBoxTest1" ItemTemplate="{DynamicResource ListBoxItemDataTemplate1}" />
</StackPanel>

我已经尝试将我的itemtemplate设置为static以查看它是否有效,并且我已经填充了我的ListBoxs后调用了后面代码调用的方法

我的dataobject不为null,但是当我在我的代码中调用该行后,我的lbi最终为null。

有什么建议吗?提前谢谢!

第一次更新

只有在我的构造函数中调用方法时才会出现此问题,所以可能是因为它还没有初始化完整的组元素部分。但是我希望尽快这样做。我可能被迫在WindowLoaded事件中这样做吗?

第二次更新

代码更新,Rachel的答案适用于迭代我的ListBoxItems,但是Listbox还没有完全呈现,因为我此时无法访问Datatemplate。所以MyListBox_GeneratorStatusChanged不能解决这个问题,但它确实得到了ListBoxItems。

2 个答案:

答案 0 :(得分:3)

WPF的主线程在不同的priority levels运行项目。在构造函数中运行的代码都以Normal优先级运行,而渲染ListBox及其项目的事件在Render优先级运行,这在Normal之后发生优先业务已经完成。

这意味着您的整个构造函数会在您SetListBoxDataTemplate()呈现并生成项目之前运行(包括ListBox)。

如果要在生成项目后运行某些代码,请使用ItemsContainerGenerator.StatusChanged事件

// Constructor
MyListBox.ItemContainerGenerator.StatusChanged += MyListBox_GeneratorStatusChanged;

...

void MyListBox_GeneratorStatusChanged(object sender, EventArgs e)
{
    // return if containers have not been generated yet
    if (MyListBox.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
        return;

    // remove event
    MyListBox.ItemContainerGenerator.StatusChanged -= MyListBox_GeneratorStatusChanged;

    // your items are now generated
    SetListBoxDataTemplate(MyListBox);
}

你还想用这种方法完成什么? WPF有点不寻常,可能有更好的WPF方式来完成你的任务。

根据添加到问题

的新代码进行了更新

设置TextItemsSource属性的更好方法是使用WPF的数据绑定。

您的DataTemplate应如下所示:

<DataTemplate x:Key="ListBoxItemDataTemplate1">
    <StackPanel Orientation="Horizontal">
        <Border BorderBrush="Black" BorderThickness="1 1 0 1" MinWidth="50">
            <TextBlock Text="{Binding Text}" Background="{Binding ElementName=ListBoxItemComboBox1, Path=SelectedValue}" >
            </TextBlock>
        </Border>
        <ComboBox ItemsSource="{Binding ListColors}" />
    </StackPanel>
</DataTemplate>*

DataTemplate就像一个曲奇饼干。它用于制作UI对象,但不是UI对象本身的一部分。它只是告诉WPF “当你去渲染这个对象时,使用这个XAML渲染它”。所以XAML的呈现方式是

<ListBoxItem>
    <StackPanel>
        <Border>
            <TextBlock Text="{Binding Text}" />
        </Border>
        <ComboBox ItemsSource="{Binding ListColors}">
    </StackPanel> 
</ListBoxItem>

此外,DataContext后面的ListBoxItem是绑定到ListBox.ItemsSource的集合中的项目,根据您的代码,CustomDataTemplateObject应为DataContext。这允许DataTemplate的绑定工作

如果您是WPF的新手,并且努力了解DataContext的确切运作方式,我建议您阅读我的这篇文章:What is this "DataContext" you speak of?

总而言之,WPF有两个应用层:UI层和数据层(ListBoxItem)。当您执行上述基本绑定时,您将数据从数据层提取到UI层。

因此,您的CustomDataTemplateObject数据层为TextBlock.TextComboBox.ItemsSource和{{1}}绑定正在从数据层提取数据,以便在UI层中使用。< / p>

我还强烈建议使用像Snoop这样的实用程序,它允许您查看正在运行的WPF应用程序的整个Visual Tree,以查看项目的呈现方式。它非常适用于调试或了解有关WPF如何工作的更多信息。

答案 1 :(得分:2)

你混淆了两个工作并将它们合二为一。首先,访问ListBoxItem

private void SetListBoxDataTemplate(ListBox MyListBox)
{
    foreach (ListBoxItem listBoxItem in MyListBox.Items)
    {
    }
}

现在,您可以从DataTemplate获取ListBoxItem

foreach (ListBoxItem listBoxItem in MyListBox.Items)
{
    ContentPresenter presenter = FindVisualChild<ContentPresenter>(listBoxItem);
    DataTemplate dataTemplate = presenter.ContentTemplate;
    if (dataTemplate != null)
    {
        // Do something with dataTemplate here
    }
}

可以在MSDN上的How to: Find DataTemplate-Generated Elements页面中找到FindVisualChild方法。


更新&gt;&gt;&gt;

要回答您的编辑,是的,构造函数为时太早,无法尝试访问这些DataTemplate,因为框架当时还没有将它们应用于所有对象。最好使用FrameworkElement.Loaded Event来执行这些操作,因为这是控件全部初始化后可以调用的第一个事件。