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
按类型而不是按名称搜索元素。该方法是一个示例的一部分,该示例显示了在您使用 ListBox
或 ListView
.
的情况下如何获取 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
而不是ItemsControl
。 ListBox
和 ListView
都是扩展的 ItemsControl
。它们都提供滚动和显着改进的性能。
我有一个绑定到列表的 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
按类型而不是按名称搜索元素。该方法是一个示例的一部分,该示例显示了在您使用 ListBox
或 ListView
.
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
而不是ItemsControl
。 ListBox
和 ListView
都是扩展的 ItemsControl
。它们都提供滚动和显着改进的性能。