Помогите реализовать обновление данных в WPF

258
09 мая 2017, 02:33

Господа, помогите реализовать грамотное обновление данных.

Работа приложения:

  • Событие скачивает периодически JSON файл.
  • Подписанным методом на событие читаем с помощью Load(); этот файл и заносим все данные в некую модель.
  • Делаем привязку данных и выводим.

На данный момент имею такой "костыль" (удаляем старую привязку к данным и привязываем по новой):

 if (alertbox.ItemsSource != null) alertbox.ItemsSource = null;
 alertbox.ItemsSource = News.Data.Posts;

Нужно от этого собственно избавится. Пытался реализовать INotifyPropertyChanged, но данные, что бы я не делал, остаются старые...

Собственно сам код:

Загрузка данных из JSON файла (основной метод Read и для удобства статичный Load):

internal class Game
{
    private static GameView Read(string fileName)
    {
        GameView data;
        using (var file = File.OpenText(fileName))
        {
            var serializer = new JsonSerializer();
            data = (GameView) serializer.Deserialize(file, typeof(GameView));
        }
        return data;
    }
    /// <summary>
    ///     Основные игровые данные.
    /// </summary>
    public static GameView Data;
    /// <summary>
    ///     Загружаем JSON файл с игровыми данными.
    /// </summary>
    /// <param name="filename">Путь до JSON файла</param>
    public static void Load(string filename = "temp")
    {
        if (filename == "temp") filename = $"{Settings.Program.Directories.Temp}/GameData.json";
        Data = Read(filename);
    }
}

Модель GameView:

public class GameView
{
    public int Version { get; set; }
    public string MobileVersion { get; set; }
    public string BuildLabel { get; set; }
    public int Time { get; set; }
    public int Date { get; set; }
    public List<Alert> Alerts { get; set; }
    public List<double> ProjectPct { get; set; }
    public string WorldSeed { get; set; }
}
public class Alert
{
    [JsonProperty("_id")]
    public Id Id { get; set; }
    public Activation Activation { get; set; }
    public Expiry Expiry { get; set; }
    public MissionInfo MissionInfo { get; set; }
}
public class MissionInfo
{
    public string MissionType { get; set; }
    public string Faction { get; set; }
    public string Location { get; set; }
    public string LevelOverride { get; set; }
    public string EnemySpec { get; set; }
    public int MinEnemyLevel { get; set; }
    public int MaxEnemyLevel { get; set; }
    public double Difficulty { get; set; }
    public int Seed { get; set; }
    public int MaxWaveNum { get; set; }
    public MissionReward MissionReward { get; set; }
    public string ExtraEnemySpec { get; set; }
    public List<string> CustomAdvancedSpawners { get; set; }
    public bool? ArchwingRequired { get; set; }
    public bool? IsSharkwingMission { get; set; }
}

Как вызываю:

Game.Load();
alertbox.ItemsSource = Game.Data.Alerts;

Ну и самый простой на данный момент ListBox:

<ListBox x:Name="alertbox" Background="{x:Null}" BorderBrush="{x:Null}">
                    <ListBox.ItemTemplate>
                        <DataTemplate >
                            <Label Content="{Binding MissionInfo.Location}" />
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

Помогите в этом случае реализовать поддержку обновления данных в интерфейсе, как только загрузится JSON файл. Весь проект залит на GitHub

Answer 1

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

Затем, привязка к статическим свойствам неудобна, да и игра — сущность самостоятельная, так что вам имеет смысл сделать методы и свойства класса Game нестатическими. Кроме того, вы должны бы реализовать INotifyPropertyChanged, не вижу этого в вашем коде.

Берём базовый класс VM отсюда, пишем:

internal class Game : VM
{
    private static GameView Read(string fileName)
    {
        GameView data;
        using (var file = File.OpenText(fileName))
        {
            var serializer = new JsonSerializer();
            data = (GameView) serializer.Deserialize(file, typeof(GameView));
        }
        return data;
    }
    GameView data;
    public GameView Data { get => data; set => Set(ref data, value); }
    public void Load(string filename = "temp")
    {
        if (filename == "temp")
            filename = $"{Settings.Program.Directories.Temp}/GameData.json";
        Data = Read(filename);
    }
}

Далее, внутренние классы у вас иммутабельные? Если нет, им тоже нужно реализовать INotifyPropertyChanged. А списку нужно реализовывать INotifyCollectionChanged, то есть по идее вам нужна просто ObservableCollection<T>:

public class GameView : VM
{
    int version;
    public int Version
    {
        get => version;
        set => Set(ref version, value);
    }
    // ...
    ObservableCollection<Alert> alerts;
    public ObservableCollection<Alert> Alerts
    {
        get => alerts;
        set => Set(ref alerts, value);
    }
    // ...
}

и то же самое для остальных классов, к которым вы планируете привязку.

Имея это, можно организовывать привязку в XAML.

<ListBox Background="{x:Null}" BorderBrush="{x:Null}" ItemsSource="{Binding Data.Alerts}">
    <ListBox.ItemTemplate>
        <DataTemplate >
            <Label Content="{Binding MissionInfo.Location}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
READ ALSO
Запуск файла с атрибутом ReadOnly

Запуск файла с атрибутом ReadOnly

Открываю из программы картинку в стандартном "Просмотр фотографий Windows" таким образом:

213
MVVM привязать двумерный массив строк к datagrid

MVVM привязать двумерный массив строк к datagrid

Необходимо заполнить dataGrid двумерным массивом строки все бы было просто если бы не паттерн

436
Не генерируются pdb-символы

Не генерируются pdb-символы

Может я что-то не так понимаю, но суть в чемЯ пишу библиотеку, ее нельзя скомпилить напрямую как мне подсказала студия, поэтому я создал проект...

224
Почему блок if выполняется?

Почему блок if выполняется?

Есть условие if:

189