/*************************************************************************************
Extended WPF Toolkit
Copyright (C) 2007-2013 Xceed Software Inc.
This program is provided to you under the terms of the Microsoft Public
License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
For more features, controls, and fast professional support,
pick up the Plus Edition at http://xceed.com/wpf_toolkit
Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
***********************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Windows;
using System.Windows.Media;
using Xceed.Wpf.Toolkit.Core.Utilities;
using Xceed.Wpf.Toolkit.PropertyGrid;
namespace Xceed.Wpf.Toolkit
{
public partial class CollectionControlDialogBase :
Window
{
}
///
/// Interaction logic for CollectionControlDialog.xaml
///
public partial class CollectionControlDialog : CollectionControlDialogBase
{
#region Private Members
private IList originalData = new List();
#endregion
#region Properties
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CollectionControlDialog), new UIPropertyMetadata(null));
public IEnumerable ItemsSource
{
get
{
return (IEnumerable)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty ItemsSourceTypeProperty = DependencyProperty.Register("ItemsSourceType", typeof(Type), typeof(CollectionControlDialog), new UIPropertyMetadata(null));
public Type ItemsSourceType
{
get
{
return (Type)GetValue(ItemsSourceTypeProperty);
}
set
{
SetValue(ItemsSourceTypeProperty, value);
}
}
public static readonly DependencyProperty NewItemTypesProperty = DependencyProperty.Register("NewItemTypes", typeof(IList), typeof(CollectionControlDialog), new UIPropertyMetadata(null));
public IList NewItemTypes
{
get
{
return (IList)GetValue(NewItemTypesProperty);
}
set
{
SetValue(NewItemTypesProperty, value);
}
}
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(CollectionControlDialog), new UIPropertyMetadata(false));
public bool IsReadOnly
{
get
{
return (bool)GetValue(IsReadOnlyProperty);
}
set
{
SetValue(IsReadOnlyProperty, value);
}
}
public static readonly DependencyProperty EditorDefinitionsProperty = DependencyProperty.Register("EditorDefinitions", typeof(EditorDefinitionCollection), typeof(CollectionControlDialog), new UIPropertyMetadata(null));
public EditorDefinitionCollection EditorDefinitions
{
get
{
return (EditorDefinitionCollection)GetValue(EditorDefinitionsProperty);
}
set
{
SetValue(EditorDefinitionsProperty, value);
}
}
public CollectionControl CollectionControl
{
get
{
return _collectionControl;
}
}
#endregion //Properties
#region Constructors
public CollectionControlDialog()
{
InitializeComponent();
}
public CollectionControlDialog(Type itemsourceType)
: this()
{
ItemsSourceType = itemsourceType;
}
public CollectionControlDialog(Type itemsourceType, IList newItemTypes)
: this(itemsourceType)
{
NewItemTypes = newItemTypes;
}
#endregion //Constructors
#region Overrides
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
//Backup data if case "Cancel" is clicked.
if (this.ItemsSource != null)
{
foreach (var item in this.ItemsSource)
{
originalData.Add(this.Clone(item));
}
}
}
#endregion
#region Event Handlers
private void OkButton_Click(object sender, RoutedEventArgs e)
{
if (this.ItemsSource is IDictionary)
{
if (!this.AreDictionaryKeysValid())
{
MessageBox.Show("All dictionary items should have distinct non-null Key values.", "Warning");
return;
}
}
_collectionControl.PersistChanges();
this.DialogResult = true;
this.Close();
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
_collectionControl.PersistChanges(originalData);
this.DialogResult = false;
this.Close();
}
#endregion //Event Hanlders
#region Private Methods
[SecuritySafeCritical]
private object Clone(object source)
{
if (source == null)
return null;
object result = null;
var sourceType = source.GetType();
if (source is Array)
{
using (var stream = new MemoryStream())
{
#pragma warning disable SYSLIB0011 // Type or member is obsolete
var formatter = new BinaryFormatter();
#pragma warning restore SYSLIB0011 // Type or member is obsolete
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
result = (Array)formatter.Deserialize(stream);
}
}
// For IDictionary, we need to create EditableKeyValuePair to edit the Key-Value.
else if ((this.ItemsSource is IDictionary)
&& sourceType.IsGenericType
&& typeof(KeyValuePair<,>).IsAssignableFrom(sourceType.GetGenericTypeDefinition()))
{
result = this.GenerateEditableKeyValuePair(source);
}
else
{
// Initialized a new object with default values
try
{
result = FormatterServices.GetUninitializedObject(sourceType);
}
catch (Exception)
{
}
var constructor = sourceType.GetConstructor(Type.EmptyTypes);
if (constructor != null)
{
constructor.Invoke(result, null);
}
else
{
result = source;
}
}
Debug.Assert(result != null);
if (result != null)
{
var properties = sourceType.GetProperties();
foreach (var propertyInfo in properties)
{
var parameters = propertyInfo.GetIndexParameters();
var index = parameters.GetLength(0) == 0 ? null : new object[] { parameters.GetLength(0) - 1 };
var propertyInfoValue = propertyInfo.GetValue(source, index);
if (propertyInfo.CanWrite)
{
// Look for nested object
if (propertyInfo.PropertyType.IsClass
&& (propertyInfo.PropertyType != typeof(Transform))
&& !propertyInfo.PropertyType.Equals(typeof(string)))
{
// We have a Collection/List of T.
if (propertyInfo.PropertyType.IsGenericType)
{
// Clone sub-objects if the T are non-primitive types objects.
var arg = propertyInfo.PropertyType.GetGenericArguments().FirstOrDefault();
if ((arg != null) && !arg.IsPrimitive && !arg.Equals(typeof(String)) && !arg.IsEnum)
{
var nestedObject = this.Clone(propertyInfoValue);
propertyInfo.SetValue(result, nestedObject, null);
}
else
{
// copy object if the T are primitive types objects.
propertyInfo.SetValue(result, propertyInfoValue, null);
}
}
else
{
var nestedObject = this.Clone(propertyInfoValue);
if (nestedObject != null)
{
// For T object included in List/Collections, Add it to the List/Collection of T.
if (index != null)
{
result.GetType().GetMethod("Add").Invoke(result, new[] { nestedObject });
}
else
{
propertyInfo.SetValue(result, nestedObject, null);
}
}
}
}
else
{
// For T object included in List/Collections, Add it to the List/Collection of T.
if (index != null)
{
result.GetType().GetMethod("Add").Invoke(result, new[] { propertyInfoValue });
}
else
{
// copy regular object
propertyInfo.SetValue(result, propertyInfoValue, null);
}
}
}
}
}
return result;
}
private object GenerateEditableKeyValuePair(object source)
{
var sourceType = source.GetType();
if ((sourceType.GetGenericArguments() == null) || (sourceType.GetGenericArguments().GetLength(0) != 2))
return null;
var propInfoKey = sourceType.GetProperty("Key");
var propInfoValue = sourceType.GetProperty("Value");
if ((propInfoKey != null) && (propInfoValue != null))
{
return ListUtilities.CreateEditableKeyValuePair(propInfoKey.GetValue(source, null)
, sourceType.GetGenericArguments()[0]
, propInfoValue.GetValue(source, null)
, sourceType.GetGenericArguments()[1]);
}
return null;
}
private bool AreDictionaryKeysValid()
{
var keys = _collectionControl.Items.Select(x =>
{
var keyType = x.GetType().GetProperty("Key");
if (keyType != null)
{
return keyType.GetValue(x, null);
}
return null;
});
return (keys.Distinct().Count() == _collectionControl.Items.Count)
&& keys.All(x => x != null);
}
#endregion
}
}