Need to perform a deep copy in the event handler for ComboBox Selection changed
The DeepCopy fails
But the exact same DeepCopy succeeds in the ctor and an event handler for Button Click
Have reproduced this on two machines
Willing to change the handler or the deep copy but I pretty much need it in the event handler for ComboBox SelectionChanged
Look for the this fails in the code
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.ComponentModel;
namespace DeepCopy
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private List<TestClass> testClassList = new List<TestClass>();
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
try
{
TestClass tca = new TestClass();
tca.Prop1 = "Tcaa";
TestClass tcb = DeepClone<TestClass>(tca);
tca.Prop1 = "TcaNew1";
System.Diagnostics.Debug.WriteLine(tca.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tcb.Prop1 = "sdf1";
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
}
catch (Exception Ex)
{
System.Diagnostics.Debug.WriteLine(Ex.Message);
}
TestClass tc = new TestClass();
tc.Prop1 = "tc1";
testClassList.Add(tc);
tc = new TestClass();
tc.Prop1 = "tc2";
testClassList.Add(tc);
foreach (TestClass tcx in TestClassList)
{
try
{
// this does not fail
TestClass tcb = DeepClone<TestClass>(tcx);
System.Diagnostics.Debug.WriteLine(tc.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tc.Prop1 += "ctorA";
System.Diagnostics.Debug.WriteLine(tc.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tcb.Prop1 += "ctorB";
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
public List<TestClass> TestClassList { get { return testClassList; } }
[Serializable()]
public abstract class TestAstract : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public abstract string Prop1 { get; set; }
}
[Serializable()]
public class TestClass : TestAstract
{
private string prop1;
public override string Prop1
{
get { return prop1; }
set
{
if (prop1 == value) return;
prop1 = value;
NotifyPropertyChanged("Prop1");
}
}
public TestClass() { }
}
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
private void clickDeepCopy(object sender, RoutedEventArgs e)
{
foreach (TestClass tc in TestClassList)
{
try
{
// this does not fail
TestClass tcb = DeepClone<TestClass>(tc);
System.Diagnostics.Debug.WriteLine(tc.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tc.Prop1 += "clickA";
System.Diagnostics.Debug.WriteLine(tc.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tcb.Prop1 += "clickB";
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
private void cbSelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (TestClass tc in TestClassList)
{
try
{
// this fails
TestClass tcb = DeepClone<TestClass>(tc);
// A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll
// Type 'System.ComponentModel.PropertyChangedEventManager' in Assembly 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' is not marked as serializable.
// A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll
System.Diagnostics.Debug.WriteLine(tc.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tc.Prop1 += "cbceA";
System.Diagnostics.Debug.WriteLine(tc.Prop1);
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
tcb.Prop1 = "cbceB";
System.Diagnostics.Debug.WriteLine(tcb.Prop1);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
}
}
<Window x:Class="DeepCopy.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="DeepCopy" Click="clickDeepCopy" />
<ComboBox Grid.Row="1" ItemsSource="{Binding Path=TestClassList}" DisplayMemberPath="Prop1" SelectionChanged="cbSelectionChanged" />
</Grid>
</Window>
More interesting
Tried an empty ComboBox SelectionChanged event handler
The Button Click will fail AFTER an empty ComboBox SelectionChanged is called
Removed the ComboBox SelectionChanged event handler in XAML
The Button Click will fail AFTER a ComboBox SelectionChanged - even with no event handler
But the Button Click will succeed before a ComboBox SelectionChanged
Removed the ComboBox SelectionChanged event handler in XAML
Set the SelectedIndex = 0 in XAML
The Button Click event handler will fail the first time
Even with no SelectionChanged event handler and no SelectedIndex on ComboBox the get is still called on TestClassList
The raw binding alone does NOT break the deep copy
Once any item is selected on the ComboBox then the deep copy breaks
I know you are suspicious but I have spent several hours on this