/************************************************************************ Extended WPF Toolkit Copyright (C) 2010-2012 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 This program can be provided to you by Xceed Software Inc. under a proprietary commercial license agreement for use in non-Open Source projects. The commercial version of Extended WPF Toolkit also includes priority technical support, commercial updates, and many additional useful WPF controls if you license Xceed Business Suite for WPF. Visit http://xceed.com and follow @datagrid on Twitter. **********************************************************************/ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Xceed.Wpf.Toolkit { public class CollectionEditor : Control { #region Properties public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register( "Items", typeof( ObservableCollection ), typeof( CollectionEditor ), new UIPropertyMetadata( null ) ); public ObservableCollection Items { get { return ( ObservableCollection )GetValue( ItemsProperty ); } set { SetValue( ItemsProperty, value ); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( "ItemsSource", typeof( IList ), typeof( CollectionEditor ), new UIPropertyMetadata( null, OnItemsSourceChanged ) ); public IList ItemsSource { get { return ( IList )GetValue( ItemsSourceProperty ); } set { SetValue( ItemsSourceProperty, value ); } } private static void OnItemsSourceChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { CollectionEditor collectionEditor = ( CollectionEditor )d; if( collectionEditor != null ) collectionEditor.OnItemSourceChanged( ( IList )e.OldValue, ( IList )e.NewValue ); } public void OnItemSourceChanged( IList oldValue, IList newValue ) { if( newValue != null ) { foreach( var item in newValue ) Items.Add( CreateClone( item ) ); } } public static readonly DependencyProperty ItemsSourceTypeProperty = DependencyProperty.Register( "ItemsSourceType", typeof( Type ), typeof( CollectionEditor ), new UIPropertyMetadata( null, new PropertyChangedCallback( ItemsSourceTypeChanged ) ) ); public Type ItemsSourceType { get { return ( Type )GetValue( ItemsSourceTypeProperty ); } set { SetValue( ItemsSourceTypeProperty, value ); } } private static void ItemsSourceTypeChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { CollectionEditor collectionEditor = ( CollectionEditor )d; if( collectionEditor != null ) collectionEditor.ItemsSourceTypeChanged( ( Type )e.OldValue, ( Type )e.NewValue ); } protected virtual void ItemsSourceTypeChanged( Type oldValue, Type newValue ) { List types = new List(); Type listType = GetListItemType( newValue ); if( listType != null ) { types.Add( listType ); } NewItemTypes = types; } public static readonly DependencyProperty NewItemTypesProperty = DependencyProperty.Register( "NewItemTypes", typeof( IList ), typeof( CollectionEditor ), new UIPropertyMetadata( null ) ); public IList NewItemTypes { get { return ( IList )GetValue( NewItemTypesProperty ); } set { SetValue( NewItemTypesProperty, value ); } } public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( "SelectedItem", typeof( object ), typeof( CollectionEditor ), new UIPropertyMetadata( null ) ); public object SelectedItem { get { return ( object )GetValue( SelectedItemProperty ); } set { SetValue( SelectedItemProperty, value ); } } #endregion //Properties #region Constructors static CollectionEditor() { DefaultStyleKeyProperty.OverrideMetadata( typeof( CollectionEditor ), new FrameworkPropertyMetadata( typeof( CollectionEditor ) ) ); } public CollectionEditor() { Items = new ObservableCollection(); CommandBindings.Add( new CommandBinding( ApplicationCommands.New, AddNew, CanAddNew ) ); CommandBindings.Add( new CommandBinding( ApplicationCommands.Delete, Delete, CanDelete ) ); CommandBindings.Add( new CommandBinding( ComponentCommands.MoveDown, MoveDown, CanMoveDown ) ); CommandBindings.Add( new CommandBinding( ComponentCommands.MoveUp, MoveUp, CanMoveUp ) ); } #endregion //Constructors #region Commands private void AddNew( object sender, ExecutedRoutedEventArgs e ) { var newItem = CreateNewItem( ( Type )e.Parameter ); Items.Add( newItem ); SelectedItem = newItem; } private void CanAddNew( object sender, CanExecuteRoutedEventArgs e ) { Type t = e.Parameter as Type; if( t != null && t.GetConstructor( Type.EmptyTypes ) != null ) e.CanExecute = true; } private void Delete( object sender, ExecutedRoutedEventArgs e ) { Items.Remove( e.Parameter ); } private void CanDelete( object sender, CanExecuteRoutedEventArgs e ) { e.CanExecute = e.Parameter != null; } private void MoveDown( object sender, ExecutedRoutedEventArgs e ) { var selectedItem = e.Parameter; var index = Items.IndexOf( selectedItem ); Items.RemoveAt( index ); Items.Insert( ++index, selectedItem ); SelectedItem = selectedItem; } private void CanMoveDown( object sender, CanExecuteRoutedEventArgs e ) { if( e.Parameter != null && Items.IndexOf( e.Parameter ) < ( Items.Count - 1 ) ) e.CanExecute = true; } private void MoveUp( object sender, ExecutedRoutedEventArgs e ) { var selectedItem = e.Parameter; var index = Items.IndexOf( selectedItem ); Items.RemoveAt( index ); Items.Insert( --index, selectedItem ); SelectedItem = selectedItem; } private void CanMoveUp( object sender, CanExecuteRoutedEventArgs e ) { if( e.Parameter != null && Items.IndexOf( e.Parameter ) > 0 ) e.CanExecute = true; } #endregion //Commands #region Methods private static void CopyValues( object source, object destination ) { FieldInfo[] myObjectFields = source.GetType().GetFields( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance ); foreach( FieldInfo fi in myObjectFields ) { fi.SetValue( destination, fi.GetValue( source ) ); } } private object CreateClone( object source ) { object clone = null; Type type = source.GetType(); clone = Activator.CreateInstance( type ); CopyValues( source, clone ); return clone; } private IList CreateItemsSource() { IList list = null; if( ItemsSourceType != null ) { ConstructorInfo constructor = ItemsSourceType.GetConstructor( Type.EmptyTypes ); list = ( IList )constructor.Invoke( null ); } return list; } private object CreateNewItem( Type type ) { return Activator.CreateInstance( type ); } internal static Type GetListItemType(Type listType) { Type iListOfT = listType.GetInterfaces().FirstOrDefault( ( i ) => i.IsGenericType && i.GetGenericTypeDefinition() == typeof( IList<> ) ); return ( iListOfT != null ) ? iListOfT.GetGenericArguments()[ 0 ] : null; } public void PersistChanges() { IList list = ResolveItemsSource(); if( list == null ) return; //the easiest way to persist changes to the source is to just clear the source list and then add all items to it. list.Clear(); foreach( var item in Items ) { list.Add( item ); } } private IList ResolveItemsSource() { if( ItemsSource == null ) ItemsSource = CreateItemsSource(); return ItemsSource; } #endregion //Methods } }