Scrolling a ListView CollectionViewSource to a group of items based on a search textbox's text in C#

我怕爱的太早我们不能终老 提交于 2019-12-11 02:24:42

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!