Я пишу приложение с графическим интерфейсом на основе WPF, в котором пользователь может сохранять текущие значения поля ввода в различных пресетах, которые впоследствии могут быть загружены в любое время (например, после перезапуска приложения или когда пользовательнеобходимо восстановить определенный набор значений). Все поля, которые должны быть сохранены, используют привязку к полям в классе MainWindowViewModel, например:
public class MainWindowViewModel : INotifyPropertyChanged
{
[XmlIgnore]
public int _UpperLimit_Textbox;
public int UpperLimit_Textbox
{
get
{
return _UpperLimit_Textbox;
}
set
{
_UpperLimit_Textbox = value;
NotifyPropertyChanged("UpperLimit_Textbox");
}
}
}
Этот класс создается следующим образом:
public static MainWindowViewModel mainTaskpaneWindow_ModelView = new MainWindowViewModel();
И в Xaml,привязка выполняется следующим образом:
<TextBox Name="UpperLimit_Textbox" Width="30" HorizontalAlignment="Left"
>
<Binding
Path="UpperLimit_Textbox"
UpdateSourceTrigger="PropertyChanged"
Mode="TwoWay">
</Binding>
</TextBox>
Теперь, чтобы сохранить пресет, я просто сериализовал класс MainWindowViewModel в файл .xml:
static void SerializeMainTaskpaneModelView(string PresetPath)
{
StreamWriter streamWriter = new StreamWriter(path, true);
streamWriter.WriteLine(SerializeObject(mainTaskpaneWindow_ModelView));
streamWriter.Close();
}
public static string SerializeObject<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
Эта часть работает как шарм. Проблема в том, что я пытаюсь десериализовать этот объект обратно:
static void DeserializeMainTaskpaneModelView(string presetPath)
{
StreamReader streamReader = new StreamReader(path);
string str = streamReader.ReadToEnd();
mainTaskpaneWindow_ModelView = Deserialize<MainWindowViewModel>(str);
}
public static T Deserialize<T>(this string toDeserialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader textReader = new StringReader(toDeserialize))
{
return (T)xmlSerializer.Deserialize(textReader);
}
}
Теперь эта часть не работает. MainTaskpaneModelView десериализован, но связь с Xaml потеряна (изменения в полях ввода в графическом интерфейсе не отражаются в десериализованном объекте). Насколько я понимаю, обязательная ссылка почему-то не переносится со старого объекта на новый. Поэтому я реализовал другой подход - десериализацию в новый объект MainTaskpaneModelView, а затем копирование его свойств по значению по отдельности в старый объект MainTaskpaneModelView, например, так:
static void DeserializeMainTaskpaneModelView(string presetPath)
{
MainWindowViewModel deserializedMainViewModel = new MainWindowViewModel();
StreamReader streamReader = new StreamReader(path);
string str = streamReader.ReadToEnd();
deserializedMainViewModel = Deserialize<MainWindowViewModel>(str);
CopyObjectProperties(deserializedMainViewModel, mainTaskpaneWindow_ModelView);
}
public static void CopyObjectProperties(this object source, object destination)
{
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
var results = from srcProp in typeSrc.GetProperties()
let targetProperty = typeDest.GetProperty(srcProp.Name)
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
&& targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new { sourceProperty = srcProp, targetProperty = targetProperty };
foreach (var props in results)
{
props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
}
}
Этот подход работает с примитивными типами полей ввода (string, int, double и т. д.), но когда я использую привязку со свойствами типа объекта (например, ListView для ObservableCollection), десериализация больше не работает с этими свойствами - ссылка на привязку теряется. Я предполагаю, что это потому, что ObservableCollection является объектом внутри объекта MainTaskpaneModelView, а метод CopyObjectProperties копирует ObservableCollection по ссылке, а не копирует его внутренние свойства по значению.
Полагаю, что я мог бы каким-то образом изменить CopyObjectPropertiesметод, позволяющий различать примитивные типы и типы классов и обрабатывать их соответствующим образом (возможно, вызывая себя рекурсивно для встроенных объектов?), но я не знаю, как это сделать.
В качестве альтернативы мне интересно,возможно, я могу каким-то образом заменить привязанный к привязке MainTaskpaneModelView десериализованным MainTaskpaneModelView таким образом, чтобы привязка теперь ссылалась на десериализованный. Возможно, как-то перенести ссылочный указатель? Я даже не уверен, что это можно сделать в C #. Таким образом, мне не пришлось бы использовать метод CopyObjectProperties.
И у меня возникает ощущение, что это становится намного сложнее, чем должно быть, и должен быть какой-то более простой способ сохранить входные данныемой графический интерфейс в XML-файл, а затем восстановить их обратно, не испортив привязку. Может кто-нибудь посоветовать, пожалуйста?