Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF TextBlock根据搜索条件高亮显示某些部分_C#_Wpf_Highlight_Textblock - Fatal编程技术网

C# WPF TextBlock根据搜索条件高亮显示某些部分

C# WPF TextBlock根据搜索条件高亮显示某些部分,c#,wpf,highlight,textblock,C#,Wpf,Highlight,Textblock,我有一个动态添加了内联线的TextBlock(基本上是一堆斜体或粗体的Run对象) 在我的应用程序中,我有搜索功能 我希望能够突出显示正在搜索的TextBlock文本 突出显示意味着更改TextBlock文本的某些部分的颜色(请记住,它可能一次突出显示几个不同的运行对象) 我试过这个例子 但它非常不稳定:( 有没有简单的方法来解决这个问题?最后编写了以下代码 目前几乎没有bug,但解决了问题 if (Main.IsFullTextSearch) { for (int i = 0; i &

我有一个动态添加了内联线的TextBlock(基本上是一堆斜体或粗体的Run对象)

在我的应用程序中,我有搜索功能

我希望能够突出显示正在搜索的TextBlock文本

突出显示意味着更改TextBlock文本的某些部分的颜色(请记住,它可能一次突出显示几个不同的运行对象)

我试过这个例子

但它非常不稳定:(


有没有简单的方法来解决这个问题?

最后编写了以下代码

目前几乎没有bug,但解决了问题

if (Main.IsFullTextSearch)
{
    for (int i = 0; i < runs.Count; i++)
    {
        if (runs[i] is Run)
        {
            Run originalRun = (Run)runs[i];

            if (Main.SearchCondition != null && originalRun.Text.ToLower()
                .Contains(Main.SearchCondition.ToLower()))
            {
                int pos = originalRun.Text.ToLower()
                          .IndexOf(Main.SearchCondition.ToLower());

                if (pos > 0)
                {
                    Run preRun = CloneRun(originalRun);
                    Run postRun = CloneRun(originalRun);

                    preRun.Text = originalRun.Text.Substring(0, pos);
                    postRun.Text = originalRun.Text
                        .Substring(pos + Main.SearchCondition.Length);

                    runs.Insert(i - 1 < 0 ? 0 : i - 1, preRun);
                    runs.Insert(i + 1, new Run(" "));
                    runs.Insert(i + 2, postRun);

                    originalRun.Text = originalRun.Text
                        .Substring(pos, Main.SearchCondition.Length);

                    SolidColorBrush brush = new SolidColorBrush(Colors.Yellow);
                    originalRun.Background = brush;

                    i += 3;
                }
            }
        }
    }
}
if(Main.IsFullTextSearch)
{
for(int i=0;i0)
{
运行预运行=CloneRun(原始运行);
运行后运行=CloneRun(原始运行);
preRun.Text=originalRun.Text.Substring(0,位置);
postRun.Text=originalRun.Text
.子字符串(位置+主搜索条件.长度);
插入(i-1<0?0:i-1,预运行);
运行。插入(i+1,新运行(“”);
运行。插入(i+2,运行后);
originalRun.Text=originalRun.Text
.子字符串(位置、主搜索条件、长度);
SolidColorBrush笔刷=新的SolidColorBrush(Colors.Yellow);
originalRun.Background=画笔;
i+=3;
}
}
}
}
}

这个问题类似于

在回答这个问题时,我提出了一种使用IValueConverter的方法。该转换器获取文本片段,将其格式化为有效的XAML标记,并使用XamlReader将标记实例化为框架对象


完整的解释相当长,因此我将其发布到了我的博客上:

我遇到了一个类似的问题-尝试在基本上代表报告的演示者负载上实现文本搜索。报告最初被写入字符串,我们利用了FlowDocumentViewer内置的ctrl-F-它不是很好,并且有一些wierd选项但是已经足够了

如果您只是想要这样的东西,您可以执行以下操作:

        <FlowDocumentScrollViewer>
            <FlowDocument>
                <Paragraph FontFamily="Lucida Console" FontSize="12">
                    <Run Text="{Binding Content, Mode=OneWay}"/>
                </Paragraph>
            </FlowDocument>
        </FlowDocumentScrollViewer>
当该类的文本和高光列表更改为更新运行列表时,该类可以绑定到的类型将使用update方法。高光本身如下所示:

public class Highlight
{
    private readonly int _length;
    private readonly Brush _colour;

    public int Start { get; private set; }

    public Highlight(int start, int length,Brush colour)
    {
        Start = start;
        _length = length;
        _colour = colour;
    }

    private string TextFrom(string currentText)
    {
        return currentText.Substring(Start, _length);
    }

    public Run RunFrom(string currentText)
    {
        return new Run(TextFrom(currentText)){Background = _colour};
    }
}
生成正确的高光集合是一个独立的问题,我基本上是通过将演示者集合视为一个递归搜索内容的树来解决的——叶节点是那些有内容的节点,而其他节点只有子节点。如果先搜索深度,则得到预期的顺序。然后基本上可以在结果列表周围写一个包装,以跟踪位置。我不打算发布所有代码-我在这里的回答是记录如何让wpf以MVP风格进行多色高亮显示

我没有在这里使用
INotifyPropertyChanged
CollectionChanged
,因为我们不需要多播更改(例如一个演示者有多个视图)。最初我尝试通过为文本添加事件更改通知,为列表添加一个事件更改通知(您还必须手动订阅上的
INotifyCollectionChanged
事件)。但是,我担心事件子描述中的内存泄漏,并且文本和突出显示的更新不是同时出现的,这一事实使它成了问题

这种方法的一个缺点是人们不应该绑定到此控件的文本属性。在实际版本中,我添加了一些检查+异常抛出来阻止人们这样做,但为了清晰起见,从示例中删除了它!

我考虑并消除了对XML解析器的需求。他在解释每个但是,这并不需要我添加任何额外的库,我是这样做的

第一步,创建一个转换器类:

using System.IO;
using System.Text;

public enum HighLightType
{
    Type = 0,
    Keyword = 1,
    CustomTerm = 2
}

public class CodeHighlighter
{
    public static string[] KeyWords = { "public", "static", "void", "return", "while", "for", "if" };
    public static string[] Types = { "string", "int", "double", "long" };

    private string FormatCodeInXaml(string code, bool withLineBreak)
    {
        string[] mapAr = { "<","&lt;" , //Replace less than sign
                            ">","&gt;" }; //Replace greater than sign
        StringBuilder sb = new StringBuilder();

        using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
        {
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();

                line = line.Replace("\t", "&#160;&#160;&#160;&#160;"); //Replace tabs
                line = line.Replace(" ", "&#160;"); //Replace spaces

                for (int i = 0; i < mapAr.Length; i += 2)
                    line = line.Replace(mapAr[i], mapAr[i + 1]);

                if (withLineBreak)
                    sb.AppendLine(line + "<LineBreak/>"); //Replace line breaks
                else
                    sb.AppendLine(line);
            }

        }
        return sb.ToString();
    }


    private string BuildForegroundTag(string highlightText, string color)
    {
        return "<Span Foreground=\"" + color + "\">" + highlightText + "</Span>";
    }

    private string BuildBackgroundTag(string highlightText, string color)
    {
        return "<Span Background=\"" + color + "\">" + highlightText + "</Span>";
    }

    private string HighlightTerm(HighLightType type, string term, string line)
    {
        if (term == string.Empty)
            return line;

        string keywordColor = "Blue";
        string typeColor = "Blue";
        string statementColor = "Yellow";

        if (type == HighLightType.Type)
            return line.Replace(term, BuildForegroundTag(term, typeColor));
        if (type == HighLightType.Keyword)
            return line.Replace(term, BuildForegroundTag(term, keywordColor));
        if (type == HighLightType.CustomTerm)
            return line.Replace(term, BuildBackgroundTag(term, statementColor));

        return line;
    }

    public string ApplyHighlights(string code, string customTerm)
    {
        code = FormatCodeInXaml(code, true);
        customTerm = FormatCodeInXaml(customTerm, false).Trim();

        StringBuilder sb = new StringBuilder();
        using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
        {
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();

                line = HighlightTerm(HighLightType.CustomTerm, customTerm, line);

                foreach (string keyWord in KeyWords)
                    line = HighlightTerm(HighLightType.Keyword, keyWord, line);

                foreach (string type in Types)
                    line = HighlightTerm(HighLightType.Type, type, line);

                sb.AppendLine(line);
            }
        }

        return sb.ToString();

    }
}
class StringToxamConverter:IValueConverter
{
公共对象转换(对象值、类型targetType、对象参数、CultureInfo区域性)
{
字符串输入=作为字符串的值;
如果(输入!=null)
{
var textBlock=新的textBlock();
textBlock.TextWrapping=TextWrapping.Wrap;
string escapedXml=SecurityElement.Escape(输入);
while(escapedXml.IndexOf(“| ~S~ |”)!=-1){
//高达| ~S~ |是正常的
添加(新运行(escapedXml.Substring(0,escapedXml.IndexOf(“| ~S~ |”));
//在| ~S~ |和| ~E~ |之间高亮显示
textBlock.Inlines.Add(新运行(escapedXml.Substring(escapedXml.IndexOf(“| ~S~ |”)+5,
escapedXml.IndexOf(“| ~E~ |”)-(escapedXml.IndexOf(“| ~S~ |”)+5)))
{fontwweight=fontwweights.Bold,Background=brusks.Yellow});
//字符串的其余部分(在| ~E~ |之后)
escapedXml=escapedXml.Substring(escapedXml.IndexOf(“| ~E~ |”)+5);
}
if(escapedXml.Length>0)
{
添加(新运行(转义XML));
}
返回文本块;
}
返回null;
}
公共对象转换回(对象值、类型targetType、对象参数、CultureInfo区域性)
{
抛出新的NotImplementedException(“此con
<ContentControl Margin="7,0,0,0"
                HorizontalAlignment="Left"
                VerticalAlignment="Center"
                Content="{Binding Description, Converter={StaticResource CONVERTERS_StringToXaml}, Mode=OneTime}">
</ContentControl>
public class SearchHightlightTextBlock : TextBlock
{
    public SearchHightlightTextBlock() : base() { }

    public String SearchText { get { return (String)GetValue(SearchTextProperty); }
                               set { SetValue(SearchTextProperty, value); } }      

    private static void OnDataChanged(DependencyObject source,
                                      DependencyPropertyChangedEventArgs e)
    {
        TextBlock tb = (TextBlock)source;

        if (tb.Text.Length == 0)
            return;

        string textUpper = tb.Text.ToUpper();
        String toFind = ((String) e.NewValue).ToUpper();
        int firstIndex = textUpper.IndexOf(toFind);
        String firstStr = tb.Text.Substring(0, firstIndex);
        String foundStr = tb.Text.Substring(firstIndex, toFind.Length);
        String endStr = tb.Text.Substring(firstIndex + toFind.Length, 
                                         tb.Text.Length - (firstIndex + toFind.Length));

        tb.Inlines.Clear();
        var run = new Run();
        run.Text = firstStr;
        tb.Inlines.Add(run);
        run = new Run();
        run.Background = Brushes.Yellow;
        run.Text = foundStr;
        tb.Inlines.Add(run);
        run = new Run();
        run.Text = endStr;

        tb.Inlines.Add(run);
    }

    public static readonly DependencyProperty SearchTextProperty =
        DependencyProperty.Register("SearchText", 
                                    typeof(String), 
                                    typeof(SearchHightlightTextBlock), 
                                    new FrameworkPropertyMetadata(null, OnDataChanged));
}
<view:SearchHightlightTextBlock SearchText="{Binding TextPropertyContainingTextToSearch}" 
                                Text="{Binding YourTextProperty}"/>
public static void TestLoop(int count)
{ 
   for(int i=0;i<count;i++)
     Console.WriteLine(i);
}
using System.IO;
using System.Text;

public enum HighLightType
{
    Type = 0,
    Keyword = 1,
    CustomTerm = 2
}

public class CodeHighlighter
{
    public static string[] KeyWords = { "public", "static", "void", "return", "while", "for", "if" };
    public static string[] Types = { "string", "int", "double", "long" };

    private string FormatCodeInXaml(string code, bool withLineBreak)
    {
        string[] mapAr = { "<","&lt;" , //Replace less than sign
                            ">","&gt;" }; //Replace greater than sign
        StringBuilder sb = new StringBuilder();

        using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
        {
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();

                line = line.Replace("\t", "&#160;&#160;&#160;&#160;"); //Replace tabs
                line = line.Replace(" ", "&#160;"); //Replace spaces

                for (int i = 0; i < mapAr.Length; i += 2)
                    line = line.Replace(mapAr[i], mapAr[i + 1]);

                if (withLineBreak)
                    sb.AppendLine(line + "<LineBreak/>"); //Replace line breaks
                else
                    sb.AppendLine(line);
            }

        }
        return sb.ToString();
    }


    private string BuildForegroundTag(string highlightText, string color)
    {
        return "<Span Foreground=\"" + color + "\">" + highlightText + "</Span>";
    }

    private string BuildBackgroundTag(string highlightText, string color)
    {
        return "<Span Background=\"" + color + "\">" + highlightText + "</Span>";
    }

    private string HighlightTerm(HighLightType type, string term, string line)
    {
        if (term == string.Empty)
            return line;

        string keywordColor = "Blue";
        string typeColor = "Blue";
        string statementColor = "Yellow";

        if (type == HighLightType.Type)
            return line.Replace(term, BuildForegroundTag(term, typeColor));
        if (type == HighLightType.Keyword)
            return line.Replace(term, BuildForegroundTag(term, keywordColor));
        if (type == HighLightType.CustomTerm)
            return line.Replace(term, BuildBackgroundTag(term, statementColor));

        return line;
    }

    public string ApplyHighlights(string code, string customTerm)
    {
        code = FormatCodeInXaml(code, true);
        customTerm = FormatCodeInXaml(customTerm, false).Trim();

        StringBuilder sb = new StringBuilder();
        using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
        {
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();

                line = HighlightTerm(HighLightType.CustomTerm, customTerm, line);

                foreach (string keyWord in KeyWords)
                    line = HighlightTerm(HighLightType.Keyword, keyWord, line);

                foreach (string type in Types)
                    line = HighlightTerm(HighLightType.Type, type, line);

                sb.AppendLine(line);
            }
        }

        return sb.ToString();

    }
}
<Window x:Class="TestCodeVisualizer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestCodeVisualizer"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Canvas Name="canvas" />
</Window>
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Markup;

namespace TestCodeVisualizer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            string testText = File.ReadAllText("Test.txt");
            FrameworkElement fe = GenerateHighlightedTextBlock(testText, "Console.WriteLine");
            this.canvas.Children.Add(fe);
        }


        private FrameworkElement GenerateHighlightedTextBlock(string code, string term)
        {
            CodeHighlighter ch = new CodeHighlighter();
            string uc = "<UserControl xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>[CONTENT]</UserControl>";

            string content = "<TextBlock>" + ch.ApplyHighlights(code, term) + "</TextBlock>";
            uc = uc.Replace("[CONTENT]", content);

            FrameworkElement fe = XamlReader.Load(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(uc))) as FrameworkElement;
            return fe;
        }

    }
}
<TextBlock local:HighlightTermBehavior.TermToBeHighlighted="{Binding MyTerm}"
           local:HighlightTermBehavior.Text="{Binding MyText}" />
<TextBlock local:HighlightTermBehavior.TermToBeHighlighted="highlight this"
           local:HighlightTermBehavior.Text="bla highlight this bla" />
public static class HighlightTermBehavior
{
    public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
        "Text",
        typeof(string),
        typeof(HighlightTermBehavior),
        new FrameworkPropertyMetadata("", OnTextChanged));

    public static string GetText(FrameworkElement frameworkElement)               => (string) frameworkElement.GetValue(TextProperty);
    public static void   SetText(FrameworkElement frameworkElement, string value) => frameworkElement.SetValue(TextProperty, value);


    public static readonly DependencyProperty TermToBeHighlightedProperty = DependencyProperty.RegisterAttached(
        "TermToBeHighlighted",
        typeof(string),
        typeof(HighlightTermBehavior),
        new FrameworkPropertyMetadata("", OnTextChanged));

    public static string GetTermToBeHighlighted(FrameworkElement frameworkElement)
    {
        return (string) frameworkElement.GetValue(TermToBeHighlightedProperty);
    }

    public static void SetTermToBeHighlighted(FrameworkElement frameworkElement, string value)
    {
        frameworkElement.SetValue(TermToBeHighlightedProperty, value);
    }


    private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBlock textBlock)
            SetTextBlockTextAndHighlightTerm(textBlock, GetText(textBlock), GetTermToBeHighlighted(textBlock));
    }

    private static void SetTextBlockTextAndHighlightTerm(TextBlock textBlock, string text, string termToBeHighlighted)
    {
        textBlock.Text = string.Empty;

        if (TextIsEmpty(text))
            return;

        if (TextIsNotContainingTermToBeHighlighted(text, termToBeHighlighted))
        {
            AddPartToTextBlock(textBlock, text);
            return;
        }

        var textParts = SplitTextIntoTermAndNotTermParts(text, termToBeHighlighted);

        foreach (var textPart in textParts)
            AddPartToTextBlockAndHighlightIfNecessary(textBlock, termToBeHighlighted, textPart);
    }

    private static bool TextIsEmpty(string text)
    {
        return text.Length == 0;
    }

    private static bool TextIsNotContainingTermToBeHighlighted(string text, string termToBeHighlighted)
    {
        return text.Contains(termToBeHighlighted, StringComparison.Ordinal) == false;
    }

    private static void AddPartToTextBlockAndHighlightIfNecessary(TextBlock textBlock, string termToBeHighlighted, string textPart)
    {
        if (textPart == termToBeHighlighted)
            AddHighlightedPartToTextBlock(textBlock, textPart);
        else
            AddPartToTextBlock(textBlock, textPart);
    }

    private static void AddPartToTextBlock(TextBlock textBlock, string part)
    {
        textBlock.Inlines.Add(new Run {Text = part, FontWeight = FontWeights.Light});
    }

    private static void AddHighlightedPartToTextBlock(TextBlock textBlock, string part)
    {
        textBlock.Inlines.Add(new Run {Text = part, FontWeight = FontWeights.ExtraBold});
    }


    public static List<string> SplitTextIntoTermAndNotTermParts(string text, string term)
    {
        if (text.IsNullOrEmpty())
            return new List<string>() {string.Empty};

        return Regex.Split(text, $@"({Regex.Escape(term)})")
                    .Where(p => p != string.Empty)
                    .ToList();
    }
}
    private TextBlock HighlightSearch(TextBlock textBlock, string searchTerm)
    {
        string[] words = textBlock.Text.Split(' ');

        textBlock.Text = string.Empty; 
        
        foreach (string word in words)
        {
            if (!string.IsNullOrEmpty(searchTerm) &&
                word.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0)
            {
                textBlock.Inlines.Add(new Run($"{word} ") { Foreground = Brushes.Purple, FontWeight = FontWeights.DemiBold });
            }
            else
            {
                textBlock.Inlines.Add($"{word} ");
            }
        }

        return textBlock; 
    }