Прокрутка ListView CollectionViewSource в группу элементов на основе текста текстового поля поиска в C#
Я работаю над приложением Windows Phone 8.1 в XAML и C#.
У меня есть список, чей источник элемента установлен в CollectionViewSource
называется MusicSource
, На бэкэнде в C# у меня есть ObservableCollection
называется source
и следующий код заполняет его, получая все музыкальные файлы на телефоне, группирует их по исполнителям и затем помещает их в CollectionViewSource, который показывает их в виде списка:
var folders = await folder.GetFoldersAsync();
if (folders != null)
foreach (var fol in folders)
await getMusic(fol);
var files = await folder.GetFilesAsync();
foreach (var file in files)
{
MusicProperties musicProperties = await file.Properties.GetMusicPropertiesAsync();
this.source.Add(new Music((musicProperties.Artist.Length > 0) ? musicProperties.Artist : "Custom", (musicProperties.Title.Length > 0) ? musicProperties.Title : file.Name, (musicProperties.Album.Length > 0) ? musicProperties.Album : "Custom Album", file.Path));
}
itemSource = AlphaKeyGroup<Music>.CreateGroups(source, CultureInfo.CurrentUICulture, s => s.Artist, true);
this.MusicSource.Source = itemSource;
Ниже приведена сторона XAML:
<Page.Resources>
<DataTemplate x:Key="GroupTemplate">
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1">
<TextBlock x:Name="SongTitle" Text="{Binding Title}"
Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock x:Name="ArtistName" Text="{Binding Album}"
Style="{ThemeResource ListViewItemContentTextBlockStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
<CollectionViewSource x:Name="MusicSource" IsSourceGrouped="true" />
<DataTemplate x:Key="headerTemplate">
<StackPanel HorizontalAlignment="Stretch" Width="{Binding ActualWidth, ElementName=contentList}">
<TextBlock Text="{Binding Key}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<ListView
x:Name="contentList"
SelectionMode="Multiple"
ItemsSource="{Binding Source={StaticResource MusicSource}}"
ItemTemplate="{StaticResource GroupTemplate}">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True" HeaderTemplate="{StaticResource headerTemplate}"/>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
<Border
x:Name="SearchBorder"
Background="White">
<TextBox
x:Name="Search" TextChanged="TextBox_TextChanged" />
</Border>
</Grid>
Таким образом, я получаю что-то вроде следующего в списке:
Майкл Джексон
- Плохой
- опасно
- Триллер
Eminem
- Не бояться
- Монстр
У меня есть текстовое поле с именем search
это предназначено для поиска по списку.
Что должно произойти, так это то, что, когда я набираю текстовое поле, просмотр списка прокручивается до ближайшей группы, заголовок группы которой соответствует тексту в текстовом поле. Поэтому, если я наберу "Em", просмотр списка должен сразу прокрутиться вниз до категории "Eminem".
Как бы я этого достиг?
Кроме того, возможно ли сделать то же самое, кроме прокрутки до элемента, songTitle
атрибут совпадает с текстом в текстовом поле?
2 ответа
Вау, это одна сложная проблема. Я сделал что-то очень похожее в прошлом. По сути, вы хотите AJAX-решение для набора ObservableCollection. Вы можете достичь этого, написав функцию поиска через AlphaKeyGroup с помощью сопоставления регулярных выражений. Это должно вернуть ваш элемент, к которому нужно перейти.
Что касается поиска каждого изменения текста, вам нужно прикрепить событие TextChanged к текстовому полю следующим образом:
<TextBox x:Name="my_search" TextChanged="my_search_TextChanged" Grid.Row="0" />
Когда он находит совпадение, вы хотите перейти к этому элементу с помощью
contentList.ScrollIntoView(matching_element);
Хорошо, опубликовано решение для Windows Phone 8.1: ListView Searching Project
Основные моменты включали в себя процедуру поиска, добавленную в класс GroupKey.
public T FindMatch(string pattern, GetKeyDelegate getKey)
{
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
foreach (T item in this.Items)
{
string key = getKey(item);
Match match = rgx.Match(key);
if (match.Success)
return item;
}
return default(T);
}
Задавать ICollectionView.Filter
обратный вызов для фильтрации вашего списка каждый раз, когда TextChanged
Событие возникает так:
private void TextBox_TextChanged(object sender, TextChangedEven)
{
ICollectionView view = lvTest.ItemsSource as ICollectionView;
string txtToSearch = searchTextBox.Text;
view.Filter = (p) => { return ((Music)p).ArtistName.Contains(txtToSearch); };
view.Refresh();
}
Таким образом, вам не нужно решать, где установить текущий элемент, если он соответствует более чем одному имени исполнителя.