WPF 如何在具有数据绑定的 ItemsControl 中查找特定控件

WPF How to find a specific control in an ItemsControl with data binding

我有一个绑定到列表的 ItemsControl

<ItemsControl x:Name="icFiles" ItemsSource="{Binding Path=files}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <CheckBox Content="" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
                <TextBlock x:Name="ThisTextBlock" Text="{Binding FileName}" />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
private readonly List<FileModel> files = new();

icFiles.ItemsSource = files;

我想在 ItemsControl 中突出显示 TextBlock 中的某些文本。为此,我考虑过使用 TextPointer:

string? highlightText = "blue";

int highlightTextIndex = ThisTextBlock.Text.IndexOf(highlightText);
if(highlightTextIndex >= 0)
{
    TextPointer textStartPointer = ThisTextBlock.ContentStart.DocumentStart.GetInsertionPosition(LogicalDirection.Forward);
    TextRange? highlightTextRange = new TextRange(textStartPointer.GetPositionAtOffset(highlightTextIndex), textStartPointer.GetPositionAtOffset(highlightTextIndex + highlightText.Length));
                highlightTextRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Blue);
    }
}

如何找到这个 ThisTextBlock

首先,您需要从后面的代码中删除绑定

您可以使用 Loaded 事件执行此操作,如下所示:

 <ItemsControl x:Name="icFiles" ItemsSource="{Binding Path=files}">
     <ItemsControl.ItemTemplate>
         <DataTemplate>
             <StackPanel Orientation="Horizontal">
                 <CheckBox Content="" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
                 <TextBlock Loaded="ThisTextBlock_OnLoaded" x:Name="ThisTextBlock" Text="{Binding FileName}" />
             </StackPanel>
         </DataTemplate>
     </ItemsControl.ItemTemplate>
 </ItemsControl>    
  

 private void ThisTextBlock_OnLoaded(object sender, RoutedEventArgs e)
 {
    if (sender is TextBlock tb)
    {
        string? highlightText = "blue";
        int highlightTextIndex = tb.Text.IndexOf(highlightText);
        if (highlightTextIndex >= 0)
        {
            TextPointer textStartPointer = tb.ContentStart.DocumentStart.GetInsertionPosition(LogicalDirection.Forward);
            TextRange? highlightTextRange = new TextRange(textStartPointer.GetPositionAtOffset(highlightTextIndex), textStartPointer.GetPositionAtOffset(highlightTextIndex + highlightText.Length));
            highlightTextRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Blue);
        }
     }
 }

您需要访问项目容器的内容模板(即项目的 DataTemplate)。

ItemsControl 的情况下,您可以使用以下示例从 DataTemplate 中获取命名元素:


for (int itemIndex = 0; itemIndex < this.ItemsControl.Items.Count; itemIndex++)
{
  var itemContainer = this.ItemsControl.ItemContainerGenerator.ContainerFromIndex(itemIndex) as ContentPresenter;
  var textBlock = itemContainer.ContentTemplate.FindName("ThisTextBlock", itemContainer) as TextBlock;

  HighlightText(textBlock);
}

在如何:Microsoft Docs: How to: Find DataTemplate-Generated Elements 中可以找到在可视化树中搜索元素的简单实现。您可以复制并使用示例的辅助方法 FindVisualChild 按类型而不是按名称搜索元素。该方法是一个示例的一部分,该示例显示了在您使用 ListBoxListView.

的情况下如何获取 DataTemplate 的内容

如果您没有修改 ListBoxItem 模板或不希望它发生变化,您可以使用这个简化和更快的版本(查找命名元素):

for (int itemIndex = 0; itemIndex < this.ListBox.Items.Count; itemIndex++)
{
  var listBoxItemContainer = this.ListBox.ItemContainerGenerator.ContainerFromIndex(itemIndex) as ListBoxItem;
  var templateRootBorder = VisualTreeHelper.GetChild(listBoxItemContainer, 0) as Border;
  var contentHost = templateRootBorder.Child as ContentPresenter;

  var textBlock = contentHost.ContentTemplate.FindName("TD", contentHost) as TextBlock;
}

除特殊情况外,强烈建议使用ListBox而不是ItemsControlListBoxListView 都是扩展的 ItemsControl。它们都提供滚动和显着改进的性能。