1

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

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

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

性能损失仅显示当组合有很多元素时,我的元素有 6000。

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

4

1 回答 1

0

下面的代码通过创建一个专门缓存所有绑定结果的专用组合框来解决这些问题。我通过使用 .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);
        }
    }
}

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

于 2013-05-25T07:45:11.057 回答