Поиск в WPF - PullRequest
       2

Поиск в WPF

0 голосов
/ 19 февраля 2020

У меня есть приложение WPF MVVM с динамическим c пользовательским интерфейсом (это означает, что в определенном пользовательском элементе управления может отображаться много разных элементов).

Я хотел бы добавить возможность для пользователя поиск по всему экрану, чтобы найти текст, который они ищут ... подобно тому, как можно было бы использовать встроенные в браузер возможности поиска.

Мне не удалось найти универсальное решение для этого и Я подумываю о поиске визуального дерева для отображаемого текста, но я надеюсь, что это похоже на необходимость общего назначения, что я здесь упускаю какое-то относительно нестандартное решение.

1 Ответ

0 голосов
/ 19 февраля 2020

Это сложно.
Пока это только часть решения, но вот пример, чтобы выделить поиск во всех текстовых блоках в VisualTree объекта root.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

public class GlobalTextHighlighter
{
    public static Brush GetHighlightBackground(DependencyObject obj)
    {
        return (Brush)obj.GetValue(HighlightBackgroundProperty);
    }

    public static void SetHighlightBackground(DependencyObject obj, Brush value)
    {
        obj.SetValue(HighlightBackgroundProperty, value);
    }

    // Using a DependencyProperty as the backing store for HighlightBackground.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HighlightBackgroundProperty =
        DependencyProperty.RegisterAttached("HighlightBackground", typeof(Brush), typeof(GlobalTextHighlighter), new PropertyMetadata(Brushes.Yellow, new PropertyChangedCallback(RefreshHighlighting)));

    public static Brush GetHighlightForeground(DependencyObject obj)
    {
        return (Brush)obj.GetValue(HighlightForegroundProperty);
    }

    public static void SetHighlightForeground(DependencyObject obj, Brush value)
    {
        obj.SetValue(HighlightForegroundProperty, value);
    }

    // Using a DependencyProperty as the backing store for HighlightForeground.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HighlightForegroundProperty =
        DependencyProperty.RegisterAttached("HighlightForeground", typeof(Brush), typeof(GlobalTextHighlighter), new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(RefreshHighlighting)));

    public static string GetSearchText(DependencyObject obj)
    {
        return (string)obj.GetValue(SearchTextProperty);
    }

    public static void SetSearchText(DependencyObject obj, string value)
    {
        obj.SetValue(SearchTextProperty, value);
    }

    // Using a DependencyProperty as the backing store for SearchText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SearchTextProperty =
        DependencyProperty.RegisterAttached("SearchText", typeof(string), typeof(GlobalTextHighlighter), new PropertyMetadata(string.Empty, new PropertyChangedCallback(RefreshHighlighting)));

    private static void RefreshHighlighting(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if(d != null)
        {
            try
            {
                Brush background = (Brush)d.GetValue(HighlightBackgroundProperty);
                Brush foreground = (Brush)d.GetValue(HighlightForegroundProperty);
                string highlightText = (string)d.GetValue(SearchTextProperty);

                FindVisualChildren<TextBlock>(d)
                    .ToList()
                    .ForEach(textBlock =>
                    {
                        try
                        {
                            string text = textBlock.Text;

                            if (!string.IsNullOrEmpty(text))
                            {
                                if (string.IsNullOrEmpty(highlightText))
                                {
                                    textBlock.Text = text;
                                }
                                else
                                {
                                    int index = text.IndexOf(highlightText, StringComparison.CurrentCultureIgnoreCase);

                                    if (index < 0)
                                        textBlock.Text = text;
                                    else
                                        textBlock.Inlines.Clear();

                                    while (index >= 0)
                                    {
                                        textBlock.Inlines.AddRange(new Inline[]
                                        {
                                            new Run(text.Substring(0, index)),
                                            new Run(text.Substring(index, highlightText.Length))
                                            {
                                                Background = background,
                                                Foreground = foreground
                                            }
                                        });

                                        text = text.Substring(index + highlightText.Length);
                                        index = text.IndexOf(highlightText, StringComparison.CurrentCultureIgnoreCase);

                                        if (index < 0)
                                        {
                                            textBlock.Inlines.Add(new Run(text));
                                        }
                                    }
                                }
                            }
                        }
                        catch
                        { }
                    });
            }
            catch { }
        }
    }

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }
}

И вы можете использовать его на root объекте, из которого хотите выполнить поиск.

Пример:

<TextBox x:Name="GlobalSearchTextBox" 
         Text="{Binding GlobalSearch, UpdateSourceTrigger=PropertyChanged, Delay=100}"/>
<DockPanel local:GlobalTextHighlighter.SearchText="{Binding GlobalSearch}"
           local:GlobalTextHighlighter.HighlightBackground="Orange"
           local:GlobalTextHighlighter.HighlightForeground="Blue">
    <!-- ... -->
</DockPanel>

Он основан на WPF TextBlock Highlighter и Найдите все элементы управления в окне WPF по типу .

Как я уже сказал, это еще не полное решение.
И оно имеет некоторые недостатки.

  • Он не работает со всеми компонентами, использующими AccessText, например Labels.
  • Он не работает с редактируемыми компонентами, такими как TextBoxs или RichTextBox.
  • Это не так просмотр от одного совпадения к другому.
    • Если вы хотите управлять вложенными ScrollAreas.

Надеюсь, это уже поможет приблизиться к решению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...