我有一个自定义视觉元素,我想在我的 silverlight 应用程序中制作一个深层副本。
我尝试了很多东西,但没有找到任何解决方案……这是我找到的最佳解决方案,但原始 DeviceControl 和副本是链接的。如果我改变其中一个的属性,第二个也会改变。我要他们独立!
你有想法吗?
void CloneDevice()
{
DeviceControl control = this;
DeviceControl copy = CloneObject.DeepClone<DeviceControl>(control);
ExtensionMethods.DeepCopy(control, copy);
}
//[Obfuscation(Exclude = true)]
internal static class CloneObject
{
private static bool _firstTry = false;
private static List<FieldInfo> _attachedProperties = null;
// Extension for any class object
internal static TT DeepClone<TT>(this TT source, bool?cloneAttachedProperties = null)
{ // Jim McCurdy's DeepClone
if (cloneAttachedProperties == null)
cloneAttachedProperties = (source is DependencyObject);
// The first time this method is called, compute a list of all
// attached properties that exist in this XAP's assemblies
if (cloneAttachedProperties == true && _attachedProperties == null)
{
_attachedProperties = new List<FieldInfo>();
List<Assembly> assemblies = GetLoadedAssemblies();
foreach (Assembly assembly in assemblies)
GetAttachedProperties(_attachedProperties, assembly);
}
TT clone = CloneRecursive(source);
if (clone is FrameworkElement)
{
FrameworkElement cloneElement = (clone as FrameworkElement);
cloneElement.Arrange(new Rect(0, 0, cloneElement.ActualWidth, cloneElement.ActualHeight));
}
return clone;
}
private static TT CloneRecursive<TT>(TT source)
{
if (source == null || source.GetType().IsValueType)
return source;
// Common types that do not have parameterless constructors
if (source is string || source is Type || source is Uri || source is DependencyProperty)
return source;
TT clone = CloneCreate(source);
if (clone == null)
return source;
if (source is IList)
CloneList(source as IList, clone as IList);
//CloneProperties(source, clone);//ca plante si on prend les propriétées comme ca
return clone;
}
private static TT CloneCreate<TT>(TT source)
{
try
{
#if DEBUG_TRACE
string.Format("Clone create object Type={0}", SimpleType(source.GetType())).Trace();
#endif
Array sourceArray = (source as Array);
if (sourceArray == null)
return (TT)Activator.CreateInstance(source.GetType());
if (sourceArray.Rank == 1)
return (TT)(object)Array.CreateInstance(source.GetType().GetElementType(),
sourceArray.GetLength(0));
if (sourceArray.Rank == 2)
return (TT)(object)Array.CreateInstance(source.GetType().GetElementType(),
sourceArray.GetLength(0), sourceArray.GetLength(1));
}
catch (Exception ex)
{
if (ex.Message.Contains("No parameterless constructor"))
return default(TT);
//string.Format("Can't create object Type={0}", SimpleType(source.GetType())).Trace();
//ex.DebugOutput();
}
return default(TT);
}
private static void CloneProperties(object source, object clone)
{
// The binding flags indicate what properties we will clone
// Unfortunately, we cannot clone "internal" or "protected" properties
BindingFlags flags =
BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public;
if (source is DependencyObject)
{
DependencyObject sourcedp = source as DependencyObject;
DependencyObject clonedp = clone as DependencyObject;
// Clone attached properties
if (_attachedProperties != null && _attachedProperties.Count > 0)
foreach (FieldInfo field in _attachedProperties)
CloneDependencyProperty(sourcedp, clonedp, field, true);
// Clone dependency properties
FieldInfo[] fields = source.GetType().GetFields(flags | BindingFlags.Static);
foreach (FieldInfo field in fields)
CloneDependencyProperty(sourcedp, clonedp, field, false);
}
// Clone CLR properties
if (source is DeviceControl && _firstTry == false)
{
_firstTry = true;
PropertyInfo[] properties = source.GetType().GetProperties(flags);
foreach (PropertyInfo property in properties)
CloneProperty(source, clone, property);
}
}
private static void CloneDependencyProperty(DependencyObject sourceObject,
DependencyObject cloneObject, FieldInfo field, bool isAttached)
{
try
{
// Blacklisted properties that can't (or shouldn't) be set
if (field.Name == "NameProperty" && sourceObject is FrameworkElement) return;
DependencyProperty dp = field.GetValue(sourceObject) as DependencyProperty;
if (dp == null) // Event DependencyProperties will be null
return;
object sourceValue = null;
try
{
sourceValue = sourceObject.GetValue(dp);
}
catch (Exception)
{
}
if (sourceValue == null)
return;
// Don't set attached properties if we don't have to
if (isAttached)
{
Type sourceType = sourceValue.GetType();
if (sourceType.IsValueType && sourceValue.Equals(Activator.CreateInstance(sourceType)))
return;
}
#if DEBUG_TRACE
string.Format("Clone dependency property Name={0}, Value={2} for source Type={1}",
field.Name, SimpleType(sourceObject.GetType()), sourceValue).Trace();
#endif
// Blacklisted properties that can't (or don't need to be) cloned
bool doClone = true;
if (field.Name == "DataContextProperty") doClone = false;
//if (field.Name == "TargetPropertyProperty") doClone = false;
object cloneValue = (doClone ? CloneRecursive(sourceValue) : sourceValue);
cloneObject.SetValue(dp, cloneValue);
}
catch (Exception ex)
{
if (ex.Message.Contains("read-only"))
return;
if (ex.Message.Contains("read only"))
return;
if (ex.Message.Contains("does not fall within the expected range"))
return;
//string.Format("Can't clone dependency property Name={0}, for source Type={1}",
// field.Name, SimpleType(sourceObject.GetType())).Trace();
//ex.DebugOutput();
}
}
private static void CloneProperty(object source, object clone, PropertyInfo property)
{
try
{
if (!property.CanRead || !property.CanWrite || property.GetIndexParameters().Length != 0)
return;
// Blacklisted properties that can't (or shouldn't) be set
if (property.Name == "Name" && source is FrameworkElement) return;
if (property.Name == "InputScope" && source is TextBox) return; // Can't get
if (property.Name == "Watermark" && source is TextBox) return; // Can't get
if (property.Name == "Source" && source is ResourceDictionary) return; // Can't set
if (property.Name == "TargetType" && source is ControlTemplate) return; // Can't set
bool publicSetter = (source.GetType().GetMethod("set_" + property.Name) != null);
bool isList = (property.PropertyType.GetInterface("IList", true) != null);
if (!publicSetter && !isList)
return;
object sourceValue = property.GetValue(source, null);
if (sourceValue == null)
return;
if (!publicSetter && isList)
{
IList cloneList = property.GetValue(clone, null) as IList;
if (cloneList != null)
CloneList(sourceValue as IList, cloneList);
return;
}
#if DEBUG_TRACE
string.Format("Clone property Type={0}, Name={1}, Value={3} for source Type={2}",
SimpleType(property.PropertyType), property.Name, SimpleType(source.GetType()),
sourceValue).Trace();
#endif
// Blacklisted properties that can't (or don't need to be) cloned
bool doClone = true;
if (source is FrameworkElement && property.Name == "DataContext") doClone = false;
//if (property.Name == "TargetProperty") doClone = false;
object cloneValue = (doClone ? CloneRecursive(sourceValue) : sourceValue);
property.SetValue(clone, cloneValue, null); // possible MethodAccessException
}
catch (Exception ex)
{
//string.Format("Can't clone property Type={0}, Name={1}, for source Type={2}",
// SimpleType(property.PropertyType), property.Name, SimpleType(source.GetType())).Trace();
//ex.DebugOutput();
}
}
private static void CloneList(IList sourceList, IList cloneList)
{
try
{
IEnumerator sourceEnumerator = sourceList.GetEnumerator();
Array sourceArray = sourceList as Array;
Array cloneArray = cloneList as Array;
int dim0 = (sourceArray != null && sourceArray.Rank > 0 ? sourceArray.GetLowerBound(0) : 0);
int dim1 = (sourceArray != null && sourceArray.Rank > 1 ? sourceArray.GetLowerBound(1) : 0);
while (sourceEnumerator.MoveNext())
{
object sourceValue = sourceEnumerator.Current;
#if DEBUG_TRACE
string.Format("Clone IList item {0}", sourceValue).Trace();
#endif
object cloneValue = CloneRecursive(sourceValue);
if (sourceArray == null)
cloneList.Add(cloneValue);
else
if (sourceArray.Rank == 1)
cloneArray.SetValue(cloneValue, dim0++);
else
if (sourceArray.Rank == 2)
{
cloneArray.SetValue(cloneValue, dim0, dim1);
if (++dim1 > sourceArray.GetUpperBound(1))
{
dim1 = sourceArray.GetLowerBound(1);
if (++dim0 > sourceArray.GetUpperBound(0))
dim0 = sourceArray.GetLowerBound(0);
}
}
}
}
catch (Exception ex)
{
//string.Format("Can't clone IList item Type={0}", SimpleType(sourceList.GetType())).Trace();
//ex.DebugOutput();
}
}
private static string SimpleType(Type type)
{
string typeName = type.ToString();
int index = typeName.LastIndexOf('[');
if (index < 0)
return typeName.Substring(typeName.LastIndexOf('.') + 1);
string collectionName = typeName.Substring(index);
collectionName = collectionName.Substring(collectionName.LastIndexOf('.') + 1);
typeName = typeName.Substring(0, index);
typeName = typeName.Substring(typeName.LastIndexOf('.') + 1);
return typeName + '[' + collectionName;
}
private static List<Assembly> GetLoadedAssemblies()
{
List<Assembly> assemblies = new List<Assembly>();
foreach (AssemblyPart part in Deployment.Current.Parts)
{
StreamResourceInfo sri =
Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));
if (sri == null)
continue;
Assembly assembly = new AssemblyPart().Load(sri.Stream);
if (assembly != null && !assemblies.Contains(assembly))
assemblies.Add(assembly);
}
// Additional assemblies that are not found when examining of Deployment.Current.Parts above
Type[] types =
{
typeof(System.Windows.Application), // System.Windows.dll,
#if INCLUDE_ASSEMBLIES_WITHOUT_ATTACHED_PROPERTIES
typeof(System.Action), // mscorlib.dll,
typeof(System.Uri), // System.dll,
typeof(System.Lazy<int>), // System.Core.dll,
typeof(System.Net.Cookie), // System.Net.dll,
typeof(System.Runtime.Serialization.StreamingContext), // System.Runtime.Serialization.dll,
typeof(System.ServiceModel.XmlSerializerFormatAttribute), // System.ServiceModel.dll,
typeof(System.Windows.Browser.BrowserInformation), // System.Windows.Browser.dll,
typeof(System.Xml.ConformanceLevel), // System.Xml.dll,
#endif
};
foreach (Type type in types)
{
Assembly assembly = type.Assembly;
if (assembly != null && !assemblies.Contains(assembly))
assemblies.Add(assembly);
}
return assemblies;
}
private static bool GetAttachedProperties(List<FieldInfo> attachedProperties, Assembly assembly)
{
BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static;
foreach (Type type in assembly.GetTypes())
{
FieldInfo[] fields = type.GetFields(flags);
MethodInfo[] methods = null;
foreach (FieldInfo field in fields)
{
if (field.FieldType==(typeof(DependencyProperty)))
continue;
if (!field.Name.EndsWith("Property"))
continue;
string fieldName = field.Name.Replace("Property", "");
string getName = "Get" + fieldName;
string setName = "Set" + fieldName;
bool foundGet = false;
bool foundSet = false;
if (methods == null)
methods = type.GetMethods(flags);
foreach (MethodInfo method in methods)
{
if (method.Name == getName && method.GetParameters().Length == 1 &&
method.GetParameters()[0].ParameterType== (typeof(DependencyObject)))
foundGet = true;
else
if (method.Name == setName && method.GetParameters().Length == 2 &&
method.GetParameters()[0].ParameterType==(typeof(DependencyObject)))
foundSet = true;
if (foundGet && foundSet)
break;
}
if (!(foundGet && foundSet))
continue;
try
{
DependencyProperty dp = field.GetValue(null) as DependencyProperty;
}
catch (Exception)
{
continue;
}
// Found an attached Dependency Property
attachedProperties.Add(field);
}
}
return true;
}
}
public static void DeepCopy(object source, object destination)
{
// Get properties
var propertyInfos = source.GetType().GetProperties();
// Evaluate
if (propertyInfos.Length > 0)
{
foreach (var propInfo in propertyInfos)
{
// Process only public properties
if (propInfo.CanWrite)
{
if (propInfo.Name == "IsSelected")
{
break;
}
else
{
object value = propInfo.GetValue(source, null);
propInfo.SetValue(destination, value, null);
// Evaluate
if (value != null)
{
var newPropInfo = value.GetType().GetProperties();
if (newPropInfo.Length > 0)
{
// Copy properties for each child where necessary
DeepCopy(
source.GetType().GetProperty(propInfo.Name),
destination.GetType().GetProperty(propInfo.Name));
}
}
}
}
}
}
}