【问题标题】:Xamarin Forms ListView Grouping Contents not BindingXamarin Forms ListView 分组内容不绑定
【发布时间】:2021-10-10 22:01:12
【问题描述】:

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

查看:

<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 Collection(两者都正常工作)。名称更改是为了清楚起见。

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 的方式:

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”包含在“2 bag of apples”中)。如果是这样,则将列表项添加到相应的 Groceries 对象中。

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 由用户输入填充。

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

解决方案

只需将标签的绑定更改为 {Binding}(没有句点),并删除“x:DataType”行。如果这对任何人有帮助,请在修改后的视图下方:

<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>

【问题讨论】:

    标签: c# listview xamarin.forms


    【解决方案1】:

    您需要一个&lt;ViewCell&gt; 作为&lt;DataTemplate&gt; 的子节点:

    <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。

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

    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 中查看修改后的 Binding。

    考虑分组 ListViews 的一种方式是它是一个列表的列表。 “外部”列表元素的成员绑定到 GroupDisplayBinding,“内部”列表元素的成员绑定到 DataTemplate 中的元素。

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

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

    编辑 2

    我创建了一个新应用程序,其页面包含与您完全一样的 ListView(您的第一次编辑下的新视图),以及与第一次编辑下完全相同的 Groceries 类。

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

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

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

            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 定义现在是正确的。

    我的两个猜测:

    1. 确保正确填充集合。
    2. 尝试更改为public class Groceries : ObservableCollection&lt;string&gt;。如果 Groceries 列表在页面初始呈现后可以更改,这一点很重要。

    编辑 3

    找到它的底部。以下是可以帮助您前进的 cmets:

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

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

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

    当然,这需要更改添加到 Groceries 集合,并且标签必须是 Text="{Binding Name}"

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

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

    【讨论】:

    • 添加 ViewCell 并不能解决问题。我也尝试过使用 TextCell 而不是标签,但我得到了相同的结果。唯一似乎改变了什么的是 GroupDisplayBinding。如果我将其绑定到“项目”,它将显示每个项目中的每个项目,但不再显示类别名称(“类型”属性)。
    • 修改了上面的答案以解决如何为分组 ListView 设置 ItemsSource
    • 将标签设置为 {Binding .} 仍然没有显示列表的内容,尽管应用程序现在根据每个列表中的值的数量创建正确数量的空标签。我在另一个类中重新创建了我的模型(旧的模型与未使用的代码有点乱)。我已经用底部的新模型编辑了原始帖子。
    • 我尝试复制您所看到的内容,并在上面添加了结果和想法。
    • 我仍然无法让文本显示在标签中。我在您的代码和我的代码中看到的唯一区别是我没有在页面后面的代码中填充我的集合。有什么我需要在那里做的吗?我尝试像您一样手动向“g”添加一个值,但仍然没有。如果我在填充 GroupedList 或 OGroupedList 后放置一个断点,我可以看到它们都包含正确的值。非常感谢帮忙。坦率地说,我已经被困在这件事上很尴尬了。
    【解决方案2】:

    根据您的代码,要显示 OAllLists,您可以像这样创建 OAllLists:

    OAllLists = new ObservableCollection<Categories>
    { 
        new Categories("FruitList"){"aaa","bbb","ccc},
        new Categories("VegetableList"){"ddd","eee"} 
    };
    

    其中,“FruitList”是Categories类中的类型,“aaa”是你在里面添加的字符串数组。您可以查看此链接了解详情(https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-list-appearance#grouping

    【讨论】:

    • 我已经用以下代码填充了我的可观察“列表列表”(名称更改来自我添加到原始帖子的新模型):OGroupedList = new ObservableCollection&lt;Groceries&gt;(); foreach (Groceries value in Groceries.GroupedList) { OGroupedList.Add(value); } 可观察集合正在正确填充,但会这不适用于我的数据绑定?我似乎在为我的评论添加代码块而苦苦挣扎,所以我将把它添加到原始帖子中以便它清晰易读。
    猜你喜欢
    • 2018-01-17
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-11
    相关资源
    最近更新 更多