简体   繁体   English

从列表框中获取Listboxitem

[英]Get Listboxitem from listbox

Hi this should be faily simple, however I don't know what I am doing wrong. 嗨,这应该很简单,但是我不知道我做错了什么。 I've been looking all over the internet seeing people make this work, even followed the tutorial on MSDN still nothing has worked for me. 我一直在互联网上四处寻找,看到人们在做这项工作,即使遵循MSDN上的教程,对我来说仍然没有任何效果。

I want to Iterate over a ListBox, and get the ListBoxItems so I can find the DataTemplate that I have added to it. 我想遍历ListBox,并获取ListBoxItems,以便可以找到已添加到其中的DataTemplate。

This is my code behind. 这是我的代码背后。

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 looks like this: 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>

I have tried with setting my itemtemplate to static to see if it works, and the method i'm calling from code behind, is called after I have populated my ListBoxs 我尝试将itemtemplate设置为static以查看其是否有效,并且在填充列表框后调用了我从后面的代码调用的方法

My dataobject is NOT null, however when i call the line in my code behind, my lbi, ends up being null. 我的dataobject不为null,但是,当我在后面的代码中调用该行时,我的lbi最终为null。

Any suggestions? 有什么建议么? thanks in advance! 提前致谢!

FIRST UPDATE 第一次更新

This problem only occurs if i call the method in my constructor, so perhaps it's because it hasn't initialized the full group element section yet. 仅当我在构造函数中调用该方法时,才会出现此问题,这也许是因为它尚未初始化完整的group element部分。 However I want to do this as soon as possible. 但是,我想尽快这样做。 Am I perhaps forced to do it in a WindowLoaded event? 我是否可能被迫在WindowLoaded事件中执行此操作?

SECOND UPDATE 第二次更新

Code updated, Rachel's answer worked for iterating over my ListBoxItems, however the Listbox Has not fully rendered since i'm unable to reach the Datatemplate at this time. 代码已更新,Rachel的答案可用于遍历我的ListBoxItems,但是由于我目前无法访问Datatemplate,因此Listbox尚未完全呈现。 So MyListBox_GeneratorStatusChanged is not working for this problem, but it does get the ListBoxItems. 因此MyListBox_GeneratorStatusChanged不适用于此问题,但它确实获取了ListBoxItems。

WPF's main thread runs items at different priority levels . WPF的主线程以不同的优先级运行项目。 Code that runs in the Constructor all gets run at Normal priority, while things like rendering the ListBox and it's items run at the Render priority level, which occurs after all Normal priority operations have finished. 在构造函数中运行的代码均以“ Normal优先级运行,而诸如渲染ListBox及其项之类的操作则以“ Render优先级运行,该级别在所有“ Normal优先级操作完成之后发生。

This means that your entire Constructor gets run (including SetListBoxDataTemplate() ) before your ListBox is even rendered and the items get generated. 这意味着您的整个构造方法(包括SetListBoxDataTemplate() )在呈现ListBox和生成项目之前SetListBoxDataTemplate()运行。

If you want to run some code after the items are generated, use the ItemsContainerGenerator.StatusChanged event 如果要在生成项目后运行一些代码,请使用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);
}

What are you trying to accomplish with this method anyways? 无论如何,您正在尝试使用该方法完成什么? It is a bit unusual for WPF, and there may be a much better WPF way of accomplishing your task. 对于WPF来说有点不寻常,并且可能有一种更好的WPF方法来完成您的任务。

Updated based on new code added to Question 根据添加到问题的新代码进行更新

A much better method of setting your Text and ItemsSource properties is to make use of WPF's data bindings. 设置TextItemsSource属性的一种更好的方法是利用WPF的数据绑定。

Your DataTemplate should look like this: 您的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>*

A DataTemplate is like a cookie cutter. DataTemplate就像千篇一律。 It's used to make the UI objects, but is not part of the UI object itself. 它用于制作UI对象,但不属于UI对象本身。 All it does is tell WPF that "When you go to render this object, render it using this XAML" . 它所做的只是告诉WPF: “当您渲染该对象时,请使用此XAML进行渲染” So the way your XAML gets rendered is 因此,呈现XAML的方式是

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

In addition, the DataContext behind your ListBoxItem is the item from the collection bound to ListBox.ItemsSource , which based on your code should be CustomDataTemplateObject . 此外, ListBoxItem后面的DataContext是绑定到ListBox.ItemsSource的集合中的项目,该项目基于您的代码应该是CustomDataTemplateObject That allows the bindings from the DataTemplate to work 这样就可以使用DataTemplate进行绑定

If you're new to WPF and struggling to understand how exact the DataContext works, I'd recommend reading this article of mine: What is this "DataContext" you speak of? 如果您是WPF的新手,并且努力了解DataContext工作原理,我建议阅读我的这篇文章: 您所说的“ DataContext”是什么? .

To summarize, WPF has two layers to an application: the UI layer and the Data Layer ( DataContext ). 总而言之,WPF对应用程序具有两层:UI层和数据层( DataContext )。 When you perform a basic binding like above, you are pulling data from the data layer into the UI layer. 当您执行上述基本绑定时,您正在将数据从数据层拉到UI层。

So your ListBoxItem has a data layer of CustomDataTemplateObject , and the TextBlock.Text and ComboBox.ItemsSource bindings are pulling data from the data layer for use in the UI layer. 因此,您的ListBoxItem具有数据层CustomDataTemplateObject ,并且TextBlock.TextComboBox.ItemsSource绑定正在从数据层提取数据以用于UI层。

I'd also highly recommend using a utility like Snoop which lets you view the entire Visual Tree of a running WPF application to see how items get rendered. 我也强烈建议您使用Snoop之类的实用程序,该实用程序可让您查看正在运行的WPF应用程序的整个可视树,以查看项目的呈现方式。 Its very useful for debugging or learning more about how WPF works. 它对于调试或了解WPF的工作原理非常有用。

You're confusing two jobs and mixing them into one. 您会混淆两个工作并将它们混合在一起。 First, get access to the ListBoxItem : 首先,访问ListBoxItem

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

Now you can get the DataTemplate from the ListBoxItem : 现在您可以从ListBoxItem获取DataTemplate

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

The FindVisualChild method can be found in the How to: Find DataTemplate-Generated Elements page on MSDN. 可以在MSDN上的“ 如何:查找由DataTemplate生成的元素”页中找到FindVisualChild方法。


UPDATE >>> 更新>>>

To answer your edit, yes, the constructor will be too early to try to access these DataTemplate s because the Framework won't have applied them to all of the objects by then. 要回答您的编辑,是的,构造函数现在尝试访问这些DataTemplate为时过早,因为到那时DataTemplate Framework不会将它们应用于所有对象。 It is best to use the FrameworkElement.Loaded Event to do these kinds of things, as that is the first event that can be called after the controls have all been initialised. 最好使用FrameworkElement.Loaded事件来执行此类操作,因为这是在控件全部初始化可以调用的第一个事件。

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

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