Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

New FAQ available

Status
Not open for further replies.

chiph

Programmer
Jun 9, 1999
9,878
US
I've just posted a new FAQ:
faq732-3363 - How to create strongly-typed collections of objects

Comments are welcome.

Chip H.
 
Hi Chip,

Thanks for that. If I may humbly add a little something, I think it's important to mention that not only does implementing IEnumerable give you access to the foreach semantics, but it makes your class bindable to any databound object, such as a datagrid, datalist, and the like.

Using this technique can further remove the presentation layer of an application from System.Data, which I would have to say I find even more attractive than the foreach advantage.

Thanks again.

:)
paul
penny1.gif
penny1.gif

The answer to getting answered -- faq855-2992
 
Thanks for the reminder, Paul. I updated the FAQ to include that info.

Didn't even consider databinding when writing it, as I don't use them myself, but you're right - anything that implements IEnumerable/IEnumerator is able to be bound.

Can I get you to write a FAQ on that subject? I can go back and add some more properties to my "Album" and "Song" objects if you want more to work with.

Chip H.
 
That sounds like a great idea. I'll play with it this week and we'll get it up soon.
penny1.gif
penny1.gif

The answer to getting answered -- faq855-2992
 
I'm thinking of writing one afterwards about parameterized constructors. Should be an easy one!

Chip H.
 
Chip,

Wouldn't inheriting System.Collections.CollectionBase work just as easily to create a stongly typed collection?

Craig
 
You could do that too, and I looked at it initially. But it seemed to be much harder, as you had to do a lot more work to manage your collection.

Much easier to encapsulate one of the .NET collections that already has behavior close to what you want.

Chip H.
 
Looked at it and came to the opposite conclusion!

Craig
 
Craig - can you write a FAQ that approaches the same subject from the inheriting from CollectionBase angle?

It'll be good to give people a choice.

Chip H.
 
Can do. I'll reuse your objects too!

Craig
 
Ok I'm back on this one. I have added a Title property to both the Songs class, and the Album class. Just simple string properties that will be what we display in the bound grid.

A few questions, though. First, as I was sitting here playing around with the syntax, it occured to me that the names and the way the classes are setup doesn't make semantic sense when I go to bind it somewhere.

For instance... to me, it seems that the Album object should be the IEnumerable class, since it really contains the songs and is the "collection of songs", if you will. And if we follow that through by moving the enumeration logic to the Album class, then we might also say that the Songs class should be renamed to Song, and I may even submit that we could take Song down to struct status since at that point, he's really only providing a little logical container class for a few primitive data.

What do you think of that idea?

And now to reveal my Windows Forms ignorance. In ASP.NET, I can simply take my IEnumerable class, set it directly as the .DataSource for any databound object, call .DataBind(), and voila... one nicely bound object. But, I've never worked w/ Windows Forms databound controls, and apparently, it isn't that simple. Either that, or there's some other way to do it. Here's what I have that is telling me that I need an IList or an IListSource to work it this way:
Code:
private void bindGrid()
{
  Album album = getBestAlbum();
  dgSongs.DataSource = album.Songs; //it yacks here
}

getBestAlbum simply news up an Album, fills it w/ songs, and returns it. No need to post it here right now.

In the interest of (a)it's late and I really just don't feel like digging on this right now and (b)This is for a FAQ to help everyone, not specifically me, could someone just set me on the straight and narrow on this? I'm freely admitting my laziness here. ;-)

I pondered plopping this in a web form project, but most folks here seem to be windows forms oriented, so I'm thinking it would be better to get this working.

Ok... comments?

:)
paul
penny.gif
penny.gif

The answer to getting answered -- faq855-2992
 
Yeah, it looks like the System.Windows.Forms.DataGrid class wants something that implements IList for it's DataSource.

It turned out to be a little harder to modify the code than I thought it would be:
Code:
using System;
using System.Collections;

namespace ClassLibrary1
{
	public class Songs : IList
	{
		private ArrayList m_SongList;

		public Songs()
		{
			m_SongList = new ArrayList();
		}

		#region Implementation of IList

		public System.Collections.IEnumerator GetEnumerator()
		{
			return new SongEnumerator(this);
		}

		/* IList version of the interator wants an Object returned		
				public Song this[int index]
				{
					get
					{
						return (Song)m_SongList[index];
					}
					set
					{
						m_SongList[index] = value;
					}
				}
		*/

		public Object this[int index]
		{
			get
			{
				return m_SongList[index];
			}
			set
			{
				if (value.GetType().ToString() == "Song") 
				{
					m_SongList[index] = value;
				} 
				else 
				{
					throw new ArgumentException("Value is not a Song");
				}
			}
		}

		public void RemoveAt(int index)
		{
			m_SongList.RemoveAt(index);
		}

		public void Insert(int index, object value)
		{
			if (value.GetType().ToString() == "Song") 
			{
				m_SongList.Insert(index, (Song)value);		
			} 
			else 
			{
				throw new ArgumentException("Value is not a Song");
			}
		}

		public void Remove(object value)
		{
			if (value.GetType().ToString() == "Song") 
			{
				m_SongList.Remove((Song)value);		
			} 
			else 
			{
				throw new ArgumentException("Value is not a Song");
			}
		}

		public bool Contains(object value)
		{
			if (value.GetType().ToString() == "Song") 
			{
				return m_SongList.Contains((Song)value);		
			} 
			else 
			{
				throw new ArgumentException("Value is not a Song");
			}
		}

		public void Clear()
		{
			m_SongList.Clear();		
		}

		public int IndexOf(object value)
		{
			if (value.GetType().ToString() == "Song") 
			{
				return m_SongList.IndexOf((Song)value);		
			} 
			else 
			{
				throw new ArgumentException("Value is not a Song");
			}
		}

		public int Add(object value)
		{
			if (value.GetType().ToString() == "Song") 
			{
				return m_SongList.Add((Song)value);		
			} 
			else 
			{
				throw new ArgumentException("Value is not a Song");
			}
		}

		public bool IsReadOnly
		{
			get
			{
				return m_SongList.IsReadOnly;
			}
		}

		public bool IsFixedSize
		{
			get
			{
				return m_SongList.IsFixedSize;
			}
		}

		#endregion

		#region Implementation of ICollection
		public void CopyTo(System.Array array, int index)
		{
			m_SongList.CopyTo(array, index);		
		}

		public bool IsSynchronized
		{
			get
			{
				return m_SongList.IsSynchronized;
			}
		}

		public int Count
		{
			get
			{
				return m_SongList.Count;
			}
		}

		public object SyncRoot
		{
			get
			{
				return m_SongList.SyncRoot;
			}
		}
		#endregion

		private class SongEnumerator : IEnumerator
		{
			private Songs m_SongRef;
			private int m_Location;

			public SongEnumerator(Songs SongRef) 
			{
				this.m_SongRef = SongRef;
				m_Location = -1;
			}

            #region Implementation of IEnumerator
			public void Reset()
			{
				m_Location = -1;
			}

			public bool MoveNext()
			{
				m_Location++;
				return (m_Location <= (m_SongRef.m_SongList.Count - 1));
			}

			public object Current
			{
				get
				{
					if ((m_Location < 0) || (m_Location > 

						m_SongRef.m_SongList.Count)) 
					{
						return null;
					}
					else
					{
						return m_SongRef.m_SongList[m_Location];
					}
				}
			}

            #endregion
		}
	}
}

It turns out that IList includes IEnumerable, so I was able to remove that. Another bummer was that IList wants the interator to return type Object, instead of the strongly-typed Song class.

Alot of the new code is boiler-plate stuff - only gotcha was you had to check the type of the value passed into the &quot;Set&quot; properties to make sure it was of the correct datatype. Don't want someone adding a cat to the dogs collection!

Regarding your user interface - I haven't done much with the Forms stuff either, other than writing simple test shells to check out class librarys I've written (unit testing). But what I suspect you're thinking of is having a list of albums, which would require an Albums class to enumerate a list of Album objects. For right now, all we have is a list of Songs that belong to an album, so your GUI should start at the context of an album. IOW, given that we have &quot;The Eagles - Hotel California&quot;, show what the songs on the album.

Chip H.


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top