Two-Way Binding Issue of Unknown Object in WPF Custom Control Dependency Property

后端 未结 1 842
粉色の甜心
粉色の甜心 2021-01-07 01:43

I\'m having a Custom Control - Implemented for AutoComplete TextBox. I got all idea\'s from the following question Create a Custom Control with the combination of multiple c

1条回答
  •  悲哀的现实
    2021-01-07 02:22

    First of all, this post would have fitted better in CodeReview.

    Second, i can imagine, what you did want to do. To shorten things, i recommend you to not use generic collections in your case.

    I've modified the Control a bit:

    public class BTextBox : ItemsControl {
    
        static BTextBox() {
          DefaultStyleKeyProperty.OverrideMetadata(typeof(BTextBox), new FrameworkPropertyMetadata(typeof(BTextBox)));
        }
    
        private TextBox _textBox;
        private ItemsControl _itemsView;
    
        public static readonly DependencyProperty ProviderCommandProperty = DependencyProperty.Register("ProviderCommand", typeof(ICommand), typeof(BTextBox), new PropertyMetadata(null));
        public static readonly DependencyProperty AutoItemsSourceProperty = DependencyProperty.Register("AutoItemsSource", typeof(IEnumerable), typeof(BTextBox), new PropertyMetadata(null, OnItemsSourceChanged));
    
        public IEnumerable AutoItemsSource {
          get {
            return (IEnumerable)GetValue(AutoItemsSourceProperty);
          }
          set {
            SetValue(AutoItemsSourceProperty, value);
          }
        }
    
        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
          var tb = d as BTextBox;
          if ((e.NewValue != null) && ((tb.ItemsSource as IList) != null)) {
            foreach (var item in e.NewValue as IEnumerable) {
              (tb.AutoItemsSource as IList).Add(item);
            }
    
          }
        }
    
        public override void OnApplyTemplate() {
          this._textBox = this.GetTemplateChild("PART_TextBox") as TextBox;
          this._itemsView = this.GetTemplateChild("PART_ListBox_Sugg") as ItemsControl;
          this._itemsView.ItemsSource = this.AutoItemsSource;
          this._textBox.TextChanged += (sender, args) => {
            this.ProviderCommand?.Execute(this._textBox.Text);
          };
    
          base.OnApplyTemplate();
        }
    
        public ICommand ProviderCommand {
          get {
            return (ICommand) this.GetValue(ProviderCommandProperty);
          }
          set {
            this.SetValue(ProviderCommandProperty, value);
          }
        }
    
        public ICommand AddCommand {
          get {
            return new RelayCommand(obj => {
              (this.ItemsSource as IList)?.Add(obj);
            });
          }
        }
    
      }
    

    Then i've fixed your XAML to get thing to even compile and run:

    
    

    At last a valuable remark:

    Never ever allow setters on your ItemsSources. If you override them, the binding will break. Use .Clear() and .Add() instead as you see below:

    public class StringModel : INotifyPropertyChanged {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private readonly ObservableCollection _collection = new ObservableCollection();
        private readonly ObservableCollection _suggCollection = new ObservableCollection();
        private readonly ObservableCollection _primaryCollection = new ObservableCollection();
    
        public ObservableCollection Collection => this._collection;
    
        public ObservableCollection SuggCollection => this._suggCollection;
    
        public StringModel() {
          this._primaryCollection.Add("John");
          this._primaryCollection.Add("Jack");
          this._primaryCollection.Add("James");
          this._primaryCollection.Add("Emma");
          this._primaryCollection.Add("Peter");
        }
    
        public ICommand AutoBTextCommand {
          get {
            return new RelayCommand(obj => {
              this.Search(obj as string);
            });
          }
        }
    
        private void Search(string str) {
          this.SuggCollection.Clear();
          foreach (var result in this._primaryCollection.Where(m => m.ToLowerInvariant().Contains(str.ToLowerInvariant())).Select(m => m)) {
            this.SuggCollection.Add(result);
          }
    
        }
    
      }
    

    Note

    Sice i didnt have your DelegateCommand-implementation, i've used my RelayCommand instead. You can change it withour any issues. I think its the same thing but a different name for it.
    You also might consider to display your suggestions right from the start. This might provide a better user-expierience, but thats just my opinion

    0 讨论(0)
提交回复
热议问题