WPF自定义控件依赖属性中未知对象的双向绑定问题

我有一个自定义控件 – 为AutoComplete TextBox实现。 我从以下问题得到了所有想法在WPF C#中使用多个控件的组合创建自定义控件 。 在该自定义控件中,他们建议使用以下代码来添加项目, 它的完美工作和双向绑定

(this.ItemsSource as IList).Add(this._textBox.Text); 

但是,我将以下代码更改为Unknown Object,因此我将IList更改为IList

 (this.ItemsSource as IList).Add(item); 

XAML:

   

但它没有更新ViewModel Property Collection 。 我也在xaml中尝试了以下更改

 ItemsSource="{Binding Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 

我不知道我在哪里犯了这个错误。

functionCustomControlTextBox接受来自User的输入并触发ProviderCommand ,该Command根据User Input过滤远程数据并通过AutoItemsSource发送Filtered Collection,此属性被绑定为ListBox内的ItemsSource CustomControl选择Item。 我们可以从ListBox项中选择Item,通过单击Item,它会触发CustomControl类中的Command AddCommand ,它会在CustomControl ItemSource属性中添加所选项。 我在这个Property ItemsSource遇到了双向绑定问题 。 仅从此属性中我们可以将Selected项目作为集合。

这是我的完整源代码

自定义控件C#代码:

 public class BTextBox : ItemsControl { static BTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BTextBox), new FrameworkPropertyMetadata(typeof(BTextBox))); } #region Private Members private TextBox _textBox; private ItemsControl _itemsView; #endregion #region Dependency Property Private Members 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)); #endregion #region Dependency Property Public members public IEnumerable AutoItemsSource { get { return (IEnumerable)GetValue(AutoItemsSourceProperty); } set { SetValue(AutoItemsSourceProperty, value); } } #endregion #region Listener Methods private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var tb = d as BTextBox; if ((e.NewValue != null) && ((tb.ItemsSource as IList) != null)) { (tb.AutoItemsSource as IList).Add(e.NewValue); } } #endregion #region Override Methods public override void OnApplyTemplate() { this._textBox = this.GetTemplateChild("PART_TextBox") as TextBox; this._itemsView = this.GetTemplateChild("PART_ListBox") as ItemsControl; this._textBox.TextChanged += (sender, args) => { if (this.ProviderCommand != null) { this.ProviderCommand.Execute(this._textBox.Text); } }; base.OnApplyTemplate(); } #endregion #region Command public ICommand ProviderCommand { get { return (ICommand)GetValue(ProviderCommandProperty); } set { SetValue(ProviderCommandProperty, value); } } public ICommand AddCommand { get { return new DelegatingCommand((obj) => { (this.ItemsSource as IList).Add(obj); }); } } #endregion } 

Generic.xaml代码是

                                 

MainWindow.xaml代码是

      

MainWindow.xaml的C#代码背后的代码

 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new StringModel(); } } 

我有两个ViewModel

ViewModel#1 StringModel

 class StringModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection _collection = new ObservableCollection(); private ObservableCollection _suggCollection = new ObservableCollection(); private ObservableCollection _primaryCollection = new ObservableCollection(); public ObservableCollection Collection { get { return _collection; } set { _collection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection")); } } public ObservableCollection SuggCollection { get { return _suggCollection; } set { _suggCollection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SuggCollection")); } } public StringModel() { _primaryCollection = new ObservableCollection { "John", "Jack", "James", "Emma", "Peter" }; } public ICommand AutoBTextCommand { get { return new DelegatingCommand((obj) => { Search(obj as string); }); } } private void Search(string str) { SuggCollection = new ObservableCollection(_primaryCollection.Where(m => m.ToLowerInvariant().Contains(str.ToLowerInvariant())).Select(m => m)); } } 

ViewModel#2 IntModel

 class IntModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection _collection = new ObservableCollection(); private ObservableCollection _suggCollection = new ObservableCollection(); private ObservableCollection _primaryCollection = new ObservableCollection(); public ObservableCollection Collection { get { return _collection; } set { _collection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection")); } } public ObservableCollection SuggCollection { get { return _suggCollection; } set { _suggCollection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SuggCollection")); } } public IntModel() { _primaryCollection = new ObservableCollection { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20 }; } public ICommand AutoBTextCommand { get { return new DelegatingCommand((obj) => { Search(obj as string); }); } } private void Search(string str) { int item = 0; int.TryParse(str, out item); SuggCollection = new ObservableCollection(_primaryCollection.Where(m => m == item).Select(m => m)); } } 

首先,这篇文章在CodeReview中更适合。

其次,我可以想象,你想做什么。 为了缩短范围,我建议您不要在您的情况下使用通用集合。

我已经修改了控件:

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

然后我修复了你的XAML以获得甚至编译和运行的东西:

  

最后一个有价值的评论:

永远不要在ItemsSources上允许setter。 如果覆盖它们,绑定将会中断。 .Clear()使用.Clear().Add() ,如下所示:

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

注意

我没有你的DelegateCommand -implementation,我使用了我的RelayCommand 。 您可以在任何问题上进行更改。 我认为它是相同的东西,但它的名称不同。
您也可以考虑从一开始就显示您的建议。 这可能会提供更好的用户体验,但这只是我的意见