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
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:
<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>
<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>
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<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)) {
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