0

In my program I am trying to make a copy of a Data Model so that I can set it as the Data Model of another User Control.

So far I have tried setting the new Data Model to the Data Model that I want to copy, but all that did was aim both User Controls to the same Data Model.

An example of what I did:

newUserControl.NewDataModel = oldUserControl.OldDataModel;

How do I make a copy of a data model so that I can set it as the Data Model context of another User Control, without making the UCs aim at the same Data Model?

Eric after dark
  • 1,768
  • 4
  • 31
  • 79

3 Answers3

2

One way to do this is to create a generic extension method. This allows you to clone any object regardless of type, as long as it is serializable (has the 'Serializable' attribute).

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);
            }
     }
}

As @TroelsLarsen mentioned, there is a risk of copying event subscriptions. To avoid that you can add the NonSerializedAttribute to the fields that you don't want serialized. Here is the MSDN documentation with an example.

Then you just use it like so:

newUserControl.DataModel = oldDataUserControl.DataModel.Clone();
evanb
  • 3,061
  • 20
  • 32
  • 2
    This requires the object to be Serializable. Also, there is a risk of copying event subscriptions when dealing with objects directly bound to the UI. – Troels Larsen Jun 27 '14 at 19:50
  • @TroelsLarsen Yes, it does require that it is Serializable as I mention in the answer. You can always add the `NonSerialized` attribute to the fields that you don't want serialized. – evanb Jun 27 '14 at 19:56
  • How would I add a `NonSerialized` attribute to my Data Models? – Eric after dark Jun 27 '14 at 20:34
  • @Ericafterdark I added a link in my answer to the MSDN doc that has an example. But basically you just add [NonSerialized()] to the field, just like you add [Serializable()] to the class. – evanb Jun 27 '14 at 20:54
0

You create a Clone() method on the class that creates a new DataModel, and copies the property you need over.

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
}

To elaborate: The reason this is not working is that by simply assigning NewDataModel to the value of OldDataModal means copying only the reference to that object. In other words, you aren't creating a new DataModel, you are just pointing both properties to the same instance.

By creating a Clone() method, you are creating a new DataModel identical to the old one, but at a different location in memory.

Troels Larsen
  • 4,462
  • 2
  • 34
  • 54
  • A few questions: With this method would I have to manually copy over each property? Also, couldn't I just do something along the lines of `newUserControl.newDataModel = new DataModel()...` – Eric after dark Jun 27 '14 at 19:37
  • Yes, and yes. The Clone() method is merely a standardized way of doing it in case you need it more than once. But yes, use the new keyword and copy over all the properties (or at least the ones you need), and you're all set. If you find yourself doing this often, you can consider using reflection to avoid having to do it manually, but that quickly becomes complicated. Answer updated with non-Clone solution. – Troels Larsen Jun 27 '14 at 19:41
  • Okay, while using the method with the `new` keyword I find that some properties copy over correctly, but some just end up still copying the reference. For example: `string` properties copy correctly, but `ObservableCollection` properties only copy by reference. – Eric after dark Jun 27 '14 at 20:29
  • Yes, reference types (anything except the simple types) will all need to be freshly instantiated if you want a copy. The problem you're having is Deep Copy vs Shallow Copy (http://en.wikipedia.org/wiki/Object_copy). If doing this approach, you will need to clone out the entire object graph. You can try to automate this using evanb's approach, bearing in mind that you might run into problems with circular references. It all depends on how many times in your system you're going to run into this issue. – Troels Larsen Jun 27 '14 at 20:39
0

What I would do is implement a messaging infrastructure. This is one that I've used before and it makes things a lot easier. It's essentially an event that you can set up on one control and then sort of subscribe to that event on another control and pass things back and forth using that event:

http://mvvmlight.codeplex.com/

asven
  • 133
  • 1
  • 7