简体   繁体   中英

Filtering a listview in Windows Phone 8.1 app

I am working on a Windows Phone 8.1 app in XAML/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
  • Monster

Eminem

  • Not Afraid
  • The Monster

When the user types in the search textbox, the listview should be filtered and only show the items that match the text in the search textbox. So for example, if I type "Monster" in the searchbox, the listview is immediately filtered and only shows "Monster" within the "Michael Jackson" group header and "The Monster" within the "Eminem" group header.

How would I achieve this?

I've got similar task for Windows 8 Store Application - live filtering of the grouped list view when user type sample text. I've made a view model that holds FilterText and Groups. Group has two observable collections - AllItems (complete list of items) and Items (visible on screen). When FilterText is changed I'm going through each item inside each group and determine whether to keep it in Items or not. Here is some code:

...

var rule = x => GetTextToFilter(x).IndexOf(filterText,
                StringComparison.CurrentCultureIgnoreCase) >= 0;

foreach (var group in Groups)
{
    group.UpdateVisibleItems(rule);
}

...

void UpdateVisibleItems(Func<ItemViewModel, bool> rule)
{
    for (int i = 0, j = 0; i < AllItems.Count; i++)
    {
        var item = AllItems[i];
        if (rule(item))
        {
            if (j == _Items.Count || (j < _Items.Count && _Items[j] != item))
            {
                _Items.Insert(j, item);
            }

            j++;
        }
        else
        {
            if (j < _Items.Count && _Items[j] == item)
            {
                _Items.RemoveAt(j);
            }
        }
    }
}

This way selection stays intact if item is still visible after filtering. Animations looks correct because system see it as series of insert/remove on observable collection (complete list refresh will make whole list removed and restored which will be animated wrong).

It works great (provided that AllItems is not millions of rows), but I'm hitting strange exception in specific case - when ListView.GroupStyle.HidesIfEmpty=True and some (or all?) groups are cleared in the process of update - process crashes within Windows.UI.Xaml.dll. No exception is trapped in C# code (UnhandledException and TaskScheduler.UnobservedTaskException are silent). Nothing usable in Event Log. Debugger fails to display details or even attach to the failing process. If I add await Task.Delay() it will be way more stable, but still may fail from time to time. If I set ListView.GroupStyle.HidesIfEmpty=False - all is stable working!

Hmm, it doesn't seem hard at all , all you have to do is to first make your songs list then search among it's items and make unwanted item COLLAPSED! for ease of work use listview control and search in its items . Note that first u have to make a cache of all listview controls in mother control for future searches , to avoid getting all musics again and also disabling search mode.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM