I have an ItemsControl
which is bound to a list:
<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;
I want to highlight certain text in the TextBlock
in the ItemsControl
. For this, I thought about using a 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);
}
}
How do I find this ThisTextBlock?
CodePudding user response:
First of all, you need to delete the Binding from code behind.
You can do this using Loaded event as follows:
<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);
}
}
}
CodePudding user response:
You need to access the item container's content template (which is the item's DataTemplate
).
In case of the ItemsControl
, you can use the following example to obtain a named element from the 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);
}
A simple implementation that searches an element in the visual tree can be found at How to: Microsoft Docs: How to: Find DataTemplate-Generated Elements. You can copy and use the example's helper method FindVisualChild
to search for elements by type rather than by name. The method is part of an example that shows how to get the content of the DataTemplate
in case you use a ListBox
or ListView
.
In case you didn't modified the ListBoxItem
template or don't expect it to change, you can use this simplified and faster version (to find named elements):
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;
}
Except for special use cases, it is highly recommended to use the ListBox
instead of the ItemsControl
. ListBox
and ListView
are both an extended ItemsControl
. They both provide scrolling and a significantly improved performance.