2013-05-14 101 views
1

我有一個WPF組合框是這樣的:WPF編輯ComboBox慢打字

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

如果我在編輯組合點擊而結合到位(MVVM)給它集中,然後我按並按住任何鍵,我認爲組合將會很快填滿該鍵,但事實並非如此。 如果我刪除displaymemberpath,然後執行相同的操作,那麼我有預期的行爲。當然,我真的需要綁定。當組合有很多的元素

的性能損失只顯示該礦擁有6000

我能不明白的地方這個性能損失的來源。有什麼辦法可以繞過這個問題嗎?

+0

我覺得你綁定到一個懶加載數據源,並且當你輸入時,搜索從數據庫中獲取數據,所以數據加載很慢,正確嗎? – 2013-05-14 18:28:04

+0

沒有數據已經​​加載。 – 2013-05-14 18:41:38

+0

您是否在更改數據時自動保存數據?如果是這樣,則可能會有處罰 - 保留您鍵入的每個字母的數據。 – 2013-05-14 19:43:34

回答

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); 
     } 
    } 
} 

選擇第一個元素將清除該實現的選擇。 所以這裏還是有一些bug