Десериализация объекта JSON в ObservableCollection ‹T› и привязка его к GridView или ListView в MVVM (шаблон 10) в App Lauch

Я новичок в MVVM для UWP, я немного запутался, я хочу привязать десериализованный объект JSON к GridView или ListView, но я столкнулся с этим сценарием.

Когда я запускаю приложение, выполняется десериализация, но GridView пуст. Чтобы увидеть десериализованные данные, мне нужно перейти на другую страницу, а затем вернуться, чтобы увидеть десериализованные данные. это также относится к привязке к ListView.

Ключевые моменты, на которые следует обратить внимание.

И в представлении сетки, и в представлении списка я использую x: Bind для привязки к нему ViewModel.ObservableCollectionList.

Я вызываю метод десериализации в конструкторе MainPage.cs

Может кто-нибудь мне помочь, пожалуйста.

П.О. Алиу

Я прикрепил ViewModel для главной страницы, mainpage.xaml и mainpage.xaml.cs

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Template10.Common;
using Template10.Mvvm;
using Template10.Services.NavigationService;
using Tymly_Revamped.Models;
using Tymly_Revamped.Views;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Tymly_Revamped.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        public ObservableCollection<Tymly> TymlysList => new ObservableCollection<Tymly>();
        public static MainPageViewModel _mainPageViewModel;
        public Tymly SelectedTymly { get; set; }

    //Storage Folders for Data
    private StorageFolder _localFolder = null;
    private StorageFolder localCacheFolder = null;
    private StorageFolder roamingFolder = null;
    private StorageFolder tempFolder = null;

    private string _filename = "TymlyFiles";
    private ObservableCollection<Tymly> _deserializedModel = new ObservableCollection<Tymly>();

    public MainPageViewModel()
    {
        _localFolder = ApplicationData.Current.LocalFolder;
        localCacheFolder = ApplicationData.Current.LocalCacheFolder;
        roamingFolder = ApplicationData.Current.RoamingFolder;
        tempFolder = ApplicationData.Current.TemporaryFolder;
        _mainPageViewModel = this;

        // TymlysList = TymlyManager.GetTymlys();
        SelectedTymly = new Tymly();
        // ReadFromJson();
    }

    public async void tymlysGrid_ItemClick(object sender, ItemClickEventArgs e)
    {
        var tymly = (Tymly)e.ClickedItem;
        SelectedTymly = tymly;
        SessionState.Add(nameof(SelectedTymly), SelectedTymly);
        await WindowWrapper.Current().NavigationServices.FirstOrDefault().NavigateAsync(typeof(SelectedTymly), nameof(SelectedTymly));
    }

    public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
    {
        ReadFromJson();
        SessionState.Remove(nameof(SelectedTymly));
        var newTymly = parameter as string;
        if (newTymly != null && SessionState.ContainsKey(newTymly))
        {
            var tym = SessionState.Get<Tymly>(newTymly);

            if (tym != null && !TymlysList.Contains(tym) && !tym.Location.Equals(string.Empty))
            {
                TymlysList.Add(tym);
                WriteToJson();
            }

        }
        await Task.CompletedTask;
    }

    public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
    {
        if (suspending)
        {
            //suspensionState[nameof(Value)] = Value;
            suspensionState[nameof(SelectedTymly)] = SelectedTymly;
        }
        await Task.CompletedTask;
    }

    public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
    {
        args.Cancel = false;
        await Task.CompletedTask;
    }

    public void GotoSelectedTymlyPage() =>
        NavigationService.Navigate(typeof(SelectedTymly), SelectedTymly);

    public void GotoSettings() =>
        NavigationService.Navigate(typeof(SettingsPage), 0);

    public void GotoPrivacy() =>
        NavigationService.Navigate(typeof(SettingsPage), 1);

    public void GotoAbout() =>
        NavigationService.Navigate(typeof(SettingsPage), 2);



    //Data Persistence
    public async Task<string> SerializeTymlyToJson<T>(ObservableCollection<T> observableCollection, string fileName)
    {
        try
        {
            var Folder = Windows.Storage.ApplicationData.Current.LocalFolder;

            var file = await Folder.CreateFileAsync(fileName + ".json", CreationCollisionOption.ReplaceExisting);
            var data = await file.OpenStreamForWriteAsync();

            using (StreamWriter streamWriter = new StreamWriter(data))
            {
                var serializedFile = JsonConvert.SerializeObject(observableCollection, Formatting.Indented);
                streamWriter.Write(serializedFile);
            }
            return fileName;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            Debug.WriteLine("It Did not work");
            throw e;

        }
    }

    public async Task<ObservableCollection<T>> DeserializeJsonToTymly<T>(string fileName)
    {
        try
        {
            ObservableCollection<T> contacts = new ObservableCollection<T>();
            var Folder = Windows.Storage.ApplicationData.Current.LocalFolder;
            var file = await Folder.GetFileAsync(fileName + ".json");
            var data = await file.OpenReadAsync();

            using (StreamReader streamReader = new StreamReader(data.AsStream()))
            {
                string text = streamReader.ReadToEnd();
                ObservableCollection<T> contact = JsonConvert.DeserializeObject<ObservableCollection<T>>(text);
                foreach (var con in contact)
                {
                    contacts.Add(con);
                }
            }
            return contacts;
        }
        catch (Exception e)
        {

            throw e;
        }
    }

    public async void WriteToJson()
    {
        try
        {
            _filename = await SerializeTymlyToJson<Tymly>(TymlysList, "TymlyFiles");
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            Debug.WriteLine("No WAY");
            throw e;
        }
    }
    public async void ReadFromJson()
    {
        try
        {
            _deserializedModel = await DeserializeJsonToTymly<Tymly>(_filename);
            //TymlysList.Clear();
            foreach (var tymly in _deserializedModel)
            {
                Debug.WriteLine("Done One");
                TymlysList.Add(tymly);
            }
            Debug.WriteLine("Read Json");

        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            Debug.WriteLine("ReadFromJSon");
        }
    }   
}

}

<Page x:Class="Tymly_Revamped.Views.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:Behaviors="using:Template10.Behaviors"
  xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
  xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
  xmlns:controls="using:Template10.Controls"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="using:Tymly_Revamped.Views"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:tm="using:Tymly_Revamped.Models"
  xmlns:vm="using:Tymly_Revamped.ViewModels" mc:Ignorable="d">

<Page.DataContext>
    <vm:MainPageViewModel x:Name="ViewModel" />
</Page.DataContext>
<!--<Page.Resources>
    <CollectionViewSource x:Name="TymlyListViewSource"
                          Source="{x:Bind ViewModel.TymlysList}"
                          IsSourceGrouped="False"/>
</Page.Resources>-->

<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="AdaptiveVisualStateGroup" CurrentStateChanged="AdaptiveVisualStateGroup_OnCurrentStateChanged">
            <VisualState x:Name="VisualStateNarrow">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="tymlysGrid.Visibility" Value="Collapsed"/>
                    <Setter Target="tymlysListView.Visibility" Value="Visible"/>
                </VisualState.Setters>
            </VisualState>
            <VisualState x:Name="VisualStateNormal">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="tymlysGrid.Visibility" Value="Visible"/>
                    <Setter Target="tymlysListView.Visibility" Value="Collapsed"/>
                </VisualState.Setters>
            </VisualState>
            <VisualState x:Name="VisualStateWide">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="tymlysGrid.Visibility" Value="Visible"/>
                    <Setter Target="tymlysListView.Visibility" Value="Collapsed"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
                         RelativePanel.AlignRightWithPanel="True" Frame="{x:Bind Frame}"
                         RelativePanel.AlignTopWithPanel="True" Text="TYMLYS" Foreground="Black" FontFamily="mononoki" FontWeight="Bold" Content="TYMLYS" BorderBrush="Black" BorderThickness="3">
        <controls:PageHeader.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="Black" Offset="0"/>
                <GradientStop Color="#FF25E67D" Offset="1"/>
            </LinearGradientBrush>
        </controls:PageHeader.Background>

        <!--  secondary commands  -->
        <controls:PageHeader.SecondaryCommands>
            <AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
            <AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
            <AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
        </controls:PageHeader.SecondaryCommands>

    </controls:PageHeader>

    <GridView x:Name="tymlysGrid"
              RelativePanel.Below="pageHeader"
              RelativePanel.AlignLeftWithPanel="True"
              RelativePanel.AlignRightWithPanel="True"
              RelativePanel.AlignBottomWithPanel="True"
              IsItemClickEnabled="True"
              ItemsSource="{x:Bind ViewModel.TymlysList, Mode=OneWay}"
              ItemClick="{x:Bind ViewModel.tymlysGrid_ItemClick}" ScrollViewer.VerticalScrollBarVisibility="Hidden">
        <GridView.ItemContainerStyle>
            <Style TargetType="GridViewItem">
                <Setter Property="MinHeight" Value="150"></Setter>
                <Setter Property="MaxHeight" Value="300"></Setter>
                <Setter Property="MinWidth" Value="320"></Setter>
                <Setter Property="MaxWidth" Value="480"></Setter>
            </Style>
        </GridView.ItemContainerStyle>
        <GridView.ItemTemplate>
            <DataTemplate x:Name="TymlyTemplate" x:DataType="tm:Tymly">
                <Grid x:Name="Main" Style="{ThemeResource GridViewInnerGrid}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="2*"/>
                        <ColumnDefinition Width="1.5*"/>
                    </Grid.ColumnDefinitions>

                    <Grid Grid.Column="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="4*"/>
                            <RowDefinition Height="2*"/>
                            <RowDefinition Height="2*"/>
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Column="0" Grid.Row="0"
                Style="{ThemeResource MediumText}"
                    Text="{x:Bind Location}"  HorizontalAlignment="Left" VerticalAlignment="Center" ></TextBlock>
                        <TextBlock Grid.Column="0" Grid.Row="1"
                Style="{ThemeResource SmallText}"
                    Text="{x:Bind Date}"  HorizontalAlignment="Left" VerticalAlignment="Center"></TextBlock>
                        <TextBlock  Grid.Column="0" Grid.Row="2" Style="{ThemeResource SmallText}" Text="{x:Bind Time}"  HorizontalAlignment="Left" VerticalAlignment="Center"></TextBlock>
                    </Grid>
                    <Viewbox Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" Height="100">
                        <Canvas Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center">
                            <Ellipse Fill="Green"  Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" />
                            <Path
                        Data="{x:Bind MapIcon}"
                        Fill="Black" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5"
                        >
                                <Path.RenderTransform>
                                    <CompositeTransform TranslateX="70" TranslateY="70" ScaleX="2" ScaleY="2"/>
                                </Path.RenderTransform>
                            </Path>
                        </Canvas>

                    </Viewbox>
                </Grid>
            </DataTemplate>
        </GridView.ItemTemplate>
    </GridView>

    <ListView x:Name="tymlysListView"
              Style="{ThemeResource NarrowListView}"
              RelativePanel.Below="pageHeader"
              IsItemClickEnabled="True"
              ItemsSource="{x:Bind ViewModel.TymlysList, Mode=OneWay}"
              ItemClick="{x:Bind ViewModel.tymlysGrid_ItemClick}" ScrollViewer.VerticalScrollBarVisibility="Hidden" Visibility="Collapsed">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="MinHeight" Value="120"></Setter>
                <Setter Property="MaxHeight" Value="250"></Setter>
                <Setter Property="MinWidth" Value="320"></Setter>
                <Setter Property="MaxWidth" Value="640"></Setter>
                <Setter Property="Padding" Value="10"></Setter>
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
            <DataTemplate x:Name="TymlyTemplate1" x:DataType="tm:Tymly">
                <Grid x:Name="Main" Style="{ThemeResource ListViewInnerGrid}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="2*"/>
                        <ColumnDefinition Width="1.5*"/>
                    </Grid.ColumnDefinitions>

                    <Grid Grid.Column="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="3*"/>
                            <RowDefinition Height="2*"/>
                            <RowDefinition Height="2*"/>
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Column="0" Grid.Row="0"
                Style="{ThemeResource MediumText}"
                    Text="{x:Bind Location}"  HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0" ></TextBlock>
                        <TextBlock Grid.Column="0" Grid.Row="1" Margin="5,0"
                Style="{ThemeResource SmallText}"
                    Text="{x:Bind Date}"  HorizontalAlignment="Left" VerticalAlignment="Center"></TextBlock>
                        <TextBlock  Grid.Column="0" Grid.Row="2" Style="{ThemeResource SmallText}" Text="{x:Bind Time}"  HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0"></TextBlock>
                    </Grid>
                    <Viewbox Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" Height="100">
                        <Canvas Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5">
                            <Ellipse Fill="Green"  Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" />
                            <Path
                        Data="{x:Bind MapIcon}"
                        Fill="Black" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5"
                        >
                                <Path.RenderTransform>
                                    <CompositeTransform TranslateX="70" TranslateY="70" ScaleX="2" ScaleY="2"/>
                                </Path.RenderTransform>
                            </Path>
                        </Canvas>

                    </Viewbox>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</RelativePanel>

using Windows.UI.Xaml;
using Windows.UI.Xaml.Navigation;


namespace Tymly_Revamped.Views
{
    public sealed partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();
            NavigationCacheMode = NavigationCacheMode.Enabled;

        }

        private void AdaptiveVisualStateGroup_OnCurrentStateChanged(object sender, VisualStateChangedEventArgs e)
        {
            if (e.NewState == VisualStateNarrow)
            {
                tymlysGrid.Visibility = Visibility.Collapsed;
                tymlysListView.Visibility = Visibility.Visible;
            }
            else if (e.NewState == VisualStateNormal || e.NewState == VisualStateWide)
            {
                tymlysGrid.Visibility = Visibility.Visible;
                tymlysListView.Visibility = Visibility.Collapsed;
            }
        }
    }
}

person Onotseike    schedule 07.08.2016    source источник
comment
Свойство viewmodel не уведомляет пользовательский интерфейс о том, что в модели просмотра были внесены изменения.   -  person Nkosi    schedule 08.08.2016
comment
Я знаком с WPF, а не с UWP, но меня удивляет, что он когда-либо работает. поскольку привязки wpf работают только со свойствами, а TymlysList - это поле.   -  person    schedule 08.08.2016
comment
@Nkosi, пожалуйста, как вы предлагаете сделать это во ViewModel?   -  person Onotseike    schedule 08.08.2016
comment
@ Попробую ли я ваше предложение, но оно все еще не работает ....   -  person Onotseike    schedule 08.08.2016


Ответы (1)


Модель представления должна уведомлять представление об изменении свойств, в противном случае представление не будет знать, что нужно обновлять, чтобы отразить изменения.

public class MainPageViewModel : ViewModelBase {
    private ObservableCollection<Tymly> tymlysList = new ObservableCollection<Tymly>();
    private Tymly selectedTymly = null

    public ObservableCollection<Tymly> TymlysList {
        get { return tymlysList; }
        set { Set(ref tymlysList, value); }
    } 

    public Tymly SelectedTymly {
        get { return selectedTymly; }
        set { Set(ref selectedTymly, value); }
    }

    //Other code removed for brevity
}
person Nkosi    schedule 08.08.2016
comment
Отличный ответ @Nkosi, обычная ошибка MVVM. - person Jerry Nixon; 19.08.2016