将Observable Collection绑定到GridView

时间:2016-07-30 03:57:37

标签: c# uwp observablecollection uwp-xaml

我的UWP需要有一个“收藏夹”页面,允许用户重新排序并保存页面上的数据。最初我的数据来自一个大型JSON文件,该文件使用Newtonsoft的Json.net进行反序列化,并在此字典中存储在一个字典中,然后填充公共的ObservableCollection。

这就是我现在迷失的地方,将ObservableCollection设置为DataContext,然后在XAML代码中使用数据作为Binding,使用每个Item所需的所有标题,字幕和图像填充GridView。 理论上这应该可行,但在我的试验和测试中,页面仍然是空白的,而幕后的所有C#代码似乎都应该填充它。

我不知道为什么页面不适合我转向所有人的集体帮助。

P.S:我并不十分关心这段代码的整洁,我只是想让它运转起来。

XAML文件

<Page
x:Name="pageRoot"
x:Class="Melbourne_Getaway.FavouritesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Melbourne_Getaway"
xmlns:data="using:Melbourne_Getaway.Data"
xmlns:common="using:Melbourne_Getaway.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Page.Resources>
    <x:String x:Key="AppName">Favourites</x:String>
</Page.Resources>

<!--
    This grid acts as a root panel for the page that defines two rows:
    * Row 0 contains the back button and page title
    * Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.ChildrenTransitions>
        <TransitionCollection>
            <EntranceThemeTransition />
        </TransitionCollection>
    </Grid.ChildrenTransitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="140" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <GridView
        x:Name="itemGridView"
        AutomationProperties.AutomationId="ItemsGridView"
        AutomationProperties.Name="Items"
        TabIndex="1"
        Grid.RowSpan="2"
        Padding="60,136,116,46"
        SelectionMode="None"
        IsSwipeEnabled="false"
        CanReorderItems="True"
        CanDragItems="True"
        AllowDrop="True"
        ItemsSource="{Binding Items}">
        <GridView.ItemTemplate>
            <DataTemplate>
                <Grid HorizontalAlignment="Left" Width="250" Height="107">
                    <Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
                        <Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" />
                    </Border>
                    <StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
                        <TextBlock Text="{Binding Title}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" Height="30" Margin="15,0,15,0" FontWeight="SemiBold" />
                        <TextBlock Text="{Binding Group}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" TextWrapping="NoWrap" Margin="15,-15,15,10" FontSize="12" />
                    </StackPanel>
                </Grid>
            </DataTemplate>
        </GridView.ItemTemplate>
    </GridView>

    <!-- Back button and page title -->
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="120" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                    Style="{StaticResource NavigationBackButtonNormalStyle}"
                    VerticalAlignment="Top"
                    AutomationProperties.Name="Back"
                    AutomationProperties.AutomationId="BackButton"
                    AutomationProperties.ItemType="Navigation Button" />
        <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
                    IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" />
    </Grid>
</Grid>

CS档案

using Melbourne_Getaway.Common;
using Melbourne_Getaway.Data;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Windows.Storage;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Melbourne_Getaway
{
    public sealed partial class FavouritesPage : Page
    {
        public ObservableCollection<ItemData> Items { get; set; }

        private ObservableDictionary defaultViewModel = new ObservableDictionary();
        private NavigationHelper navigationHelper;
        private RootObject jsonLines;
        private StorageFile fileFavourites;
        private Dictionary<string, ItemData> ItemData = new Dictionary<string, ItemData>();

        public FavouritesPage()
        {
            loadJson();
            getFavFile();

            this.InitializeComponent();
            this.navigationHelper = new NavigationHelper(this);
            this.navigationHelper.LoadState += navigationHelper_LoadState;
        }

        private void setupObservableCollection()
        {
            Items = new ObservableCollection<ItemData>(ItemData.Values);
            DataContext = Items;
        }

        private async void loadJson()
        {
            var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///DataModel/SampleData.json"));
            string lines = await FileIO.ReadTextAsync(file);
            jsonLines = JsonConvert.DeserializeObject<RootObject>(lines);
            feedItems();
        }

        private async void getFavFile()
        {
            Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
            fileFavourites = await storageFolder.GetFileAsync("MelbGetaway.fav");
        }

        private async void feedItems()
        {
            if (await FileIO.ReadTextAsync(fileFavourites) != "")
            {
                foreach (var line in await FileIO.ReadLinesAsync(fileFavourites))
                {
                    foreach (var Group in jsonLines.Groups)
                    {
                        foreach (var Item in Group.Items)
                        {
                            if (Item.UniqueId == line)
                            {
                                var storage = new ItemData()
                                {
                                    Title = Item.Title,
                                    UniqueID = Item.UniqueId,
                                    ImagePath = Item.ImagePath,
                                    Group = Group.Title
                                };
                                ItemData.Add(storage.UniqueID, storage);
                            }
                        }
                    }
                }
            }
            else
            {//should only execute if favourites file is empty, first time use?
                foreach (var Group in jsonLines.Groups)
                {
                    foreach (var Item in Group.Items)
                    {
                        var storage = new ItemData()
                        {
                            Title = Item.Title,
                            UniqueID = Item.UniqueId,
                            ImagePath = Item.ImagePath,
                            Group = Group.Title
                        };
                        ItemData.Add(storage.UniqueID, storage);
                        await FileIO.AppendTextAsync(fileFavourites, Item.UniqueId + "\r\n");
                    }
                }
            }
            setupObservableCollection();
        }

        public ObservableDictionary DefaultViewModel
        {
            get { return this.defaultViewModel; }
        }

        #region NavigationHelper loader

        public NavigationHelper NavigationHelper
        {
            get { return this.navigationHelper; }
        }

        private async void MessageBox(string Message)
        {
            MessageDialog dialog = new MessageDialog(Message);
            await dialog.ShowAsync();
        }

        private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
            this.defaultViewModel["Groups"] = sampleDataGroups;
        }

        #endregion NavigationHelper loader

        #region NavigationHelper registration

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            navigationHelper.OnNavigatedFrom(e);
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            navigationHelper.OnNavigatedTo(e);
        }

        #endregion NavigationHelper registration
    }

    public class ItemData
    {
        public string UniqueID { get; set; }
        public string Title { get; set; }
        public string Group { get; set; }
        public string ImagePath { get; set; }
    }
}

2 个答案:

答案 0 :(得分:0)

如果没有好的Minimal, Complete, and Verifiable code example,就不可能确定是什么问题。但是,您的代码中会出现一个明显的错误:

private void setupObservableCollection()
{
    Items = new ObservableCollection<ItemData>(ItemData.Values);
    DataContext = Items;
}

在您的XAML中,绑定到{Binding Items}。将DataContext设置为Items属性值后,正确的绑定实际上只是{Binding}

或者,如果您想保持XAML的方式,则必须设置DataContext = this;。当然,如果你这样做,那么你会遇到一个问题,你似乎没有提出INotifyPropertyChanged.PropertyChanged,甚至实现那个接口。如果您确定在调用InitializeComponent()方法之前设置了属性,那么您可以使用它,但在您显示的代码中似乎并非如此。

因此,如果您要将绑定设置为{Binding Items},则还需要实施INotifyPropertyChanged并确保在您使用属性名称PropertyChanged引发"Items"事件时实际上设置了属性。

如果上述问题无法解决您的问题,请通过提供可靠再现问题的良好MCVE来改进问题。

答案 1 :(得分:0)

我明白了。我的问题在于我试图将数据传递给页面本身的方式。而不是使用DataContext = Items;并试图以这种方式访问​​数据。我改为为ItemsSource设置直接GridView

最终结果只是将DataContext = Items更改为itemGridView.ItemsSource = Items;