简体   繁体   中英

Populating a grouped ListView from a list

I have a big list of objects retrieved through parsing a JSON file. Right now I'm binding said list to a ListView but the list is unwieldy and I wanted to divide it in separate groups for an ease of use. I tried following several different guides but I'm unable to prepare my data in the correct way. If I manually initialize one of my sorted lists with some items they do show up so the code is indeed functioning.

My grouping model:

public class SortedItem
{
    public string Header { get; set; }
    public List<Item> Items { get; set; }

    public SortedItem(string header)
    {
        Header = header;
    }
}

My object model:

public class Item
{
    public string item { get; set; }
    //public int icon { get; set; }

    private string ico;
    public string icon
    {
        get { return ico; }
        set { ico = "Icons/" + value + ".png"; }
    }

    public int id { get; set; }
    public string slot { get; set; }
    public string scrip { get; set; }
    public Reduce reduce { get; set; }
    public int lvl { get; set; }
    public string zone { get; set; }
    public int time { get; set; }
}

Right now my XAML is as follows:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
     x:Class="Eorzea_Gatherer.Pages.NodesPage"
     xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" 
     ios:Page.UseSafeArea="true"
     BackgroundColor="#F4F4F4">
<!--https://xamarinhelp.com/safeareainsets-xamarin-forms-ios/-->
<ListView x:Name="nodesListView"
      IsGroupingEnabled="True"
      GroupDisplayBinding="{Binding Header}"
      HasUnevenRows="True"
      BackgroundColor="#F4F4F4"
      Margin="30, 30, 30, 0">
<ListView.ItemTemplate>
    <DataTemplate>
        <ViewCell>
            <Grid Padding="0, 5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Image Source="{Binding icon}"
                       HeightRequest="50"
                       WidthRequest="50"
                       Grid.Column="0"/>
                <StackLayout Grid.Column="1">
                    <Label Text="{Binding item}"
                           TextColor="#171717"
                           FontSize="13"
                           FontFamily="SegoeUI"/>
                    <!--https://forums.xamarin.com/discussion/97996/binding-more-than-one-property-in-listview-->
                    <Label TextColor="#171717"
                           FontSize="12"
                           FontFamily="SegoeUI">
                        <Label.FormattedText>
                            <FormattedString>
                                <Span Text="{Binding zone}"/>
                                <Span Text=" - "/>
                                <Span Text="{Binding slot}"/>
                            </FormattedString>
                        </Label.FormattedText>
                    </Label>
                    <Label TextColor="#171717"
                           FontSize="12"
                           FontFamily="SegoeUI">
                        <Label.FormattedText>
                            <FormattedString>
                                <Span Text="{Binding time}"/>
                                <Span Text=" - "/>
                                <Span Text="00:00 AM"/>
                            </FormattedString>
                        </Label.FormattedText>
                    </Label>
                </StackLayout>
            </Grid>
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>
</ListView>

The function I'm using to retrieve the list and to bind it as a source to the ListView:

public static List<SortedItem> GetSortedItems()
{
    List<Item> items = GetItems();

    List<SortedItem> sortedItems = new List<SortedItem>()
    {
        new SortedItem("50")
        {
           Items = items.Where(x => x.lvl == 50).ToList()
        },
        new SortedItem("55"),
        new SortedItem("60"),
        new SortedItem("65"),
        new SortedItem("70")
    };

    return sortedItems;
}

With my code I'm able to see the different groups in my ListView (50, 55, ...) but nothing else pops out. I'm sure my issue is taking my objects list and splitting it in an appropriate way but I'm stumped. What puzzles me is during debugging when hovering on the resulting sortedItems I get to see my first group does contain the objects it needs to but they still don't show up in the view.

try this, stolen from James Montemagno

public class Grouping<K, T> : ObservableCollection<T>
{
  public K Key { get; private set; }

  public Grouping(K key, IEnumerable<T> items)
  {
      Key = key;
      foreach (var item in items)
        this.Items.Add(item);
  }
}

var sorted = from item in Items
             orderby item.lvl
             group item by item.lvl into itemGroup
             select new Grouping<int, Item>(itemGroup.Key, itemGroup);

//create a new collection of groups
ItemsGrouped = new ObservableCollection<Grouping<int, Item>>(sorted);

then in your XAML

GroupDisplayBinding="{Binding Key}"

You should make your grouping model inherit from ObservableCollection :

public class SortedItem : ObservableCollection<Item>
{
    public string Header { get; set; }

    public SortedItem(List<Item> list) : base(list)
    {

    }
}

Then sort it like:

public static List<SortedItem> GetSortedItems()
{
    List<Item> items = GetItems();

    List<SortedItem> sortedItems = new List<SortedItem>()
    {
        new SortedItem(items.Where(x => x.lvl == 50).ToList())
        {
            Header = "50"
        },
        new SortedItem(items.Where(x => x.lvl == 55).ToList())
        {
            Header = "55"
        },
        new SortedItem(items.Where(x => x.lvl == 60).ToList())
        {
            Header = "60"
        },
        new SortedItem(items.Where(x => x.lvl == 65).ToList())
        {
            Header = "65"
        },
        new SortedItem(items.Where(x => x.lvl == 70).ToList())
        {
            Header = "70"
        }
    };

    return sortedItems;
}

Moreover, try to implement the INotifyPropertyChanged interface in your model. Otherwise, the UI won't be notified if you changed the model's property at runtime.

I may have missed something, but where are you binding your List<item> Items at? I think your ListView is missing something like ItemSource="{Binding Items}" From there, it looks like your ViewCell is bound fine and should work as expected.

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