1

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

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));
            }
        }
    }
}
4

3 回答 3

2

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

为了实现你想要的,你可以:

  • 通过设置禁用 ListView 虚拟化:VirtualizingStackPanel.IsVirtualizing="False"在您的 ListView 上(如果您的列表中有很多项目,则不推荐)。
  • IItemContainerGenerator.GenerateNext您可以通过调用and强制创建不可见的 ListViewItem IItemContainerGenerator.PrepareItemContainer(根本不推荐)。(也看看这个
  • 找到更好的逻辑来突出显示您的 ListViewItems :)(推荐)。(例如,在您的集合中搜索您想要突出显示的项目,而不是搜索仅显示您的项目的 UI 元素。然后将找到的项目标记为突出显示并基于此,相应地显示 ListViewItems(使用不同的模板或风格))
于 2012-12-05T10:06:57.500 回答
0

In Addition to my Comment:

Use a Property and a Observable Collection and directly filter on that 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));
        }
    }


}

along with

<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>

And you are done. No need to manually touch the listview at any time. (To Increase Performance: For the Raising of the PropertyChanged Event you may want to add a check, if its really changing, or if it has been set to white from white etc.)

于 2012-12-05T13:59:24.747 回答
0

您可以通过多种方式做到这一点。这是我认为可以在您的场景中使用的一种方式,并且部分使用您的代码并且仍然使用虚拟化。

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

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

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

HighlightText(sender)

为了触发加载的事件,您需要在每次搜索字符串更改时刷新列表视图。应该这样ListView.Items.Refresh()做。

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

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

于 2012-12-05T13:26:54.067 回答