简体   繁体   中英

Data Binding list to listbox C# XAML (Azure mobile services) UWP

So I have an online database. And I want the query results to be displayed in a listbox. I tried to use data binding but I think I'm doing it totally wrong.

Files

[![enter image description here][1]][1]

Table

class ProductTable
    {
        public string id { get; set; }
        public string Name { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public double Price { get; set; }
        public string Type { get; set; }
        public string Seller { get; set; }
        public int Expiration { get; set; }
    }

MainViewModel.cs

namespace Test
{
    class MainViewModel
    {
        IMobileServiceTable<ProductTable> product = App.MobileService.GetTable<ProductTable>();

        //In this method, load your data into Products
        public async void Load()
        {
            // This query filters out completed TodoItems.
            MobileServiceCollection<ProductTable, ProductTable> Products = await product
                .Where(ProductTable => ProductTable.Price == 15)
                .ToCollectionAsync();

            // itemsControl is an IEnumerable that could be bound to a UI list control
            IEnumerable itemsControl = Products;
        }
    }
}

XAML

    <Page x:Name="PAGE"
xmlns:local="clr-namespace:Test;assembly=Version1">
        <Grid>
            <Grid.DataContext>
                <local:MainViewModel />
            </Grid.DataContext>
            <ListBox Margin="10,10,10,100" x:Name="lb" ItemsSource="{Binding Products}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Name}" FontSize="10"></TextBlock>
                            <TextBlock Text="{Binding Title}" FontSize="10"></TextBlock>
                            <TextBlock Text="{Binding Description}" FontSize="10"></TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Page>

Solution

Table needs JSON property name explicitly declared otherwise you won't be able to use data binding.

Table.cs

public class ProductTable
{
    [JsonProperty(PropertyName = "id")]
    public string id { get; set; }

    [JsonProperty(PropertyName = "Name")]
    public string Name { get; set; }

    [JsonProperty(PropertyName = "Title")]
    public string Title { get; set; }

    [JsonProperty(PropertyName = "Description")]
    public string Description { get; set; }

    [JsonProperty(PropertyName = "Price")]
    public double Price { get; set; }

    [JsonProperty(PropertyName = "Type")]
    public string Type { get; set; }

    [JsonProperty(PropertyName = "Seller")]
    public string Seller { get; set; }

    [JsonProperty(PropertyName = "Expiration")]
    public int Expiration { get; set; }
}

XAML.cs You don't need to declare a new listbox, only use itemsource.

private async void button1_Click(object sender, RoutedEventArgs e)
{
    IMobileServiceTable<ProductTable> productTest = App.MobileService.GetTable<ProductTable>();

    // This query filters out completed TodoItems.
    MobileServiceCollection<ProductTable, ProductTable> products = await productTest
        .Where(ProductTable => ProductTable.Type == "Test")
        .ToCollectionAsync();

    lb.ItemsSource = products;

}

XAML

IN THE STACKPANEL, NEVER EVER USE HEIGHT"*" this will cause a critical error!

<ListBox x:Name="lb" Margin="10,10,10,100" >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Width="300">
                <TextBlock Text="{Binding Name}"  FontSize="10"></TextBlock>
                <TextBlock Text="{Binding Title}" FontSize="10"></TextBlock>
                <TextBlock Text="{Binding Description}" FontSize="10"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

You are creating two ListBoxes - one in code that isn't being displayed and one in XAML which is bound to whatever your DataContext is.

Remove the last two lines in your code, and change your XAML to:

ItemsSource="{Binding Products}"

Next, expose Products as a Property on a class (ViewModel if you want to use MVVM), and ensure that the DataContext of your ListBox is set to said ViewModel/class.

I'm not familiar with MobileServiceCollection, so I assume it is bindable. If it is not, expose Products as a ObservableCollection (if the values in it change over time), or any other supported collection type.

Elaboration on Properties

Create a class:

public class MainViewModel {
    //This is a property
    public ObservableCollection<ProductTable> Products { get; set; }

    //In this method, load your data into Products
    public void Load(){ 
        //Products = xxx
    }
}

In your XAML:

<Page [...]
    xmlns:local="clr-namespace:YourNamespace;assembly=YourProjectName">
    <Grid>
        <Grid.DataContext>
            <local:MainViewModel />
        </Grid.DataContext>
        <!-- ListBox goes in here somewhere, still with ItemsSource bound to Products -->
    </Grid>
</Page>

Read more about namespaces here .

By doing this, your Windows' DataContext will be set to the MainViewModel (could also be named ProductsViewModel). Since your ListBox will be within your Window, it will inherit (due to Property Value Inheritance ) the same DataContext.

The Binding will look for a property 'Products' on the ViewModel.

You will need to call the Load() method somewhere.

Part 2 - After looking at the code

Mind you, I am not able to run the code, so I'm flying somewhat blind.

Buy.xaml.cs

public sealed partial class Buy : Page
{
    private readonly MainViewModel _viewModel;

    public Buy()
    {
        this.InitializeComponent();

        _viewModel = new MainViewModel();
        DataContext = _viewModel;
    }

    private async void button1_Click(object sender, RoutedEventArgs e)
    {
        _viewModel.Load();
    }
}

MainViewModel.cs

public class MainViewModel : INotifyPropertyChanged
{
    IMobileServiceTable<ProductTable> product = App.MobileService.GetTable<ProductTable>();

    public List<ProductTable> Products { get; private set; }

    public event PropertyChangedEventHandler PropertyChanged;

    public async void Load()
    {
        Products = await product
            .Where(ProductTable => ProductTable.Price == 15)
            .ToListAsync();

        //Notify that the property has changed to alert to UI to update.
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products)));
    }
}

You need to make ProductTable public, or you will get a compilation error.

Hopefully, the above will enable you to bind your ListBox using the ItemsSource binding described above.

And please note that the above code is not necessarily best practice.

you don't need that line in your code behind

ListBox lb = new ListBox();

and you can keep that line

lb.ItemsSource = Products;

or you can sit the Binding to the MobileServiceCollection in the XAML

<ListBox Margin="10,10,10,100" x:Name="lb" ItemsSource="{Binding Products}">
       <ListBox.ItemTemplate>
         <DataTemplate>
           <TextBlock Text="{Binding Name}"/>
           <TextBlock Text="{Binding Title}"/>
           <TextBlock Text="{Binding Description}"/>
         </DataTemplate>
     </ListBox.ItemTemplate>
</ListBox>

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