来自 ItemsControl returns 的 ContentPresenter 空

ContentPresenter from ItemsControl returns null

我想从 ItemsControl 中获取所有 UI 项。

由此post How do I access the children of an ItemsControl? 我复制了一个答案,目前有效。

但是,如果我 DIRECTLY 在设置 ItemsSource 后执行 for 循环中的代码(如底部示例),contentpresenternull,我无法使用它。
如果我 运行 for-loop 相当长一段时间后(也许当我按下按钮时),一切正常。

如何在设置 ItemsSource 后直接访问 ItemsControl 的所有子项?

itemscontrol.ItemsSource = items; // items is a list
itemscontrol.ApplyTemplate(); // might refresh the itemscontrol

for (int i = 0; i < itemscontrol.Items.Count; i++)
{
    //                      ↓ this is null
    ContentPresenter contentpresenter = (ContentPresenter)itemscontrol.ItemContainerGenerator.ContainerFromItem(itemscontrol.Items[i]);
    //                      ↑ this is null

    contentpresenter.ApplyTemplate();

    StackPanel stackPanel = (StackPanel)contentpresenter.ContentTemplate.FindName("selectedStackpanel", contentpresenter);
    // do some stuff with the stackpanel
}

更好的解决方案是将相关属性添加到项目模型中,例如一个 IsUserSelected 属性。然后创建一个 Style,您将其分配给 ItemsControl.ItemContainerStyle。在此 Style 中,您定义了一个在 IsUserSelected.

上触发的触发器

就是这样。不要处理生成器并检查是否生成了每个项目。让框架为您完成这项工作。

 <ListBox ItemsSource="{Binding Items}">
   <ListBox.ItemContainerStyle>
     <Style TargetType="ListBoxItem">

       <Style.Triggers>
         <DataTrigger Binding="{Binding IsUserSelected}"
                      Value="True">
           <Setter Property="Background" Value="Red" />
         </DataTrigger>
       </Style.Triggers>
     </Style>
   </ListBox.ItemContainerStyle>`enter code here`
 </ListBox>

由于您的代码隐藏文件中已有 属性 HighlightId,因此您可以使用 IMultiValueConverterMultiBinding 来定义基于以下颜色的颜色值:

MainWindow.xaml.cs

partial class MainWindow
{
  public static readonly DependencyProperty HighlightIdProperty = DependencyProperty.Register(
    "HighlightId",
    typeof(int),
    typeof(MainWindow),
    new PropertyMetadata(default(int)));

  public int HighlightId
  {
    get => (int) GetValue(MainWindow.HighlightIdProperty);
    set => SetValue(MainWindow.HighlightIdProperty, value);
  }
}

HighlightIdToBrushConverter.cs

public class HighlightIdToBrushConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    if (!(values[0] is MyModelType currentItem 
      && values[1] is int highlightId))
    {
      return Binding.DoNothing;
    }
    
    var highlightBrush = highlightId == currentItem.Id
      ? new SolidColorBrush(Colors.Red)
      : new SolidColorBrush(Colors.Transparent);
      
     highlightBrush.Freeze();
     return highlightBrush; 
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
    throw new NotSupportedException();
}

MainWindow.xaml

<ListBox ItemsSource="{Binding Items}">
  <ListBox.Resources>
    <HighlightIdToBrushConverter x:Key="HighlightIdToBrushConverter" />
  </ListBox.Resources>

  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Setter Property="Background">
        <Setter.Value>
          <MultiBinding Converter="{StaticResource HighlightIdToBrushConverter}">
            <Binding />
            <Binding RelativeSource="{RelativeSource AncestorType=Window}" 
                     Path="HighlightId" />
          </MultiBinding>
        </Setter.Value>
      </Setter>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

也许不是最好的编码风格,但它工作可靠。

public ClassConstructor()
{
    InitializeComponent();
    itemscontrol.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}

...

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
    if (itemscontrol.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
    {
        for (int i = 0; i < itemscontrol.Items.Count; i++)
        {
            ContentPresenter contentpresenter = (ContentPresenter)itemscontrol.ItemContainerGenerator.ContainerFromItem(itemscontrol.Items[i]);
            contentpresenter.ApplyTemplate();
            StackPanel stackPanel = (StackPanel)contentpresenter.ContentTemplate.FindName("selectedStackpanel", contentpresenter);
            int ID = FindIDofSelectedItemFromStackpanel(stackPanel);

            if (highlightID == ID)
            {
                Border border = (Border)contentpresenter.ContentTemplate.FindName("border_models", contentpresenter);
                border.Background = (Brush)FindResource("brushAccent3");
            }
            else
            {
                Border border = (Border)contentpresenter.ContentTemplate.FindName("border_models", contentpresenter);
                border.Background = Brushes.White;
            }
        }
    }
}