Filtering a list in a Windows Phone 8.1 app

I am working on a Windows Phone 8.1 application in XAML / C #.

I have a listview whose origin source is CollectionViewSource

called MusicSource

. On the backend in C #, I have a ObservableCollection

called source

and the following code populates it, getting all the music files on the phone, groups it by artist, and then puts them in a CollectionViewSource that shows them in a 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;

      

Below is its XAML side:

<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, the list contains something like the following:

Michael Jackson

  • Bad
  • Dangerous
  • Thriller
  • monster

Eminem

  • Not afraid
  • Monster

When the user enters the search text box, the list should be filtered and only display items that match the text in the search text box. For example, if I find "Monster" in the search box, the list is immediately filtered and only displays "Monster" in the band header "Michael Jackson" and "The Monster" in the band header "Eminem".

How can I achieve this?

+3


source to share


2 answers


I have a similar task for Windows 8 Store Application - filtering a grouped list directly when the user enters sample text. I have created a View Model containing FilterText and Groups. A group has two observable collections, AllItems (a complete list of items) and Items (visible on screen). When the FilterText changes, I go through each element within each group and determine whether to store it in elements or not. Here's 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 method remains intact if the item is still displayed after filtering. The animation looks correct because the system sees it as a series of inserts / deletes on the observable collection (a complete update of the list will delete and restore the entire list, which will not animate correctly).

It works fine (assuming AllItems is not a million rows), but I am hitting a weird exception in a particular case - when ListView.GroupStyle.HidesIfEmpty=True

some (or all?) Of the groups are cleared during the upgrade process - the process crashes in Windows.UI.Xaml.dll. No exception gets caught in C # code (UnhandledException and TaskScheduler.UnobservedTaskException are silent). Nothing can be used in the event log. The debugger cannot display details or even attach to the crash process. If I add await Task.Delay()

it is more stable, but may still fail from time to time. If I install ListView.GroupStyle.HidesIfEmpty=False

, everything works stably!

+1


source


Hmm, it's not difficult at all, all you have to do is first make a list of your songs, then find items among it and make the unwanted COLLAPSED item! for convenience, use listview control and search in your elements. Note that you must cache all the listview controls in the mother's control first for future searches to avoid re-ingesting all music files and also disabling the search mode.



0


source







All Articles