WPF Listview项突出显示

时间:2012-12-05 09:05:54

标签: c# wpf listview textbox

我有一个包含客户信息的列表视图。列表视图上方有一个搜索文本框。当您在文本框中键入任何内容时,它会高亮显示列表视图中的匹配项。但是,问题在于它仅在列表视图的可视化侧进行搜索。它不会在列表视图的未滚动侧(列表视图的底部)中搜索。我的代码如下。请看一看。

private void FindListViewItem(DependencyObject obj)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        ListViewItem lv = obj as ListViewItem;
        if (lv != null)
        {
            HighlightText(lv);
        }
        FindListViewItem(VisualTreeHelper.GetChild(obj as DependencyObject, i));
    }
}

private void HighlightText(Object itx)
{
    if (itx != null)
    {
        if (itx is TextBlock)
        {
            Regex regex = new Regex("(" +TxtSearch.Text + ")", RegexOptions.IgnoreCase);
            TextBlock tb = itx as TextBlock;
            if (TxtSearch.Text.Length == 0)
            {
                string str = tb.Text;
                tb.Inlines.Clear();
                tb.Inlines.Add(str);
                return;
            }
            string[] substrings = regex.Split(tb.Text);
            tb.Inlines.Clear();
            foreach (var item in substrings)
            {
                if (regex.Match(item).Success)
                {
                    Run runx = new Run(item);
                    runx.Background = Brushes.Lime;
                    tb.Inlines.Add(runx);

                    if (tb.IsMouseOver)
                    {
                        tb.IsEnabled = false;
                    }
                }
                else
                {
                    tb.Inlines.Add(item);
                    tb.IsEnabled = false;
                }
            }

            return;
        }
        else
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(itx as DependencyObject); i++)
            {
                HighlightText(VisualTreeHelper.GetChild(itx as DependencyObject, i));
            }
        }
    }
}

3 个答案:

答案 0 :(得分:2)

这是因为ListView默认使用虚拟化作为其内容。这意味着ListViewItems在需要时创建。如果您没有滚动ListView,则不会创建一些ListViewItems,并且VisualTreeHelper.GetChildrenCount将无法返回那些ListViewItems。

要实现您的目标,您可以:

  • 通过在ListView上设置:VirtualizingStackPanel.IsVirtualizing="False"来禁用ListView虚拟化(如果列表中有许多项目,则不建议这样做)。
  • 您可以通过调用IItemContainerGenerator.GenerateNextIItemContainerGenerator.PrepareItemContainer(根本不推荐)来强制创建看不见的ListViewItem。 (另请查看this
  • 找到一个更好的逻辑来突出你的ListViewItems :)(推荐)。 (例如,在您的集合中搜索要突出显示的项目,而不是搜索仅显示项目的UI元素。然后将找到的项目标记为突出显示并基于此,相应地显示ListViewItems(使用不同的模板或式))

答案 1 :(得分:0)

您可以通过多种方式完成此操作。以下是我认为可以在您的方案中使用的一种方法,部分使用您的代码并仍然使用虚拟化。

使用数据模板作为列表视图项,并为加载的事件创建事件处理程序,如:

<ListView.ItemTemplate>
      <DataTemplate>
          <TextBlock Text="{Binding}" Loaded="FrameworkElement_OnLoaded"/>
      </DataTemplate>
 </ListView.ItemTemplate>

在OnLoaded事件处理程序中,在发件人上调用HighlightText方法:

HighlightText(sender)

为了触发加载的事件,每次搜索字符串发生变化时,您都需要刷新列表视图。像ListView.Items.Refresh()之类的东西应该这样做。

您可以通过在更改的搜索文本上添加一个小计时器来改善这一点,这样用户就可以在搜索某些内容时完成输入。

还有其他更优雅的方法来处理这个问题,但对于你的情况,我认为这应该有用。

答案 2 :(得分:0)

除了我的评论:

使用Property和Observable Collection并直接过滤该Collection。

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public ObservableCollection<Entry> MyCollection {get;set;}

    public MainWindow()
    {
        InitializeComponent();

        MyCollection = new ObservableCollection<Entry>();
        MyCollection.Add(new Entry() { Name = "Test" });
        MyCollection.Add(new Entry() { Name = "ABCD" });
        MyCollection.Add(new Entry() { Name = "TESTABC" });
        MyCollection.Add(new Entry() { Name = "BCDtest" });

        this.MyListView.DataContext = this;
    }

    private void searchTerm_KeyUp(object sender, KeyEventArgs e)
    {
        String term = ((TextBox)sender).Text;

        foreach (Entry entry in this.MyCollection)
        {
            if (entry.Name.Contains(term))
                entry.Highlight();
            else
                entry.UnHighlight();
        }

    }
}

public class Entry : INotifyPropertyChanged
{
    public String Name { get; set; }
    public Color BGColor { get; set; }
    public SolidColorBrush BGBrush
    {
        get
        {
            return new SolidColorBrush(this.BGColor);
        }
    }

    public Entry()
    {
        this.UnHighlight();
    }

    public void Highlight()
    {
        this.BGColor = Colors.Yellow;
        this.NotifyPropertyChanged("BGBrush");
    }

    public void UnHighlight()
    {
        this.BGColor = Colors.White;
        this.NotifyPropertyChanged("BGBrush");
    }
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }


}

一起
<Grid>
    <DockPanel>
        <TextBox DockPanel.Dock="Top" Name="searchTerm" KeyUp="searchTerm_KeyUp"></TextBox>
        <ListView Name="MyListView" ItemsSource="{Binding MyCollection}" >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Background="{Binding BGBrush}" Text="{Binding Name}"></TextBlock>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </DockPanel>
</Grid>

你完成了。无需随时手动触摸列表视图。 (提高性能:对于PropertyChanged事件的提升,您可能需要添加一个支票,如果它真的发生了变化,或者它是否已从白色等设置为白色。)