Не удалось загрузить источник элементов списка со списком после десериализации в WPF - PullRequest
2 голосов
/ 06 февраля 2020

Я столкнулся с проблемой, когда я не могу перезагрузить сериализованный объект (используя Newtonsoft Json) в каскадные выпадающие списки. Я также использую Prism MVVM lib.

. При первом запуске мое приложение работает как положено:

enter image description here

, поэтому я Я могу выбрать значения из второго комбинированного списка на основе первого комбинированного списка, но когда я сохраняю модель и перезагружаю ее, у меня возникают две основные проблемы:

  • Свойство SelectedItem никогда не устанавливается (даже отладчик) показывает, что он не равен нулю)
  • Второй комбинированный список остается пустым, даже если значения загружены, и выглядит так:

enter image description here

Что я здесь не так делаю? Кроме того, мне не нравится подход ComboboxSelectionChanged, поэтому, возможно, кто-то может подсказать мне подход, основанный на MVVM.

Вот минимальный рабочий пример:

MainWindow.xaml.cs

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private ViewModel viewModel;

    public MainWindow()
    {
        InitializeComponent();

        viewModel = new ViewModel();

        ConstructRandomData();

        DataContext = viewModel;
    }

    private void ConstructRandomData()
    {
        // Construct data
        for (int i = 0; i < 5; i++)
        {
            var ids = new List<SubId>();

            for (int r = 0; r < 10; r++)
            {
                ids.Add(
                    new SubId
                    {
                        Name = $"Id_{i}.{r}"
                    }
                );
            }

            viewModel.MainIds.Add(
                new MainId
                {
                    Name = $"MainId{i}",
                    SubIds = ids
                });
        }
    }

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ComboBox combo = sender as ComboBox;

        if (combo.SelectedItem is MainId selectedItem)
        {
            var subIdList = (from mainId in viewModel.MainIds
                                  where mainId.Name.Equals(selectedItem.Name)
                                  select mainId.SubIds).First();

            viewModel.SubIds.Clear();

            viewModel.SubIds.AddRange(subIdList.ToArray());

        }

    }

    private void SaveButtton_Click(object sender, RoutedEventArgs e)
    {
        File.WriteAllText("savedData.json", JsonConvert.SerializeObject(viewModel));
    }

    private void LoadButton_Click(object sender, RoutedEventArgs e)
    {
        ViewModel deserializedModel = JsonConvert.DeserializeObject<ViewModel>(File.ReadAllText("savedData.json"));

        viewModel.MainIds = deserializedModel.MainIds;
        viewModel.SubIds = deserializedModel.SubIds;
    }
}

public class ViewModel : BindableBase
{
    public ObservableCollection<MainId> MainIds { get; set; } = new ObservableCollection<MainId>();
    public ObservableCollection<SubId> SubIds { get; set; } = new ObservableCollection<SubId>();

    private MainId mainId;
    public MainId SelectedMainId
    {
        get { return mainId; }
        set { SetProperty(ref mainId, value); }
    }

    private SubId selectedId;
    public SubId SelectedId
    {
        get { return selectedId; }
        set { SetProperty(ref selectedId, value); }
    }
}
public class MainId : BindableBase
{
    private string name;
    public string Name
    {
        get { return name; }
        set 
        { 
            SetProperty(ref name, value); 
        }
    }

    public List<SubId> SubIds { get; set; } = new List<SubId>();
}
public class SubId : BindableBase  
{
    private string name;
    public string Name
    {
        get { return name; }
        set { SetProperty(ref name, value); }
    }
}

MainWindow.xaml

<Window x:Class="CascadingComboBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CascadingComboBox"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel Margin="30">
        <ComboBox Margin="5" Width="150" 
                  ItemsSource="{Binding MainIds}" 
                  DisplayMemberPath="Name" 
                  SelectedItem="{Binding SelectedMainId}"
                  SelectionChanged="ComboBox_SelectionChanged"/>
        <ComboBox Margin="5" Width="150" 
                  ItemsSource="{Binding SubIds}" 
                  SelectedItem="{Binding SelectedId}"
                  DisplayMemberPath="Name"/>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
            <Button Margin="5" Width="50" Content="Save" Click="SaveButtton_Click" />
            <Button Margin="5" Width="50" Content="Load" Click="LoadButton_Click"/>
        </StackPanel>
    </StackPanel>
</Window>

1 Ответ

0 голосов
/ 08 февраля 2020

Во время сериализации SelectedItem содержит объект из коллекции ComboBoxItems.

Но после десериализации это уже не так: теперь SelectedItem - это новый экземпляр, хотя он имеет то же содержимое, что и один из элементов в ComboBoxItems. Вот как Json. NET работает по умолчанию.

Это можно исправить, изменив параметр PreserveReferencesHandling следующим образом:

var jsonSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };

JsonConvert.SerializeObject(model, Formatting.Indented, jsonSettings);

...

model = JsonConvert.DeserializeObject<List<Person>>(json, jsonSettings);

https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_PreserveReferencesHandling.htm

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...