【问题标题】:WPF editable combobox slow typingWPF 可编辑组合框慢速输入
【发布时间】:2015-04-15 23:49:45
【问题描述】:

我有一个这样的 WPF 组合框:

<ComboBox x:Name="CustomerComboBox" IsEditable="True" ItemsSource="{Binding Relations.View}" DisplayMemberPath="Model.sname" />

如果我在绑定到位 (MVVM) 时单击可编辑组合以使其获得焦点,然后按住任意键,我认为组合将很快被该键填充,但事实并非如此吨。 如果我删除 displaymemberpath 然后做同样的事情,那么我有预期的行为。当然我真的需要绑定。

只有当组合有很多元素我有 6000 时才会显示性能损失。

我无法理解这种性能损失来自何处。有没有办法绕过这个问题?

【问题讨论】:

  • 我认为您绑定到延迟加载数据源,并且当您键入时,搜索会从数据库中获取数据,因此数据加载速度很慢,对吗?
  • 没有数据已经​​加载。
  • 您是否在更改数据时自动保存数据?如果是这样,可能会受到惩罚 - 为您输入的每个字母重新保存数据。
  • 也不是这样。这个问题可以在一个小的测试项目中很容易地重现。
  • 发生的情况是,每次在属于组合的文本框中键入一个字符时,组合框事件代码都会处理一个 textchanged 事件。在该代码中,基于 displaymemberpath 创建绑定表达式,并对组合框中的每个条目进行评估。这样做的原因是它尝试执行完成并计算用户当时在组合中键入的文本的选定索引。我能够使用 .NET 反射器对此进行逆向工程

标签: wpf performance combobox


【解决方案1】:

下面的代码通过创建一个专门缓存所有绑定结果的专用组合框来解决这些问题。我通过使用 .NET 反射器查看组合框和项目控件的原始源代码来创建它。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Reflection;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Controls.Primitives;
using System.Collections;

namespace ICeTechControlLibrary
{
    public class FastEditComboBox : ComboBox
    {
        //PARTS
        private TextBox _TextBoxPart = null;

        //DEPENDENCY PROPERTIES
        public static readonly DependencyProperty TextProperty
            = DependencyProperty.Register("Text", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(FastEditComboBox.OnTextChanged)));

        private List<string> _CompletionStrings = new List<string>();
        private int _textBoxSelectionStart;
        private bool _updatingText;
        private bool _updatingSelectedItem;
        private static Dictionary<TextBox, FastEditComboBox> _TextBoxDictionary = new Dictionary<TextBox,FastEditComboBox>();

        static FastEditComboBox()
        {
            EventManager.RegisterClassHandler(typeof(TextBox), TextBox.TextChangedEvent, new TextChangedEventHandler(FastEditComboBox.OnTextChanged));
            EventManager.RegisterClassHandler(typeof(TextBox), TextBox.SelectionChangedEvent, new RoutedEventHandler(FastEditComboBox.OnSelectionChanged));
        }

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

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _TextBoxPart = base.GetTemplateChild("PART_EditableTextBox") as TextBox;
            if (!_TextBoxDictionary.ContainsKey(_TextBoxPart)) _TextBoxDictionary.Add(_TextBoxPart, this);
        }

        private void OnTextBoxSelectionChanged(object sender, RoutedEventArgs e)
        {
            this._textBoxSelectionStart = this._TextBoxPart.SelectionStart;
        }

        private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
        {
            if (IsEditable)
            {
                TextUpdated(_TextBoxPart.Text, true);
            }
        }

        private void TextUpdated(string newText, bool textBoxUpdated)
        {
            if (!_updatingText && !_updatingSelectedItem)
            {
                try
                {
                    _updatingText = true;
                    if (base.IsTextSearchEnabled)
                    {
                        int num = FindMatchingPrefix(newText);
                        if (num >= 0)
                        {
                            if (textBoxUpdated)
                            {
                                int selectionStart = this._TextBoxPart.SelectionStart;
                                if ((selectionStart == newText.Length) && (selectionStart > this._textBoxSelectionStart))
                                {
                                    string primaryTextFromItem = _CompletionStrings[num];
                                    this._TextBoxPart.Text = primaryTextFromItem;
                                    this._TextBoxPart.SelectionStart = newText.Length;
                                    this._TextBoxPart.SelectionLength = primaryTextFromItem.Length - newText.Length;
                                    newText = primaryTextFromItem;
                                }
                            }
                            else
                            {
                                string b = _CompletionStrings[num];
                                if (!string.Equals(newText, b, StringComparison.CurrentCulture))
                                {
                                    num = -1;
                                }
                            }
                        }
                        if (num != base.SelectedIndex)
                        {
                            SelectedIndex = num;
                        }
                    }
                    if (textBoxUpdated)
                    {
                        Text = newText;
                    }
                    else if (_TextBoxPart != null)
                    {
                        _TextBoxPart.Text = newText;
                    }
                }
                finally
                {
                    _updatingText = false;
                }
            }
        }

        internal void SelectedItemUpdated()
        {
            try
            {
                this._updatingSelectedItem = true;
                if (!this._updatingText)
                {
                    string primaryTextFromItem = GetPrimaryTextFromItem(SelectedItem);
                    Text = primaryTextFromItem;
                }
                this.Update();
            }
            finally
            {
                this._updatingSelectedItem = false;
            }
        }

        private void Update()
        {
            if (this.IsEditable)
            {
                this.UpdateEditableTextBox();
            }
            else
            {
                //this.UpdateSelectionBoxItem();
            }
        }

        private void UpdateEditableTextBox()
        {
            if (!_updatingText)
            {
                try
                {
                    this._updatingText = true;
                    string text = this.Text;
                    if ((this._TextBoxPart != null) && (this._TextBoxPart.Text != text))
                    {
                        this._TextBoxPart.Text = text;
                        this._TextBoxPart.SelectAll();
                    }
                }
                finally
                {
                    this._updatingText = false;
                }
            }
        }

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.RaiseEvent(e);
            this.SelectedItemUpdated();
            if (this.IsDropDownOpen)
            {
                object Item = SelectedItem;
                if (Item != null)
                {
                    base.OnSelectionChanged(e);
                }
                //object internalSelectedItem = base.InternalSelectedItem;
                //if (internalSelectedItem != null)
                //{
                //    base.NavigateToItem(internalSelectedItem, ItemsControl.ItemNavigateArgs.Empty);
                //}
            }
        }

        int FindMatchingPrefix(string s)
        {
            int index = _CompletionStrings.BinarySearch(s, StringComparer.OrdinalIgnoreCase);
            if (index >= 0) return index;
            index = ~index;
            string p = _CompletionStrings[index];
            if (p.StartsWith(s, StringComparison.CurrentCultureIgnoreCase)) return index;
            return -1;
        }

        protected override void OnDisplayMemberPathChanged(string oldDisplayMemberPath, string newDisplayMemberPath)
        {
            FillCompletionStrings();
        }

        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            switch (e.Action)
            {
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    AddCompletionStrings(e.NewItems);
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    RemoveCompletionStrings(e.OldItems);
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    FillCompletionStrings();
                    break;
            }
        }

        private void FillCompletionStrings()
        {
            _CompletionStrings.Clear();
            AddCompletionStrings(Items);
        }

        private void RemoveCompletionStrings(IList items)
        {
            foreach (object o in items)
            {
                RemoveCompletionStringForItem(o);
            }
        }

        private void AddCompletionStrings(IList items)
        {
            foreach (object o in items)
            {
                AddCompletionStringForItem(o);
            }
        }

        private void AddCompletionStringForItem(object item)
        {
            Binding binding = new Binding(DisplayMemberPath);
            TextBlock tb = new TextBlock();
            tb.DataContext = item;
            tb.SetBinding(TextBlock.TextProperty, binding);
            string s = tb.Text;
            int index = _CompletionStrings.BinarySearch(s, StringComparer.OrdinalIgnoreCase);
            if (index < 0)
            {
                _CompletionStrings.Insert(~index, s);
            }
            else
            {
                _CompletionStrings.Insert(index, s);
            }
        }

        private string GetPrimaryTextFromItem(object item)
        {
            Binding binding = new Binding(DisplayMemberPath);
            TextBlock tb = new TextBlock();
            tb.DataContext = item;
            tb.SetBinding(TextBlock.TextProperty, binding);
            string s = tb.Text;
            return s;
        }

        private void RemoveCompletionStringForItem(object item)
        {
            Binding binding = new Binding(DisplayMemberPath);
            TextBlock tb = new TextBlock();
            tb.DataContext = item;
            tb.SetBinding(TextBlock.TextProperty, binding);
            string s = tb.Text;
            int index = _CompletionStrings.BinarySearch(s, StringComparer.OrdinalIgnoreCase);
            if (index >= 0) _CompletionStrings.RemoveAt(index);
        }

        private static void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            TextBox tb = e.Source as TextBox;
            if (tb.Name == "PART_EditableTextBox")
            {
                if (_TextBoxDictionary.ContainsKey(tb))
                {
                    FastEditComboBox combo = _TextBoxDictionary[tb];
                    combo.OnTextBoxTextChanged(sender, e);
                    e.Handled = true;
                }
            }
        }

        private static void OnSelectionChanged(object sender, RoutedEventArgs e)
        {
            TextBox tb = e.Source as TextBox;
            if (tb.Name == "PART_EditableTextBox")
            {
                if (_TextBoxDictionary.ContainsKey(tb))
                {
                    FastEditComboBox combo = _TextBoxDictionary[tb];
                    combo.OnTextBoxSelectionChanged(sender, e);
                    e.Handled = true;
                }
            }
        }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FastEditComboBox actb = (FastEditComboBox)d;
            actb.TextUpdated((string)e.NewValue, false);
        }
    }
}

选择第一个元素会清除此实现的选择。 所以这里还有一些错误

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多