【问题标题】:Can't bind ListView to ViewModel无法将 ListView 绑定到 ViewModel
【发布时间】:2018-04-09 23:31:50
【问题描述】:

这是我的 ViewModel:

namespace DietAndFitness.ViewModels
{
public class FoodItemsViewModel 
{
    private SQLiteAsyncConnection database;

    public static ObservableCollection<GlobalFoodItem> FoodItems { get; set; }

    public FoodItemsViewModel(SQLiteAsyncConnection _database)
    {
        database = _database;        
    }

    public async void LoadList()
    {
         FoodItems = new ObservableCollection<GlobalFoodItem>(await database.Table<GlobalFoodItem>().ToListAsync());
    }
    public void Add()
    {
        FoodItems.Add(new GlobalFoodItem("Item"));
    }
}
}

我想将此虚拟机的一个实例绑定到我的视图中的列表,但我无法让它正常工作。我设法开始工作的唯一事情是:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:DietAndFitness.ViewModels"
         x:Class="DietAndFitness.Views.FoodDatabasePage">
<ContentPage.Content>
    <StackLayout>
        <ListView ItemsSource="{Binding FoodItems}" RowHeight="120">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <ContentView Padding="5">
                            <Frame OutlineColor="Accent" Padding="10">
                                <StackLayout>
                                    <Grid HorizontalOptions="Center">
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto"/>
                                        </Grid.RowDefinitions>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto"/>
                                            <ColumnDefinition Width="Auto" />
                                        </Grid.ColumnDefinitions>
                                        <Label Text="{Binding Name}" FontSize="22" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.Column="0"/>
                                        <Label Text="{Binding Calories}" FontSize="22" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.Column="1"/>
                                    </Grid>

                                    <Grid HorizontalOptions="Center">
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto"/>
                                            <RowDefinition Height="Auto"/>
                                        </Grid.RowDefinitions>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto"/>
                                            <ColumnDefinition Width="Auto" />
                                            <ColumnDefinition Width="Auto" />
                                        </Grid.ColumnDefinitions>

                                        <Label Text="Carbohydrates" Grid.Row="1" Grid.Column="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
                                        <Label Text="Proteins" Grid.Row="1" Grid.Column="1" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
                                        <Label Text="Fats" Grid.Row="1" Grid.Column="2" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>

                                        <Label Text="{Binding Carbohydrates}" Grid.Row="2" Grid.Column="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
                                        <Label Text="{Binding Proteins}" Grid.Row="2" Grid.Column="1" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
                                        <Label Text="{Binding Fats}" Grid.Row="2" Grid.Column="2" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
                                    </Grid>
                                </StackLayout>
                            </Frame>
                        </ContentView>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Grid HorizontalOptions="Center">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Button x:Name="AddFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Add" Grid.Row="0" Grid.Column="0" Clicked="AddFoodItemButton_Clicked" />
            <Button x:Name="EditFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Edit" Grid.Row="0" Grid.Column="1" />
            <Button x:Name="DeleteFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Delete" Grid.Row="0" Grid.Column="2"  />
        </Grid>
    </StackLayout>
</ContentPage.Content>

但这也有问题:

  1. 如果我在页面构造函数中创建 VM,除非我刷新页面,否则列表不会加载
  2. 如果我在 App.cs 中创建 VM,列表将在第一次尝试时加载,但我似乎无法访问 VM 实例

如何将我的 ListView 绑定到此 VM?是否可以在不使我的 ObservableCollection 静态的情况下这样做? 如果有任何区别,我会在我的应用中使用主从页面(这是详细信息页面之一)。

编辑:这是我在构造函数中所做的。 SQLiteConnection.Database 只是一个包含与数据库的连接的静态类。

    public FoodDatabasePage ()
    {
        FoodDatabase = new FoodItemsViewModel(SQLiteConnection.Database);
        FoodDatabase.LoadList();
        InitializeComponent ();
    }

【问题讨论】:

  • 请显示 DietAndFitness.Views.FoodDatabasePage 的 C# 构造函数
  • @DavidS 我添加了构造函数。

标签: xamarin data-binding xamarin.forms


【解决方案1】:

你有一些东西。

在绑定 ViewModel 时,通常是针对整个 Page 使 ViewModel 成为 BindingContext,特别是在像您这样的情况下,当您有想要稍后访问的方法时。

因此,您需要进行一些更改。

您的页面 XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:DietAndFitness.ViewModels"
        x:Class="DietAndFitness.Views.FoodDatabasePage">
<ContentPage.Content>
    <StackLayout>
        <ListView ItemsSource="{Binding FoodItems}" 
                RowHeight="120">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Frame OutlineColor="Accent" Padding="15">
                            <StackLayout>
                                <Grid HorizontalOptions="Center">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Label Text="{Binding Name}" 
                                        FontSize="22" 
                                        VerticalOptions="CenterAndExpand" 
                                        HorizontalOptions="Center" 
                                        Grid.Row="0" 
                                        Grid.Column="0"/>
                                </Grid>
                            </StackLayout>
                        </Frame>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

检查我是否更改了ListView ItemSource 为您的FoodItemsViewModel 中的集合名称。

另外,你的部分 XAML 消失了,所以我无法完成它。

您的代码隐藏类: (FoodDatabasePage.xaml.cs)

public FoodItemsViewModel FoodDatabase {get; set;}

public FoodDatabasePage ()
{
    InitializeComponent ();
    FoodDatabase = new FoodItemsViewModel(SQLiteConnection.Database);
    BindingContext = FoodDatabase;
}

public override void OnAppearing()
{
    FoodDatabase.LoadList();    
}

这就是“魔法”发生的地方。我们正在创建和存储 FoodItemsViewModel 的对象,以便您稍后可以根据需要访问它,然后我们将最近创建的这个对象设置为页面的 BindingContext(如上所述)。将 ViewModel 设置为Page 的 BindingContext,后者将能够访问FoodItemsViewModel 的任何公共属性。

另外,您会注意到LoadList() 方法已从构造函数中删除,这是因为Page 构造函数中不应有任何繁重的事务(实际上我们应该避免任何事务)。我们将其移至 OnAppearing 回调方法,该方法将在即将显示时由 Page 执行。

注意:每次页面显示在屏幕上时都会调用此方法,因此如果您离开并返回,它将再次被调用。

您的 ViewModel:

namespace DietAndFitness.ViewModels
{
    public class FoodItemsViewModel 
    {
        private SQLiteAsyncConnection database;

        //You don't need it static
        public ObservableCollection<GlobalFoodItem> FoodItems { get; set; }

        public FoodItemsViewModel(SQLiteAsyncConnection _database)
        {
            database = _database;        
        }

        public async void LoadList()
        {
            // separated it only for readability.
            var items = await database.Table<GlobalFoodItem>().ToListAsync();
            FoodItems = new ObservableCollection<GlobalFoodItem>(items);
        }

        public void Add()
        {
            FoodItems.Add(new GlobalFoodItem("Item"));
        }
    }
}

这几乎保持不变。唯一的变化是将 static 限定符删除到 FoodItems 属性,因为它不再需要它了。

以上只是Mvvm和DataBinding的基础。您还需要考虑其他一些事项,例如 INotifiedPropertyChangedCommands 和其他重要事项。

你可以找到更多关于这个here的信息

希望这会有所帮助。-

PS:我无法检查您的所有代码是否有任何错误。

【讨论】:

  • 我进行了更改,并使用完整的 FoodDatabasePage.xaml 更新了帖子,但它仍然无法正常工作。页面是空白的,只有按钮。
  • 好的,我已经设法解决了。显然,必须实现 INotifiyProperty 才能使 ListView 绑定工作并填充列表。不知何故,我希望 ListView 在没有它的情况下至少第一次被填充,只是不更新​​。谢谢你的回答,很有见地。
猜你喜欢
  • 2018-05-10
  • 2021-09-23
  • 2012-03-18
  • 1970-01-01
  • 2019-11-09
  • 2019-02-03
  • 2012-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多