#c# #wpf #mvvm #datamodel
#c# #wpf #mvvm #datamodel
Вопрос:
В моей программе я пытаюсь создать копию модели данных, чтобы я мог установить ее в качестве модели данных другого пользовательского элемента управления.
До сих пор я пытался настроить новую модель данных на модель данных, которую я хочу скопировать, но все, что я сделал, это нацелил оба пользовательских элемента управления на одну и ту же модель данных.
Пример того, что я сделал:
newUserControl.NewDataModel = oldUserControl.OldDataModel;
Как мне создать копию модели данных, чтобы я мог установить ее в качестве контекста модели данных другого пользовательского элемента управления, не заставляя UCS ориентироваться на ту же модель данных?
Ответ №1:
Один из способов сделать это — создать универсальный метод расширения. Это позволяет вам клонировать любой объект независимо от типа, если он сериализуем (имеет атрибут ‘Serializable’).
public static class ObjectExtensions
{
public static T Clone<T>(this T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("This type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null))
return default(T);
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
Как упоминал @TroelsLarsen, существует риск копирования подписок на события. Чтобы избежать этого, вы можете добавить NonSerializedAttribute
в поля, которые вы не хотите сериализовать. Вот документация MSDN с примером.
Затем вы просто используете это так:
newUserControl.DataModel = oldDataUserControl.DataModel.Clone();
Комментарии:
1. Для этого требуется, чтобы объект был сериализуемым. Кроме того, существует риск копирования подписок на события при работе с объектами, напрямую связанными с пользовательским интерфейсом.
2. @TroelsLarsen Да, для этого требуется, чтобы он был сериализуемым, как я упоминаю в ответе. Вы всегда можете добавить
NonSerialized
атрибут к полям, которые вы не хотите сериализовать.3. Как мне добавить
NonSerialized
атрибут в мои модели данных?4. @Ericafterdark В своем ответе я добавил ссылку на документ MSDN, в котором есть пример. Но в основном вы просто добавляете [NonSerialized()] в поле, точно так же, как вы добавляете [Serializable()] в класс.
Ответ №2:
Что бы я сделал, так это внедрил инфраструктуру обмена сообщениями. Это тот, который я использовал раньше, и он намного упрощает задачу. По сути, это событие, которое вы можете настроить для одного элемента управления, а затем как бы подписаться на это событие в другом элементе управления и передавать данные туда и обратно, используя это событие:
Ответ №3:
Вы создаете метод Clone() в классе, который создает новую модель данных, и копирует нужное вам свойство.
public DataModel Clone() {
return new DataModel() {
PropertyA = this.PropertyA,
PropertyB = this.PropertyB,
//etc.
}
}
//OR - Without a clone method:
newUserControl.NewDataModel = new DataModel()
{
PropertyA = oldUserControl.OldDataModel.PropertyA,
PropertyB = oldUserControl.OldDataModel.PropertyB,
//etc
}
Для разработки:
Причина, по которой это не работает, заключается в том, что простое присвоение NewDataModel значению OldDataModal означает копирование только ссылки на этот объект. Другими словами, вы не создаете новую модель данных, вы просто указываете оба свойства на один и тот же экземпляр.
Создавая метод Clone(), вы создаете новую модель данных, идентичную старой, но в другом месте в памяти.
Комментарии:
1. Несколько вопросов: с помощью этого метода мне придется вручную копировать каждое свойство? Кроме того, не мог бы я просто сделать что-то вроде
newUserControl.newDataModel = new DataModel()...
2. Да, и да. Метод Clone() — это просто стандартизированный способ сделать это на случай, если он понадобится вам более одного раза. Но да, используйте ключевое слово new и скопируйте все свойства (или, по крайней мере, те, которые вам нужны), и все готово. Если вы часто это делаете, вы можете рассмотреть возможность использования отражения, чтобы избежать необходимости делать это вручную, но это быстро усложняется. Ответ обновлен с помощью решения, не являющегося клоном.
3. Хорошо, при использовании метода с
new
ключевым словом я обнаружил, что некоторые свойства копируются правильно, но некоторые просто копируют ссылку. Например:string
свойства копируются правильно, ноObservableCollection
свойства копируются только по ссылке.4. Да, ссылочные типы (любые, кроме простых типов) должны быть созданы заново, если вам нужна копия. Проблема, с которой вы столкнулись, заключается в глубоком копировании и мелком копировании ( en.wikipedia.org/wiki/Object_copy ). При таком подходе вам нужно будет клонировать весь граф объектов. Вы можете попытаться автоматизировать это, используя подход evanb, имея в виду, что у вас могут возникнуть проблемы с циклическими ссылками. Все зависит от того, сколько раз в вашей системе вы столкнетесь с этой проблемой.