我有一个为AutoComplete TextBox实现的自定义控件。我从下面的问题 Create a Custom Control with the combination of multiple controls in WPF C# 中得到了所有的想法。在那个自定义控件中,他们建议使用以下代码来添加项, 它的完美工作和双向绑定太 。
(this.ItemsSource as IList<string>).Add(this._textBox.Text);
但是,我将下面的代码更改为未知对象,因此我将
IList<string>
更改为
IList<object>
(this.ItemsSource as IList<object>).Add(item);
XAML:
<local:BTextBox
ItemsSource="{Binding Collection}"
ProviderCommand="{Binding AutoBTextCommand}"
AutoItemsSource="{Binding SuggCollection}" />
但是它没有更新
Collection
.的ViewModel属性我也尝试过在xaml中进行以下更改
ItemsSource="{Binding Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
我不知道我在哪里犯的错。
Functionality
:--
CustomControl
内部的
TextBox
从用户那里获取输入,它触发命令根据用户输入过滤远程数据的
ProviderCommand
,,并通过
AutoItemsSource
,发送过滤的集合--该属性被绑定为
CustomControl
中的
ListBox
的
ItemsSource
以选择项。我们可以从
AddCommand
项中选择项,通过单击该项,它将触发位于
CustomControl
类中的命令
CustomControl
,它在
CustomControl
的
ItemSource
属性中添加所选的项。我在这个属性
ItemsSource
.中有
双向绑定问题
从这个属性中,我们可以获得所选的项作为集合。
这是 我的完整源代码
自定义控件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<dynamic>), typeof(BTextBox), new PropertyMetadata(null, OnItemsSourceChanged));
#endregion
#region Dependency Property Public members
public IEnumerable<dynamic> AutoItemsSource
get { return (IEnumerable<dynamic>)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<object>) != null))
(tb.AutoItemsSource as IList<object>).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
return new DelegatingCommand((obj) =>
(this.ItemsSource as IList<object>).Add(obj);
#endregion
}
Generic.xaml 代码是
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SampleControl">
<Style TargetType="{x:Type local:BTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BTextBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="PART_TextBox" Grid.Row="0" Width="*" VerticalAlignment="Center" />
<ListBox ItemsSource="{TemplateBinding AutoItemsSource}" Grid.Row="1" x:Name="PART_ListBox_Sugg" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Value.IsChecked}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:BTextBox}}, Path=AddCommand}" CommandParameter="{Binding }" Foreground="#404040">
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding }" Visibility="Visible" TextWrapping="Wrap" MaxWidth="270"/>
</StackPanel>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MainWindow.xaml 代码是
<Window x:Class="SampleControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SampleControl"
Title="MainWindow" Height="400" Width="525">
<local:BTextBox
ItemsSource="{Binding Collection}"
ProviderCommand="{Binding AutoBTextCommand}"
AutoItemsSource="{Binding SuggCollection}" />
</Grid>
</Window>
代码 背后的MainWindow.xaml代码
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
this.DataContext = new StringModel();
}
我有两个ViewModels
StringModel
ViewModel #1
class StringModel : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<string> _collection = new ObservableCollection<string>();
private ObservableCollection<string> _suggCollection = new ObservableCollection<string>();
private ObservableCollection<string> _primaryCollection = new ObservableCollection<string>();
public ObservableCollection<string> Collection
get { return _collection; }
_collection = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection"));
public ObservableCollection<string> SuggCollection
get { return _suggCollection; }
_suggCollection = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SuggCollection"));
public StringModel()
_primaryCollection = new ObservableCollection<string> {
"John", "Jack", "James", "Emma", "Peter"
public ICommand AutoBTextCommand
return new DelegatingCommand((obj) =>
Search(obj as string);
private void Search(string str)
SuggCollection = new ObservableCollection<string>(_primaryCollection.Where(m => m.ToLowerInvariant().Contains(str.ToLowerInvariant())).Select(m => m));
}
IntModel
ViewModel #2
class IntModel : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<int> _collection = new ObservableCollection<int>();
private ObservableCollection<int> _suggCollection = new ObservableCollection<int>();
private ObservableCollection<int> _primaryCollection = new ObservableCollection<int>();
public ObservableCollection<int> Collection
get { return _collection; }
_collection = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection"));
public ObservableCollection<int> SuggCollection
get { return _suggCollection; }
_suggCollection = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SuggCollection"));
public IntModel()
_primaryCollection = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 16, 17, 18, 19, 20 };
public ICommand AutoBTextCommand
return new DelegatingCommand((obj) =>
Search(obj as string);
private void Search(string str)
int item = 0;
int.TryParse(str, out item);
SuggCollection = new ObservableCollection<int>(_primaryCollection.Where(m => m == item).Select(m => m));
}
发布于 2016-08-30 12:48:53
首先,这篇文章在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,使其能够编译和运行:
<Style TargetType="{x:Type local:BTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BTextBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="PART_TextBox" Grid.Row="0" VerticalAlignment="Center" />
<ListBox ItemsSource="{TemplateBinding AutoItemsSource}" Grid.Row="1" x:Name="PART_ListBox_Sugg" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:BTextBox}}, Path=AddCommand}" CommandParameter="{Binding}" Foreground="#404040">
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding }" Visibility="Visible" TextWrapping="Wrap" MaxWidth="270"/>
</StackPanel>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
最后,一句有价值的话:
永远不要允许设置在您的ItemsSources上。如果覆盖它们,绑定就会中断。使用
.Clear()
和
.Add()
代替如下所示:
public class StringModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private readonly ObservableCollection<string> _collection = new ObservableCollection<string>();
private readonly ObservableCollection<string> _suggCollection = new ObservableCollection<string>();
private readonly ObservableCollection<string> _primaryCollection = new ObservableCollection<string>();
public ObservableCollection<string> Collection => this._collection;
public ObservableCollection<string> 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)) {