Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Item-Level Presentation Models for WPF

, 2 Apr 2011 GPL3
Make your life easier by inserting a Presentation Model layer (aka ViewModel) between your domain-model collection contents and template-generated WPF objects.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;

namespace HappyNomad.BidirectionalAssociations {
	/// <summary>
	/// Keeps both sides of a bidirectional one-to-many association in sync with each other.
	/// </summary>
	/// <author>Adrian Alexander</author>
	public sealed class OneToManyAssocSync {
		private object thisOneSide;

		/// <param name="thisOneSide">The entity participating in the association which has a multiplicity of one</param>
		/// <param name="manyToOnePropertyName"></param>
		public OneToManyAssocSync( object thisOneSide, string manyToOnePropertyName ) {
			this.thisOneSide = thisOneSide;
			this.manyToOnePropertyName = manyToOnePropertyName;
		}

		/// <summary>
		/// Responds to add/remove events raised by the one-side's collection.
		/// </summary>
		public void UpdateManySide( object sender, NotifyCollectionChangedEventArgs e ) {
			if ( e.Action == NotifyCollectionChangedAction.Add ) {
				//addingToManySide: the item that was just added to this one-side's collection
				foreach ( object addingToManySide in e.NewItems )
					if ( addingToManySide != null ) {
						IList list = sender as IList;
						if ( NavigateManyToOne(addingToManySide) != thisOneSide )
							SetManyToOne( addingToManySide, thisOneSide );
						else if ( list != null && CollectionsUtil.Count(list, addingToManySide) == 2 ) {
							throw new InvalidOperationException("Cannot synchronize an item being moved within a single list. You must remove the item before re-adding it.");
							/*Thread t = new Thread( new ThreadStart( delegate() {
								for ( int i = 0; i < list.Count; i++ ) {
									if ( list[i] == addingToManySide && i != e.NewStartingIndex )
										list.RemoveAt( i ); //remove from old location
								}
							} ) );
							t.Start();*/
						}
					}
			} else if ( e.Action == NotifyCollectionChangedAction.Remove ) {
				//removingFromManySide: the item that was just removed from this one-side's collection
				foreach ( object removingFromManySide in e.OldItems )
					if ( NavigateManyToOne( removingFromManySide ) == thisOneSide )
						SetManyToOne( removingFromManySide, null );
			}
		}

		/// <summary>
		/// Responds to a many-side entity's parent one-side property being set to a new value.
		/// </summary>
		public static void UpdateOneSide<T>( T thisManySide, object oldOneSide, object newOneSide, string oneToManyPropertyName ) {
			if ( oldOneSide != null && oldOneSide != newOneSide ) {
				ICollection<T> oldCollection =
				  ReflectionUtil.NavigateToManySide<T>( oldOneSide, oneToManyPropertyName );
				if ( oldCollection.Contains(thisManySide) ) {
					Console.WriteLine( "\tremoving from old parent collection" );
					oldCollection.Remove( thisManySide );
					if ( oldCollection is IList && oldCollection.Contains(thisManySide) ) // true if there was more than one
						throw new InvalidOperationException( "Collection is non-unique" );
				}
			} 
			if ( newOneSide != null ) {
				ICollection<T> newCollection =
				  ReflectionUtil.NavigateToManySide<T>( newOneSide, oneToManyPropertyName );
				if ( ReflectionUtil.IsInitialized(newCollection) && !newCollection.Contains(thisManySide) ) {
					Console.WriteLine( "\tadding to new parent collection" );
					if ( newCollection is IList && newCollection.Contains(thisManySide) ) // true if add will cause duplicates
						throw new InvalidOperationException( "Collection is non-unique" );
					newCollection.Add( thisManySide );
				}
			}
		}

		#region Many-to-One Navigation

		private string manyToOnePropertyName;
		private PropertyInfo manyToOneProperty;

		private object NavigateManyToOne( object manySide ) {
			return GetManyToOneProperty( manySide.GetType() ).GetValue( manySide, null );
		}

		private void SetManyToOne( object manySide, object newValue ) {
			GetManyToOneProperty( manySide.GetType() ).SetValue( manySide, newValue, null );
		}

		private PropertyInfo GetManyToOneProperty( Type manySideType ) {
			if ( manyToOneProperty == null ) {
				PropertyInfo pi = manySideType.GetProperty( manyToOnePropertyName );
				manyToOneProperty = pi.DeclaringType.GetProperty( manyToOnePropertyName );
			}
			return manyToOneProperty;
		}

		#endregion
	
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Adrian Alexander

United States United States
Adrian loves facilitating suave user experiences via the latest and greatest GUI technologies such as Windows 8 Metro-style apps as well as WPF. More generally, he finds joy in architecting software that is easy to comprehend and maintain. He does so by applying design patterns at the top-level, and by incessantly refactoring code at lower levels. He's always interested in hearing about opportunities for full or part-time development work. He resides in Pennsylvania but can potentially travel anywhere in the country. (Writing about himself in the third-person is Adrian's new hobby.)

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150327.1 | Last Updated 2 Apr 2011
Article Copyright 2008 by Adrian Alexander
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid