Xamarin Forms ListView 分组内容未绑定 [英] Xamarin Forms ListView Grouping Contents not Binding

查看:28
本文介绍了Xamarin Forms ListView 分组内容未绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用 ListView Grouping 来获取要在标签中显示的列表内容时遇到问题.ListView 的 ItemsSource 和 GroupDisplayBinding 设置正确,但 ListView 内的标签不会显示任何内容(我什至尝试将其设置为文字字符串).我所有的清单,以及我的清单清单"正在正确填充.我已经翻阅了文档、网站文章和教学视频,但仍然卡住了.有人可以指出我正确的方向吗?总的来说,我仍然很难处理数据绑定和 MVVM.谢谢.

I am having issues using ListView Grouping to get the content of my lists to display in labels. The ListView's ItemsSource and GroupDisplayBinding are setting properly, but the label inside the ListView will not display anything (I even tried setting it to a literal string). All of my lists, as well as my "list of lists" are populating correctly. I have poured over the documentation, website articles, and instructional videos but I am still stuck. Can someone point me in the right direction? I am still having a tough time with databinding and MVVM in general. Thanks.

查看:

<ListView ItemsSource="{Binding OAllLists}"
              GroupDisplayBinding="{Binding Type}"
              IsGroupingEnabled="true"
              HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Label Text="{Binding Items}">
                </Label>
            </DataTemplate>
        </ListView.ItemTemplate>
</ListView>

视图模型:

class OrganizedViewModel
{
    public ObservableCollection<Categories> OAllLists { get; set; }

    public OrganizedViewModel()
        {
        OAllLists = new ObservableCollection<Categories>();
                foreach(Categories value in Categories.AllLists)
                {
                    OAllLists.Add(value);
                }
        }
} 

型号:

public class Categories
    {
        public static List<Categories> AllLists { get; set; } = new List<Categories>();
        
        public static Categories FruitList { get; set; } = new Categories("Fruit");
        public static Categories VegetableList { get; set; } = new Categories("Vegetables");
        ///Each type of item has its own static Categories object

        public string Type { get; set; }
        public List<string> Items { get; set; } = new List<string>();
    }

整理物品的方法:

class OrganizeIt
{
    public void OrganizeItems(List<string> groceries)
    {
        foreach (string value in groceries) ///Checks each value for keywords
        {
            if (Keywords.fruitKeys.Any(value.Contains))
            {
                Categories.FruitList.Items.Add(value);
            }
            else if (Keywords.vegetableKeys.Any(value.Contains))
            {
                Categories.VegetableList.Items.Add(value);
            }
        }

        ///Adds each type of list to "list of lists" if it contains values
        if (Categories.FruitList.Items.Any())
        {
            Categories.AllLists.Add(FruitItem);
        }
        if (Categories.VegetableList.Items.Any())
        {
            Categories.AllLists.Add(Categories.VegetableList);
        }

编辑

每个评论推荐的新课程.我还在 GroupedList 填充的 ViewModel 中创建了另一个 Observable 集合(两者都正常工作).名称更改是为了清楚起见.

New class per comment's recommendation. I also created another Observable Collection in the ViewModel populated by GroupedList (both are working correctly). Name changes are for clarity.

public class Groceries : List<string>
    {
        public string Category { get; set; }

        public static List<Groceries> GroupedList { get; set; } = new List<Groceries>();
        public static Groceries Fruits { get; set; } = new Groceries("Fruit");
        public static Groceries Vegetables { get; set; } = new Groceries("Vegetables");

        public Groceries(string s)
        {
            Category = s;
        }
    }

新视图:

<ListView ItemsSource="{Binding OGroupedList}"
                  GroupDisplayBinding="{Binding Category}"
                  IsGroupingEnabled="true">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Label Text="{Binding .}"
                               VerticalOptions="FillAndExpand"/>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

编辑 2

这就是我现在在 ViewModel 中填充 ObservableCollection 的方式:

This is how I'm populating the ObservableCollection in my ViewModel now:

class OrganizedViewModel
    {
        public ObservableCollection<Groceries> OGroupedList { get; set; }
        public string Category { get; set; }

         public OrganizedViewModel()
        {
            OGroupedList = new ObservableCollection<Groceries>();
            foreach (Groceries value in Groceries.GroupedList)
            {
                OGroupedList.Add(value);
            }
    }

编辑 3

这是组织项目的方法.它接受一个字符串列表并检查每个列表项以查看它是否包含与某个类别相关联的任何关键字(例如,apple"包含在2bags of apples"中).如果是,则将列表项添加到相应的 Groceries 对象中.

This is the method for organizing items. It takes in a string list and checks each list item to see if it contains any of the keywords associated with a certain category (ex. "apple" is contained in "2 bags of apples"). If so, the list item is added to the corresponding Groceries object.

class OrganizeIt
{
    public void OrganizeItems(List<string> groceries)
    {
        foreach (string value in groceries)
        {
            if (Keywords.fruitKeys.Any(value.Contains))
            {
                Groceries.Fruits.Add(value);
            }
            else if (Keywords.vegetableKeys.Any(value.Contains))
            {
                Groceries.Vegetables.Add(value);
            }
    }
    if (Groceries.Fruits.Any())
    {
        Groceries.GroupedList.Add(Groceries.Fruits);
    }
    if (Groceries.Vegetables.Any())
    {
        Groceries.GroupedList.Add(Groceries.Vegetables);
    }
}

这是在 MainPage 上调用方法的地方.UnorganizedList 是根据用户输入填充的.

Here is where the method is called on the MainPage. UnorganizedList is populated from user input.

private void SortItems()
{
    OrganizeIt o = new OrganizeIt();
    o.OrganizeItems(UnorganizedList);
}

解决方案

所需要做的就是将 Label 的绑定更改为仅 {Binding}(没有句点),并删除x:DataType".线.下面是修改后的视图,以防万一这对任何人都有帮助:

All that was needed was to change the Label's binding to just {Binding} (with no period), as well as remove the "x:DataType" line. Below in the revised View in case this helps anybody:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodels="clr-namespace:GroceryListMobile.ViewModels"
             xmlns:mvvm="clr-namespace:MvvmHelpers;assembly=MvvmHelpers"
             xmlns:model="clr-namespace:GroceryListMobile.Models"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             
             x:Class="GroceryListMobile.Views.OrganizedView"
             x:Name="Organized">
    <ContentPage.BindingContext>
        <viewmodels:OrganizedViewModel/>
    </ContentPage.BindingContext>

    <ContentPage.Content>

        <ListView ItemsSource="{Binding OGroupedList}"
                  GroupDisplayBinding="{Binding Category}"
                  IsGroupingEnabled="true">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Label Text="{Binding}"
                               VerticalOptions="FillAndExpand"/>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

推荐答案

你需要一个 作为你的 的子节点:

You need a <ViewCell> as the child node of your <DataTemplate>:

<ListView ItemsSource="{Binding OAllLists}"
              GroupDisplayBinding="{Binding Type}"
              IsGroupingEnabled="true"
              HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <Label Text="{Binding .}">
                    </Label>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
</ListView>

ListView.ItemTemplate 总是需要一个 DataTemplate,它是一个 Cell.

ListView.ItemTemplate always requires a DataTemplate which is a Cell.

编辑接下来要解决的是用作 ItemSource 的集合.使用分组时,它需要是集合的集合.我会尝试的是更改您的模型类:

EDIT The next thing to address is the collection used as the ItemSource. When using grouping, that needs to be a collection of collections. The thing I would try is to change your model class:

public class Categories : List<string>
    {
        public static List<Categories> AllLists { get; set; } = new List<Categories>();
        
        public static Categories FruitList { get; set; } = new Categories("Fruit");
        public static Categories VegetableList { get; set; } = new Categories("Vegetables");
        ///Each type of item has its own static Categories object

        public string Type { get; set; }
    }

并在上面的 Xaml 中查看修改后的绑定.

And see the revised Binding in the Xaml above.

考虑分组 ListView 的一种方式是它是一个列表列表.外"列表元素有一个绑定到 GroupDisplayBinding 的成员,而内部"是绑定到 GroupDisplayBinding 的.列表元素的成员绑定到 DataTemplate 中的元素.

A way to think about grouped ListViews is that it is a list of lists. The "outer" list elements have a member bound to GroupDisplayBinding, and the "inner" list elements have members bound to the elements in the DataTemplate.

在您的情况下,外部"collection 是 ObservableCollection,因此 GroupDisplayBinding 将绑定到 Categories 上的某些内容.然后每个类别需要是一个集合本身.在修订版中,这是一个列表,因此 DataTemplate 被赋予一个字符串作为 BindingContext.所以标签只需要绑定到字符串(因此 Binding ..

In your case, the "outer" collection is the ObservableCollection, so the GroupDisplayBinding will bind to something on Categories. Then each Categories needs to be a collection itself. In the revisions, that is a List, so the DataTemplate is given a string as the BindingContext. So the Label just needs to bind to the string (hence the Binding ..

其余的代码必须稍微调整一下,因为曾经是 Categories 成员的 Items 集合现在是 Categories 对象本身(通过继承).

The rest of the code would have to adjust a bit as the Items collection that used to be a member of Categories is now the Categories object itself (through inheritance).

编辑 2

我创建了一个新应用,其中包含与您拥有的 ListView 完全一样的页面(您的第一个 Edit 下的 New View),以及与第一个 Edit 下完全相同的 Groceries 类.

I created a new app with a page containing the ListView exactly as you have it (New View under your first Edit), and the Groceries class exactly as it is under the first Edit.

我稍微修改了 OrganizedViewModel.它似乎没有使用类别,我想确保 OGroupedList 填充了类别:

I revised OrganizedViewModel a little. It doesn't seem to use Category, and I wanted to make sure OGroupedList was being populated with Categories:

    class OrganizedViewModel
    {
        public ObservableCollection<Groceries> OGroupedList { get; set; }

        public OrganizedViewModel()
        {
            OGroupedList = new ObservableCollection<Groceries>();
            OGroupedList.Add(Groceries.Fruits);
            OGroupedList.Add(Groceries.Vegetables);
        }
    }

最后,我在创建页面时在页面的构造函数中为每个类别添加了一些项目:

Finally, I added some items to each category in the page's constructor when creating the page:

        public Page1()
        {
            InitializeComponent();

            var bc = new OrganizedViewModel();
            var index = 1;
            foreach (var g in bc.OGroupedList)
            {
                g.Add(g.Category + $" {index++}");
                g.Add(g.Category + $" {index++}");
                g.Add(g.Category + $" {index++}");
                g.Add(g.Category + $" {index++}");
            }

            BindingContext = bc;
        }

对我来说,这是正确显示带有项目名称和组标题的列表.所以无论问题是什么,你仍然看到的是其他地方.分组后的 ListView 的基本类结构和 Xaml 定义现在是正确的.

And for me this is showing the lists with the item names and the group headers correctly. So whatever the problem is you're still seeing is somewhere else. The basic class structure and Xaml definition for the grouped ListView is now correct.

我的两个猜测:

  1. 确保正确填充集合.
  2. 尝试更改为 public class Groceries : ObservableCollection.如果在最初呈现页面后 Groceries 列表可以更改,这一点很重要.
  1. Make sure the collections are being populated correctly.
  2. Try changing to public class Groceries : ObservableCollection<string>. This is important if the Groceries list can change after the page is initially rendered.

编辑 3

到了它的底部.以下是应该可以帮助您前进的评论:

Got to the bottom of it. Here are comments that should get you going:

  1. {Binding .}{Binding} 之间存在细微差别.在您的情况下, {Binding} 有效但 {Binding .} 无效.

  1. There are subtle differences between {Binding .} and {Binding}. In your case, {Binding} works but {Binding .} does not.

更常见的情况是元素是对象,而不是字符串,所以这样的事情也可以解决:

A more common case is where the elements are objects, not strings, so something like this also solves it:

public class GroceryItem
{
    public string Name { get; set; }
}
...
public class Groceries: ObservableCollection<GroceryItem>
{
    ...

这当然需要在添加到 Groceries 集合时进行更改,并且标签需要为 Text={Binding Name}".

This will, of course, require changes in adding to the Groceries collection and the Label needs to be Text="{Binding Name}".

  1. 当我尝试 #2 时,Visual Studio 在进行更改后导致 x:DataType 出现问题.它在 IDE 中对绑定上下文的检测不是最好的,所以我不得不删除那行.

  1. When I tried #2, Visual Studio was causing problems with x:DataType after making that change. Its detection of binding contexts in the IDE is not the best, so I had to delete that line.

在这里使用静态集合时要小心.如果您添加项目、整理、返回并再次整理,应用将崩溃,因为它会尝试重新添加水果和蔬菜收藏.

Be careful when using static collections here. If you add items, organize, go back, and organize again, the app will crash because it tries to re-add the fruits and vegetables collections.

这篇关于Xamarin Forms ListView 分组内容未绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆