嗨,这应该是简单的,但我不知道我做错了什么。我一直在网上看到人们都在做这项工作,即使按照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。
答案 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方式来完成你的任务。
根据添加到问题
的新代码进行了更新设置Text
和ItemsSource
属性的更好方法是使用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.Text
,ComboBox.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来执行这些操作,因为这是控件全部初始化后可以调用的第一个事件。