Seleccionar elementos de WPF cuadro de lista a través del teclado de búsqueda “de escritura anticipada”
Pregunta
Tengo un cuadro de lista de control de WPF y me gustaría para permitir al usuario cambiar el elemento seleccionado mediante el uso de escritura anticipada. El comportamiento que estoy buscando es exactamente igual que el Explorador de Windows. A medida que continúe a escribir el texto de un nombre de carpeta, la lista se mantenga seleccionado el elemento más correcto.
Por ejemplo asumir esta estructura de carpetas:
OtherFolderName
MyFirstFolder
MyFirstFileFolder
MyFirstList
Si selecciona OtherFolderName
con el ratón, a continuación, empezar a escribir MyFirstF
se seleccionará el MyFirstFolder
artículo, pero si continúa escribiendo MyFirstFi
se seleccionará el MyFirstFileFolder
artículo.
Mi WPF cuadro de lista no presenta este behavor, espero que pueda fácilmente activarlo, como el cuadro de lista WinForms viejos hizo exactamente esto.
Solución
Tome un vistazo a la clase TextSearch, específicamente la propiedad TextSearch.TextPath adjunto:
<ListBox TextSearch.TextPath="FolderName" ... />
La propiedad TextSearch.TextPath permite la búsqueda de texto y especifica cómo extraer el texto de búsqueda de cada elemento. En este caso asumí cada uno de sus objetos de carpeta tiene una propiedad denominada "FolderName".
Si esto no hace todo lo que está buscando, es probable que tenga que poner en práctica su propia búsqueda, ya que la función TextSearch no es particularmente modificables. Para hacer esto:
- Controle el evento TextInput
- Compare el tiempo de la TextInput TextInput actual con el anterior. Si lo suficientemente cerca, anexados para prefijo de cadena establece lo contrario al carácter único escrito.
- Buscar todos los artículos para el prefijo dado y si SelectedItem conjunto encontrado.
Yo construiría esto como una clase separada usando una propiedad asociada, similar a la clase incorporada TextSearch.
Otros consejos
I utilizar un cuadro de texto oculto que aparece brevemente mientras la persona está escribiendo, y se restablece después de un par de segundos y desaparece, por lo que no utilizar la concordancia en su contenido después de que el temporizador expira. La persona tendría que escribir en el cuadro de lista, y su caso KeyUp
va a llenar en el cuadro de texto debido a la unión de SearchText
. Cuando se llena SearchText
, se dispara MyFilteredItems()
para realizar una correspondencia entre el texto y el cuadro de lista. Entonces, si las prensas de persona a entrar, la selección iría en otro cuadro de texto (que no figura en el XAML, pero se proporciona como se ha comentado en el código) y ser limpiado de lstPickList
. El cuadro de texto se borra y luego se reinicia el temporizador.
XAML:
<TextBox Name="txtPicker" IsReadOnly="True" Foreground="LightGreen" FontFamily="Consolas" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<ListBox Name="lstPickList" Grid.Row="1" ItemsSource="{Binding MyFilteredItems}" KeyUp="lstPickList_KeyUp"></ListBox>
Y entonces este es el correspondiente código subyacente:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Timer t = new Timer();
public System.Windows.Threading.DispatcherTimer tCleanup =
new System.Windows.Threading.DispatcherTimer();
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
_searchText = value;
OnPropertyChanged("SearchText");
OnPropertyChanged("MyFilteredItems");
}
}
public List<string> MyItems { get; set; }
public IEnumerable<string> MyFilteredItems
{
get
{
if (SearchText == null) return MyItems;
return MyItems.Where(x => x.ToUpper().StartsWith(SearchText.ToUpper()));
}
}
public MainWindow()
{
InitializeComponent();
MyItems = new List<string>() { "ABC", "DEF", "GHI" };
this.DataContext = this;
t.Interval = 1000;
t.Elapsed += new ElapsedEventHandler(timerCounter);
tCleanup.Interval = new TimeSpan(0,0,1);
tCleanup.Tick += new EventHandler(cleanupCounter_Tick);
txtPicker.Visibility = Visibility.Collapsed;
tCleanup.Start();
}
private static int counter = 0;
protected void timerCounter(object sender, ElaspedEventArgs e)
{
counter++;
}
protected void cleanupCounter_Tick(object sender, EventArgs e)
{
if (counter > 2 && txtPicker.Visibility == Visibility.Visible)
txtPicker.Visibility = Visibility.Collapsed;
}
private void lstPickList_KeyUp(object sender, KeyEventArgs e)
{
ListBox lst = (ListBox)sender;
string strg = Convert.ToString(e.Key.ToString().Replace("D",""));
if (counter < 2)
{
txtPicker.Visibility = Visibility.Visible;
t.Start();
if (strg == "Return")
{
txtPicker.Text += "{Enter}";
SearchText += "{Enter}";
}
else
{
txtPicker.Text += strg;
SearchText += strg;
}
}
else
{
SearchText = strg;
txtPicker.Text = strg;
t.Stop();
counter = 0;
t.Start();
}
if (strg == "Return")
{
// This next line would be if you had a "selected items" ListBox to store the item
// lstSelectedList.Items.Add(lstPickList.SelectedItem);
lstPickList.Items.Remove(lstPickList.SelectedItem);
t.Stop();
txtPicker.Visibility = Visibility.Collapsed;
counter = 0;
txtPicker.Text = String.Empty;
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}