RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 638995
Accepted
Andrey K.
Andrey K.
Asked:2020-03-14 00:21:06 +0000 UTC2020-03-14 00:21:06 +0000 UTC 2020-03-14 00:21:06 +0000 UTC

慢速WPF控件(自制带子串高亮的HighlightTextBlock)

  • 772

告诉我为什么我的控件变慢了,尽管它是从并行线程调用的。

我正在尝试为.NET 4.0 ( HighlightTextBlock )创建一个控件,它将以黄色突出显示子文本。这个想法是,如果找到子文本,则ContentControl的Content变成3 Runs的TextBlock ,其中一个以黄色突出显示。

  • result.Add(new Run(stringBefore));

  • result.Add(new Run(stringMatch) { Background = this.HighlightBrush });

  • result.Add(new Run(stringAfter));

如果未找到子文本,则仅插入文本。

一切都已经证明并有效,但问题在于性能。

我将更详细地描述:

此控件用于可能有数百条记录的工作表中。子文本在TextBox中编辑。绑定到这个文本框如下:HighlightText = "{Binding ElementName=myTextBox, Path=Text}"。如果您编辑此子文本,它会冻结,光标会“跳动”,如图所示。对于每次击键,都会执行代码以突出显示文本,并且很明显它在我的控制下已经减慢了 30 行。

擦除时,文本应该平滑地淡化,但不是很平滑: 文本框的行为方式

在我的控制范围内,我试图这样做:

Dispatcher.BeginInvoke(
(ThreadStart)delegate
{
    //update control
}
);

因此,通过异步:

Task.Factory.StartNew(/*поиск в строке и разбиение строки на 3 части*/)
    .ContinueWith(
        x =>
        {
            //update control
        }
        ,TaskScheduler.FromCurrentSynchronizationContext()
        );

但是不可能摆脱刹车。

您可以检查一个简单示例的控件(我立即手写)。制动已经发生在 30 行。

<TextBox Name="myTextBox"/>
<ItemsControl>
    <ItemsControl.Items>
        <!-- ... -->
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <controls:HighlightTextBlock Text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" HighlightText = "{Binding ElementName=myTextBox, Path=Text}"/>
        <!-- ... -->
    </ItemsControl.Items>
</ItemsControl>

这是承诺的完全控制代码。无论有没有调试器,它都会变慢。

public class HighlightTextBlock : ContentControl
{

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set
        {
            SetValue(TextProperty, value);
        }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(HighlightTextBlock), new UIPropertyMetadata("", new PropertyChangedCallback(TextProperty_PropertyChanged)));

    private static void TextProperty_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as HighlightTextBlock;
        control.SetContent();
    }



    public string HighlightText
    {
        get { return (string)GetValue(HighlightTextProperty); }
        set
        {
            SetValue(HighlightTextProperty, value);
        }
    }

    public static readonly DependencyProperty HighlightTextProperty =
        DependencyProperty.Register("HighlightText", typeof(string), typeof(HighlightTextBlock), new UIPropertyMetadata("", new PropertyChangedCallback(HighlightTextProperty_PropertyChanged)));

    private static void HighlightTextProperty_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as HighlightTextBlock;
        control.SetContent();
    }

    public Brush HighlightBrush
    {
        get { return (Brush)GetValue(ColorProperty); }
        set { SetValue(ColorProperty, value); }
    }

    public static readonly DependencyProperty ColorProperty =
        DependencyProperty.Register("HighlightBrush",
        typeof(Brush),
        typeof(HighlightTextBlock),
        new UIPropertyMetadata(Brushes.Orange));


    static HighlightTextBlock()
    {
        //DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentControl), new FrameworkPropertyMetadata(typeof(ContentControl)));
    }


    private TextBlock TextContent
    {
        get { return Content as TextBlock; }
        set { Content = value as TextBlock; }
    }


    public HighlightTextBlock()
    {
        TextContent = new TextBlock();
        SetContent();
    }

    private void SetContent()
    {

        //TextContent.Text = Text;

        var tmpText = Text;
        var tmpHighlightText = HighlightText;
        /*Task.Factory.StartNew<CombinedText>(() => GetCombined(txt, hghlghtTxt))
            .ContinueWith(
                x =>
                {
                    Debug.WriteLine("------- XXXXXXXX -------");
                    var text = (CombinedText)x.Result;

                    TextContent = new TextBlock();
                    TextContent.Inlines.Add(new Run(text.Before ?? ""));
                    TextContent.Inlines.Add(new Run(text.Match ?? "") { Background = HighlightBrush });
                    TextContent.Inlines.Add(new Run(text.After ?? ""));

                    Debug.WriteLine("---------ZZZZZZZZ------------");
                }
                ,TaskScheduler.FromCurrentSynchronizationContext()
            );*/

        Dispatcher.BeginInvoke(
            (ThreadStart)delegate
            {
                var text = GetCombined(tmpText, tmpHighlightText);
                TextContent = new TextBlock();
                TextContent.Inlines.Add(new Run(text.Before ?? ""));
                TextContent.Inlines.Add(new Run(text.Match ?? "") { Background = HighlightBrush });
                TextContent.Inlines.Add(new Run(text.After ?? ""));
            }
            );
    }

    private CombinedText GetCombined(string text, string highlightText)
    {
        Debug.WriteLine("------------1111---------------");
        var result = new CombinedText();
        SharpDebug.WriteLine(text);
        SharpDebug.WriteLine(highlightText);

        if (text != null && highlightText != null)
        {
            Debug.WriteLine("-----------2222----------------");
            var queryLength = highlightText.Length;
            var sourceLength = text.Length;

            var index = text.IndexOf(highlightText, StringComparison.InvariantCultureIgnoreCase);

            if (index >= 0)
            {
                Debug.WriteLine("-------------3333--------------");
                var stringBefore = text.Substring(0, index);
                var stringMatch = text.Substring(index, queryLength);
                var stringAfter = text.Substring(index + queryLength, sourceLength - (index + queryLength));

                if (stringBefore.Length > 0)
                {
                    Debug.WriteLine("-------------4444--------------");
                    result.Before = stringBefore;
                }

                if (stringMatch.Length > 0)
                {
                    Debug.WriteLine("------------5555---------------");
                    result.Match = stringMatch;
                }

                if (stringAfter.Length > 0)
                {
                    Debug.WriteLine("-------------6666--------------");
                    result.After = stringAfter;
                }

                Debug.WriteLine("------------7777---------------");
                return result;
            }
        }
        Debug.WriteLine("-------------9999--------------");

        result.Before = text;
        return result;
    }
}

internal struct CombinedText
{
    public string Before { get; set; }
    public string Match { get; set; }
    public string After { get; set; }
} 

internal static class ControlExtensions
{
    public static void Clear(this TextBlock text)
    {
        text.Text = "";
    }
}
c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    VladD
    2020-03-15T18:44:44Z2020-03-15T18:44:44Z

    我试图加快执行速度。结果比原始版本更快,尽管它仍然不完美。

    public class HighlightTextBlock : ContentControl
    {
        #region dependency property string Text, on change SetContentStatic
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
    
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register(
                "Text", typeof(string), typeof(HighlightTextBlock),
                new UIPropertyMetadata("", SetContentStatic));
        #endregion
    
        #region dependency property string HighlightText, on change SetContentStatic
        public string HighlightText
        {
            get { return (string)GetValue(HighlightTextProperty); }
            set { SetValue(HighlightTextProperty, value); }
        }
    
        public static readonly DependencyProperty HighlightTextProperty =
            DependencyProperty.Register(
                "HighlightText", typeof(string), typeof(HighlightTextBlock),
                new UIPropertyMetadata("", SetContentStatic));
        #endregion
    
        private static void SetContentStatic(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (HighlightTextBlock)d;
            control.SetContent();
        }
    
        #region dependency property Brush HighlightBrush, default = Orange
        public Brush HighlightBrush
        {
            get { return (Brush)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }
    
        public static readonly DependencyProperty ColorProperty =
            DependencyProperty.Register(
                "HighlightBrush", typeof(Brush), typeof(HighlightTextBlock),
                new UIPropertyMetadata(Brushes.Orange));
        #endregion
    
        private TextBlock TextContent = new TextBlock();
    
        public HighlightTextBlock()
        {
            Content = TextContent;
            SetContent();
        }
    
        // текущий процесс обновления 
        Task currentWaitTask = null;
        private async void SetContent45() // это вариант для .NET 4.5
        {
            // для начала подождём 0.1 секунды
            var waitTask = Task.Delay(TimeSpan.FromSeconds(0.1));
            currentWaitTask = waitTask;
            await waitTask;
            // если за это время запустилось более новое обновление, выходим
            if (currentWaitTask != waitTask)
                return;
            currentWaitTask = null;
    
            // обновляем контент
            var parts = GetCombined(Text, HighlightText); 
            ApplyParts(parts);
        }
    
        private void SetContent() // это вариант для .NET 4.0
        {
            var mainTS = TaskScheduler.FromCurrentSynchronizationContext();
            // для начала подождём 0.1 секунды
            var waitTask = Task.Delay(TimeSpan.FromSeconds(0.1));
            currentWaitTask = waitTask;
            waitTask.ContinueWith(t => // когда дождались:
                {
                    // если за это время запустилось более новое обновление, выходим
                    if (currentWaitTask != waitTask)
                        return;
                    currentWaitTask = null;
    
                    // обновляем контент
                    var parts = GetCombined(Text, HighlightText);
                    ApplyParts(parts);
                }, mainTS);
        }
    
        private void ApplyParts(List<string> parts)
        {
            var highlightBrush = HighlightBrush;
            System.Collections.IList existingInlines = TextContent.Inlines;
            var existingCount = existingInlines.Count;
            List<Inline> inlinesToAdd = null;
            var max = Math.Max(existingCount, parts.Count);
            for (int i = 0; i < max; i++)
            {
                var text = i < parts.Count ? parts[i] : null;
                if (i < existingCount)
                {
                    ((Run)existingInlines[i]).Text = text;
                }
                else // add new inline
                {
                    var inline = new Run(text);
                    if (i % 2 != 0) // highlight
                        inline.Background = highlightBrush;
                    if (inlinesToAdd == null)
                        inlinesToAdd = new List<Inline>(parts.Count);
                    inlinesToAdd.Add(inline);
                }
            }
            if (inlinesToAdd != null)
                TextContent.Inlines.AddRange(inlinesToAdd);
        }
    
        private List<string> GetCombined(string text, string highlightText)
        {
            var list = new List<string>();
            if (text == null)
                return list;
            if (highlightText == null)
            {
                list.Add(text);
                return list;
            }
    
            var parts = text.Split(new[] { highlightText }, StringSplitOptions.None);
            bool first = true;
            foreach (var part in parts)
            {
                if (!first)
                    list.Add(highlightText);
                list.Add(part);
                first = false;
            }
            return list;
        }
    }
    

    更新:我想到了另外两个澄清。

    首先,您可以将旧的部分与新的部分进行比较,如果没有任何变化,请不要启动昂贵的多线程操作:

        List<string> oldParts = new List<string>();
        TaskScheduler mainTS;
        private void SetContent() // это вариант для .NET 4.0
        {
            var parts = GetCombined(Text, HighlightText);
            if (parts.SequenceEqual(oldParts))
                return;
            oldParts = parts;
    
            if (mainTS == null)
                mainTS = TaskScheduler.FromCurrentSynchronizationContext();
            ...
    

    然后,你可以在更“平静”的生活阶段发送变化Dispatcher'a。在这种情况下,Task您根本无法将其带入主要上下文:

        List<string> oldParts = new List<string>();
        private void SetContent() // это вариант для .NET 4.0
        {
            var parts = GetCombined(Text, HighlightText);
            if (parts.SequenceEqual(oldParts))
                return;
            oldParts = parts;
    
            // подождём 0.1 секунды
            var waitTask = Task.Delay(TimeSpan.FromSeconds(0.1));
            currentWaitTask = waitTask;
            waitTask.ContinueWith(t => // когда дождались:
            {
                Dispatcher.BeginInvoke((Action)(() =>
                {
                    // если за это время запустилось более новое обновление, выходим
                    if (currentWaitTask != waitTask)
                        return;
                    currentWaitTask = null;
    
                    // обновляем контент
                    ApplyParts(parts);
                }), DispatcherPriority.Input);
            });
        }
    

    我机器上的这个选项表现得更好,尤其是在HighlightText更改时没有真正改变的情况下。

    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5