我创建了自己的WPF用户控件,这是一个包含自动完成建议的文本框。 XAML看起来像这样:
<UserControl x:Class="WpfApplication4.AutoCompleteTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication4"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical">
<TextBox x:Name="textBox" />
<ListBox x:Name="listBox" MaxHeight="100"/>
</StackPanel>
背后的代码如下:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication4
{
/// <summary>
/// A WPF component which has a textbox. If the user enters some text, a ListBox is displayed showing some suggestions which the user can follow or not.
/// </summary>
public partial class AutoCompleteTextBox : UserControl
{
/// <summary>
/// This event is invoked when the text of the textbox is changed.
/// </summary>
public event TextChangedEventHandler TextChanged;
/// <summary>
/// Needed for DataBinding of Suggestions
/// </summary>
public static DependencyProperty SuggestionsProperty;
/// <summary>
/// Needed for data binding of Text
/// </summary>
public static DependencyProperty TextProperty;
/// <summary>
/// A list of the suggestions which are displayed to the user.
/// </summary>
public List<string> Suggestions { get
{
return (List<string>) GetValue(SuggestionsProperty);
}
set
{
SetValue(SuggestionsProperty, value);
}
}
/// <summary>
/// True if showing of the suggestions is case-sensitive; false, if case-insensitive
/// </summary>
public bool CaseSensitive { get; set; }
/// <summary>
/// The text displayed inside the textbox
/// </summary>
public string Text
{
get
{
return textBox.Text;
//return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
textBox.Text = value;
}
}
/// <summary>
/// Create a new AutoCompleteTextBox
/// </summary>
public AutoCompleteTextBox()
{
InitializeComponent();
Suggestions = new List<string>();
DataContext = this;
listBox.Visibility = Visibility.Collapsed;
CaseSensitive = true;
textBox.TextChanged += ExternalTextEvent;
textBox.TextChanged += textChanged;
textBox.PreviewKeyDown += keyDown;
textBox.LostFocus += lostFocus;
textBox.GotFocus += gotFocus;
listBox.SelectionChanged += selectionChanged;
}
private void ExternalTextEvent(object sender, TextChangedEventArgs e)
{
TextChanged?.Invoke(this, e);
}
static AutoCompleteTextBox()
{
SuggestionsProperty = DependencyProperty.Register(nameof(Suggestions), typeof(List<string>), typeof(AutoCompleteTextBox));
TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(AutoCompleteTextBox));
}
private void gotFocus(object sender, RoutedEventArgs e)
{
updateListBox();
}
private void lostFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Visibility.Collapsed;
}
private void keyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Return)
{
if (listBox.Visibility == Visibility.Visible)
{
listBox.Visibility = Visibility.Collapsed;
textBox.TextChanged -= textChanged;
textBox.Text = (string)listBox.Items[0];
textBox.TextChanged += textChanged;
}
}
if (e.Key == Key.Escape)
{
listBox.Visibility = Visibility.Collapsed;
}
//if(e.Key == Key.Down && listBox.Visibility == Visibility.Visible)
//{
// textBox.LostFocus -= lostFocus;
// listBox.SelectedIndex = 0;
// textBox.LostFocus += lostFocus;
//}
}
private void selectionChanged(object sender, SelectionChangedEventArgs e)
{
if (listBox.ItemsSource != null)
{
listBox.Visibility = Visibility.Collapsed;
textBox.TextChanged -= textChanged;
if (listBox.SelectedIndex != -1)
{
textBox.Text = listBox.SelectedItem.ToString();
}
textBox.TextChanged += textChanged;
}
}
private void textChanged(object sender, TextChangedEventArgs e)
{
SetValue(TextProperty, textBox.Text);
updateListBox();
}
private void updateListBox()
{
if (String.IsNullOrEmpty(textBox.Text) || Suggestions == null || Suggestions.Count == 0)
{
return;
}
List<string> autoList = new List<string>();
foreach (string item in Suggestions)
{
if (CaseSensitive && item.StartsWith(textBox.Text))
{
autoList.Add(item);
}
if (!CaseSensitive && item.ToUpper().StartsWith(textBox.Text.ToUpper()))
{
autoList.Add(item);
}
}
if (autoList.Count > 0)
{
listBox.ItemsSource = autoList;
listBox.Visibility = Visibility.Visible;
}
else
{
listBox.Visibility = Visibility.Collapsed;
listBox.ItemsSource = null;
}
}
}
}
如果我使用此用户控件,例如
<local:AutoCompleteTextBox x:Name="autocomplete" Suggestions="{Binding SomeList}" Text="{Binding SomeText,UpdateSourceTrigger=PropertyChanged}"/>
然后数据绑定对于建议列表非常有效,但它不适用于文本框的文本。我不知道自己做错了什么。有人可以帮忙吗?我将非常感激。
答案 0 :(得分:0)
依赖项属性的CLR包装器的getter和setter应仅调用GetValue
和SetValue
方法。如果您在设置属性时想要其他任何内容,则应该使用回调。
您可能还应该将Text
属性的默认绑定模式设置为TwoWay
:
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
static AutoCompleteTextBox()
{
SuggestionsProperty = DependencyProperty.Register(nameof(Suggestions), typeof(List<string>), typeof(AutoCompleteTextBox));
TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(AutoCompleteTextBox),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnTextChanged)) { BindsTwoWayByDefault = true });
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AutoCompleteTextBox ctrl = d as AutoCompleteTextBox;
ctrl.textBox.Text = e.NewValue as string;
}