问题
I am working on a Windows Phone 8.1 app in XAML and C#.
I have a listview, whose item source is set to a CollectionViewSource
called MusicSource
. On the backend in C#, I have an ObservableCollection
called source
and the following code populates it by getting getting all the music files on the phone, groups it by artist and then puts them in the CollectionViewSource, which shows them in the listview:
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;
The following is the XAML side of it:
<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>
So I get something like the following in the listview:
Michael Jackson
- Bad
- Dangerous
- Thriller
Eminem
- Not Afraid
- The Monster
I have a textbox with the name search
that is intended to search the listview.
What should happen is that as I type in the textbox, the listview scrolls to the nearest group whose group header matches the text in the textbox. So if I type "Em", the listview should immediately scroll downwards to the "Eminem" category of items.
How would I achieve this?
Also, is it possible to do the same thing, except scroll to the item whose songTitle
attribute matches that of the text in the textbox?
回答1:
Wow that is one hard problem. I've done something very similar in the past. Basically you want an AJAX solution to an ObservableCollection set. You can achieve this by writing a search function through your AlphaKeyGroup with the help from a Regular Expression match. This should return your element that it needs to scroll to.
As for getting to it to search each text change you need to attach a TextChanged Event to the textbox like so:
<TextBox x:Name="my_search" TextChanged="my_search_TextChanged" Grid.Row="0" />
When it finds a match, you want to scroll to that element with
contentList.ScrollIntoView(matching_element);
Okay, posted the Windows Phone 8.1 Solution : ListView Searching Project
Highlights included the Searching Routine that are added to the GroupKey class.
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);
}

回答2:
Set ICollectionView.Filter
callback to filter your listview everytime the TextChanged
event is raised like so:
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();
}
That way you avoid having to decide where to set the current item when it matches more than one Artist name.
来源:https://stackoverflow.com/questions/25538411/scrolling-a-listview-collectionviewsource-to-a-group-of-items-based-on-a-search