情况:
我有一个绑定到设置视图的ViewModel,用户应该输入许多个性化设置,并且应该能够将它们保存为预设并加载它们。为此,ViewModel保存预设数据模型的集合,这些模型本身包含不同的属性,类对象等。计划是通过整个ViewModel的xml序列化和反序列化来实现保存所有预设。
代码/问题
从ViewModel的构造函数中,我调用以下方法:
private void InitializePresetsFromFile()
{
if (!File.Exists(Info.GetDefaultColorPalettePresetsXml()))
{
SetupNewEmpty();
SerializePresets(Info.GetDefaultColorPalettePresetsXml());
}
else
{
DeserializePresets(Info.GetDefaultColorPalettePresetsXml());
}
}
因此,方法检查保存预设的文件是否存在 - 如果不存在,则应设置空预设并将其保存到新创建的文件中,否则应从现有文件加载预设。
序列化过程工作正常,但是因为我序列化到this
,所以deserializiation存在问题:
private void DeserializePresets(string path)
{
XmlSerializer deserializer = new XmlSerializer(typeof(LinearAxisColorPresetsViewModel));
TextReader reader = new StreamReader(path);
object obj = deserializer.Deserialize(reader);
LinearAxisColorPresetsViewModel XmlData = (LinearAxisColorPresetsViewModel)obj;
reader.Close();
VolumePresetList = XmlData.VolumePresetList;
WaveShapePresetList = XmlData.WaveShapePresetList;
VolumePresetSelectedIndex = XmlData.VolumePresetSelectedIndex;
WaveShapePresetSelectedIndex = XmlData.WaveShapePresetSelectedIndex;
}
这里的问题是,因为我直接从构造函数调用方法InitializePresetsFromFile()
,所以反序列化器在一个永不停止的循环中调用自己,导致stackoverflow错误。
所以,最简单的解决方案应该是使用带参数的另一个构造函数,我在其中调用InitializePresetsFromFile()
,对吧?这里的问题是ViewModel类直接在相应View的xaml中实例化:
<UserControl.Resources>
<ResourceDictionary>
<vm:LinearAxisColorPresetsViewModel x:Key="vm" />
</ResourceDictionary>
</UserControl.Resources>
This posts第二个回答说,约定是,从XAML调用的构造函数应该是无参数的,我想坚持这一点。
问题:
问题是如何根据最佳实践解决这个问题。由于这是我第一次尝试序列化和反序列化,我担心,我在这里走错了路。我的感觉是只应序列化数据模型类。我的ViewModel包含两个这样的类的ObservableCollection,但是我想序列化整个集合以及ViewModel中的其他属性,例如所选索引。
你确实已经达到了必须决定如何继续的程度。你现在正在做的事情是行不通的。在这种情况下,XML序列化程序和XAML都使用默认构造函数。你不能在这里达到两个目的。
我的建议是创建一个类,该类镜像视图模型中的属性,用于反序列化XML文件。这个类只需要属性,仅此而已。
如果视图模型类实际上是静态的,则可以使用a locator class将其绑定到。
首先,你不应该在类的构造函数中调用InitializePresetsFromFile
方法。构造函数必须尽可能快,不应引起副作用。在构造函数中读取文件是一种不好的做法:如果不访问文件系统,则无法创建类的实例。这意味着你的代码是不可测试的,它容易出错(例如你是否考虑过突然的UnauthorizedAccessException
s?),而且它很慢。
而是创建一个公共方法,从文件中反序列化数据。这将打破你无休止的递归。
如何调用该方法?
LinearAxisColorPresetsViewModel
实例吗?如果没有,只需将反序列化的实例分配给视图的DataContext
属性。ICommand
,例如InitializeCommand
使用上述方法从文件初始化内部状态;在app启动/查看显示等处执行该命令。您可以使用例如InvokeCommandAction
为Loaded
事件。