【问题标题】:WPF Listbox highlight part of ListBoxItem elementWPF Listbox 突出显示 ListBoxItem 元素的一部分
【发布时间】:2010-10-22 08:46:01
【问题描述】:

我有一个文本框和列表框。用户可以从 TextBox 中搜索 ListBox 元素。

ListBox 绑定到 CollectionViewSource。

CollectionViewSource 具有 Filter 事件处理程序,它根据用户输入到 TextBox 中的文本过滤元素。

我的要求是在 ListBoxItem 元素的 TextBlock 中突出显示用户输入的文本。

我正在考虑将 TextBlock 分解为几个 Runs 对象并修改需要突出显示的 Run 对象的 Background 属性。

我认为不可能使用 DataTemplates。

有没有简单的方法可以做到这一点?

谢谢!

【问题讨论】:

  • 你能提供更多关于“文本部分”的细节吗?它是匹配整个文本块还是文本块中的文本?如果您尝试匹配文本块的某些部分并突出显示它,那么如果不使用文本运行而不是文本块,您将无法做到这一点。请提供更多详细信息,也许是您的 xaml 示例。
  • 我已经编辑了我的问题。请看看,让我知道。谢谢你的努力:)

标签: .net wpf xaml listbox highlighting


【解决方案1】:

更新:我在this blog post 中详细阐述了这个主题。

我不认为有任何简单的方法可以做到这一点,但我将如何解决这个问题:

  1. 为列表中的项目定义视图模型。它应该包含一些属性来显示文本并定义应该突出显示文本的哪一部分(基本上是开始和结束索引)。
  2. 当用户在搜索框中输入文本时,查看视图模型并检查文本中的匹配项。如果找到匹配项,请适当设置索引。如果未找到匹配项,请将索引设置回 -1 或任何表示不匹配的值。
  3. 在您看来,将TextBlocks 的Background 设置为索引。使用转换器将索引转换为两个索引之间的GradientBrush,即亮黄色(或其他)。

我认为您可以通过以下方式找出TextBlock 突出显示部分的尺寸:

  1. 通过TextBlock.ContentStart 属性获取TextPointer
  2. 使用LogicalDirection.Forwards 调用TextPointer.GetPositionAtOffset(indexOfStart) 移动到选择的开头。
  3. 使用LogicalDirection.Backwards 调用TextPointer.GetPositionAtOffset(indexOfStart) 移动到选择的末尾。
  4. 调用TextPointer.GetCharacterRect获取突出显示内容的边界Rectangle

说实话,我不确定最后一点是否有效。我必须自己尝试一下,我可能会为博客文章这样做。

编辑:刚刚有时间亲自尝试一下。它确实有效,尽管我上面的逻辑略有改变。下面是演示的代码。这是截图:

Screenshot http://img219.imageshack.us/img219/2969/searchx.png

Window1.xaml

<Window x:Class="TextSearch.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1">
    <StackPanel>
        <TextBox x:Name="_searchTextBox"/>
        <Grid>
            <Path Fill="Yellow" Stroke="Black" StrokeThickness="0">
                <Path.Data>
                    <RectangleGeometry x:Name="_rectangleGeometry"/>
                </Path.Data>
            </Path>
            <TextBlock x:Name="_textBlock">Some sample text that you can search through by typing in the above TextBox.</TextBlock>
        </Grid>
    </StackPanel>
</Window>

Window1.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace TextSearch
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            _searchTextBox.TextChanged += _searchTextBox_TextChanged;
        }

        void _searchTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            var searchText = _searchTextBox.Text;
            var index = _textBlock.Text.IndexOf(searchText);

            if (index == -1)
            {
                _rectangleGeometry.Rect = Rect.Empty;
            }
            else
            {
                var textPointer = _textBlock.ContentStart;
                textPointer = textPointer.GetPositionAtOffset(index + 1, LogicalDirection.Forward);
                var leftRectangle = textPointer.GetCharacterRect(LogicalDirection.Forward);
                textPointer = textPointer.GetPositionAtOffset(searchText.Length, LogicalDirection.Backward);
                var rightRectangle = textPointer.GetCharacterRect(LogicalDirection.Forward);
                _rectangleGeometry.Rect = new Rect(leftRectangle.TopLeft, rightRectangle.BottomRight);
            }
        }
    }
}

我认为代码是不言自明的。显然,您需要将该概念扩展到您的特定场景。您可能更愿意利用TextBlockBackground 属性与DrawingBrushGradientBrush 结合使用,而不是使用单独的Path

【讨论】:

  • 好的,我有我的 ViewModel。我会尝试从你的想法开始。我会及时通知你我的进展。谢谢。
  • 哇。深入研究代码将是一种乐趣。非常感谢...我会告诉你的
  • 我一直在寻找非常好的解决方案。谢谢。
【解决方案2】:

在 WPF 列表框中过滤。 以下示例代码对我来说很好。

<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
    <TextBox Name="txtSearch" Height="21" Margin="63,12,12,0" VerticalAlignment="Top"></TextBox>
    <ListBox Name="listItems" ItemsSource="{Binding}" Margin="22,0,0,44" Height="179" VerticalAlignment="Bottom" />
</Grid>

vb.net 代码

将数据表绑定到您的列表框

Dim _dtable As New DataTable("tblItems") _dtable.Columns.Add("Id", GetType(Integer))
    _dtable.Columns.Add("Name", GetType(String))
    _dtable.Columns.Add("Price", GetType(Double))
    ' Add any initialization after the InitializeComponent() call.
    For i = 100 To 110
        _dtable.Rows.Add(i, "Item " & i, 15.0)
    Next







  Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    listItems.DataContext = _dtable.DefaultView
End Sub
Private Sub txtSearch_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs) Handles txtSearch.TextChanged

_dtable.DefaultView.RowFilter = "Name like '" & txtSearch.Text & "%'"

End Sub

以上示例代码适用于我

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多