从序列化ViewModel还原时,Combox SelectedItem不适用 [英] Combox SelectedItem does not apply when restoring from serialized ViewModel

查看:131
本文介绍了从序列化ViewModel还原时,Combox SelectedItem不适用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在还原ViewModel(使用Json.Net序列化)时使用C#WPF和MVVM模式时,我遇到一个奇怪的问题.

I'm facing a strange problem when using C# WPF and MVVM Pattern while restoring a ViewModel (serialized using Json.Net).

该软件的想法是-当关闭窗口时-将当前Viewmodel状态保存在json文件中.

The idea of the software is - when closing the window - to persist the current Viewmodel state in a json file.

在下次启动时,该应用程序仅为json服务.

At the next startup the app just serarches for the json.

  • 如果有文件,则将其反序列化并还原ViewModel(设置公共属性).
  • 如果没有文件,则创建视图模型并设置默认值.

现在我的问题是,当使用json文件还原一个包含自定义类型列表的组合框时,该组合框具有值,但没有SelectedItem.创建视图模型实例并使用默认值初始化公共属性时(通过后面的代码执行此操作),一切都很好.

Now my problem is, that when restoring it with the json file, a combobox containing a list of a custom type, the combobox has values but no SelectedItem. When creating the viewmodel instance and initiailizing the public properties with default values (doing this via the code behind) then everything is fine.

以下是代表错误"的一些代码: 查看

Here is some code that represents the "error": View

<Window x:Class="CrazyWpf.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:CrazyWpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        Closing="Window_Closing"
        Loaded="Window_Loaded">
    <StackPanel
        x:Name="rootElement"
        Orientation="Vertical"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        Margin="10">
        <StackPanel.DataContext>
            <local:DemoViewModel />
        </StackPanel.DataContext>
        <StackPanel
            Orientation="Horizontal">
            <Label
                x:Name="lblID"
                Width="30"
                Content="ID:"/>
            <TextBox
                x:Name="tbID"
                Width="50"
                Margin="30,0,0,0"
                Text="{Binding ID, UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
        <StackPanel
            Orientation="Horizontal">
            <Label
                x:Name="lblName"
                Width="45"
                Content="Name:"/>
            <TextBox
                x:Name="tbName"
                Width="200"
                Margin="15,0,0,0"
                Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
        <StackPanel
            Orientation="Horizontal">
            <Label
                x:Name="lblStai"
                Width="60"
                Content="Status:"/>
            <ComboBox
                x:Name="cbStati"
                Width="200"
                ItemsSource="{Binding StatusTypeList}"
                SelectedItem="{Binding StatusType, UpdateSourceTrigger=PropertyChanged}"
                DisplayMemberPath="Name"/>
        </StackPanel>
    </StackPanel>
</Window>

隐藏代码

using System;
using System.Windows;
using System.IO;

using Newtonsoft.Json;

namespace CrazyWpf
{
    public partial class MainWindow : Window
    {
        private DemoViewModel dvm;
        public MainWindow()
        {
            InitializeComponent();

            this.dvm = (DemoViewModel)this.rootElement.DataContext;
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "settings.json");
            if (File.Exists(filePath))
                File.Delete(filePath);

            File.WriteAllText(filePath, JsonConvert.SerializeObject(this.dvm, Formatting.Indented));
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            string filePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "settings.json");
            if (!File.Exists(filePath))
            { this.SetDefaultSettings(); return; }

            DemoViewModel d = JsonConvert.DeserializeObject<DemoViewModel>(File.ReadAllText(filePath));
            this.dvm.ID = d.ID;
            this.dvm.Name = d.Name;
            this.dvm.StatusType = d.StatusType;

        }

    }
}

BaseViewModel:

BaseViewModel:

using System.ComponentModel;

namespace CrazyWpf
{
    public abstract class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

ViewModel

ViewModel

using System;
using System.Collections.Generic;
using System.Linq;

using Newtonsoft.Json;

namespace CrazyWpf
{
    class DemoViewModel : BaseViewModel
    {
        [JsonIgnore]
        private int id;
        [JsonProperty(Order = 1)]
        public int ID
        {
            get { return this.id; }
            set
            {
                if (this.id != value)
                {
                    this.id = value;
                    this.OnPropertyChanged("ID");
                }
            }
        }

        [JsonIgnore]
        private string name;
        [JsonProperty(Order = 2)]
        public string Name
        {
            get { return this.name; }
            set
            {
                if (this.name != value && value != null)
                {
                    this.name = value;
                    this.OnPropertyChanged("Name");
                }
            }
        }

        [JsonIgnore]
        private StatusTyp statusType;
        [JsonProperty(Order = 3)]
        public StatusTyp StatusType
        {
            get { return this.statusType; }
            set
            {
                if (this.statusType != value && value != null)
                {
                    this.statusType = value;
                    this.OnPropertyChanged("StatusType");
                }
            }
        }

        [JsonIgnore]
        private List<StatusTyp> statusTypeList;
        [JsonProperty(Order = 4)]
        public List<StatusTyp> StatusTypeList
        {
            get { return this.statusTypeList; }
            set
            {
                if (this.statusTypeList != value && value != null)
                {
                    this.statusTypeList = value;
                    this.OnPropertyChanged("StatusTypeList");
                }
            }
        }

        public DemoViewModel()
        {
            this.StatusTypeList = new Func<List<StatusTyp>>(() =>
            {
                var list = Enum.GetValues(typeof(Status))
                    .Cast<Status>()
                    .ToDictionary(k => (int)k, v => v.ToString())
                    .Select(e => new StatusTyp()
                    {
                        Value = e.Key,
                        Name = e.Value,
                        Status =
                            Enum.GetValues(typeof(Status))
                                .Cast<Status>().
                                Where(x =>
                                {
                                    return (int)x == e.Key;
                                }).FirstOrDefault()
                    })
                    .ToList();
                return list;
            })();
        }
    }

    public class StatusTyp
    {
        public int Value { get; set; }
        public string Name { get; set; }
        public Status Status { get; set; }
    }

    public enum Status
    {
        NotDetermined = 0,
        Determined = 1,
        Undeterminded = 2,
        Unknown = 3
    }
}

推荐答案

如果您具有ItemsSource和SelectedItem,则SelectedItem中的实例必须位于绑定到ItemsSource的集合中.如果不是,则您的绑定将无法按预期工作.

If you have an ItemsSource and a SelectedItem, the instance in SelectedItem MUST BE in the collection bound to ItemsSource. If it is not, then your bindings will not work as expected.

该控件使用引用相等性来确定ItemsSource中的哪个项目是SelectedItem中的一项,并更新UI.通常这不是问题,因为控件会为您填充SelectedItem,但是如果从ViewModel端进行更新,则必须确保对引用的管理正确.

The control uses reference equality to determine which item in ItemsSource is the one in SelectedItem and update the UI. This normally isn't a problem as the control populates SelectedItem for you, but if you are updating from the ViewModel side, you have to make sure your references are managed correctly.

当序列化/反序列化视图模型时,这可能是一个问题.大多数常见的序列化程序不会跟踪引用,因此无法在反序列化时恢复这些引用.可以在原始对象图中的多个位置引用同一对象,但是在反序列化之后,您现在拥有遍及整个重新水化图中的原始对象的多个实例.这不能满足您的要求.

This can be an issue when serializing/deserializing your view model. Most common serializers don't track references, and so cannot restore these on deserialization. The same object may be referenced multiple places in the original object graph, but after deserialization you now have multiple instances of the original spread throughout the rehydrated graph. This won't work with your requirements.

您需要做的是,在反序列化之后,在您的集合中找到匹配的实例,并将其替换为SelectedItem中的实例.或者,使用跟踪实例的序列化器.. XAML序列化程序已经做到了这一点,并且对于.net对象图来说,它是一个非常好的XML序列化程序.

What you have to do is, after deserializing, find the matching instance in your collection and substitute it for the instance in SelectedItem. Or, use a serializer that tracks instances.. The XAML serializer already does this, and is a surprisingly good xml serializer for .net object graphs.

这篇关于从序列化ViewModel还原时,Combox SelectedItem不适用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆