You can get the type to be created by calling ItemsSource.GetType().GetInterfaces(), finding the Type object for the IEnumerable<T> interface (which any generic collection will implement), and calling GetGenericArguments() on it. IEnumerable<T> has one type argument, of course, so that's the type you need to create an instance of.
Then you can create an instance fairly easily (see UPDATE below for a static method which wraps this all up into a single method call):
ObjectType instance = (ObjectType)Activator.CreateInstance("AssemblyName",
"MyNamespace.ObjectType");
You'll need the assembly in which the type is declared, but that's a property of Type. Assembly has a CreateInstance method as well. Here's another way to do the same thing:
Type otype = typeof(ObjectType);
ObjectType instance = (ObjectType)otype.Assembly.CreateInstance(otype.FullName);
If the type to be instantiated doesn't have a default constructor, this gets uglier. You'd have to write explicit code to provide values, and there's no way to guarantee that they make any sense. But at least that's a much lighter burden to impose on the consumer than a mess of IPOCOFactory implementations.
Remember by the way that System.String doesn't have a default constructor. It's natural to test the code below with List<String>, but that's going to fail.
Once you have the type of the objects in ItemsSource, you can further simplify maintenance by programmatically enumerating the names and types of the properties and auto-generating columns. If desired, you could write an Attribute class to control which ones are displayed, provide display names, etc. etc.
UPDATE
Here's a rough implementation that's working for me to create instances of a class declared in a different assembly:
/// <summary>
/// Collection item type must have default constructor
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public static Object CreateInstanceOfCollectionItem(IEnumerable items)
{
try
{
var itemType = items.GetType()
.GetInterfaces()
.FirstOrDefault(t => t.Name == "IEnumerable`1")
?.GetGenericArguments()
.First();
// If it's not generic, we may be able to retrieve an item and get its type.
// System.Windows.Controls.DataGrid will auto-generate columns for items in
// a non-generic collection, based on the properties of the first object in
// the collection (I tried it).
if (itemType == null)
{
itemType = items.Cast<Object>().FirstOrDefault()?.GetType();
}
// If that failed, we can't do anything.
if (itemType == null)
{
return null;
}
return itemType.Assembly.CreateInstance(itemType.FullName);
}
catch (Exception ex)
{
return null;
}
}
public static TestCreate()
{
var e = Enumerable.Empty<Foo.Bar<Foo.Baz>>();
var result = CreateInstanceOfCollectionItem(e);
}
You could make CreateInstanceOfCollectionItem() an extension method on IEnumerable if you like:
var newItem = ItemsSource?.CreateInstanceOfCollectionItem();
NOTE
This depends on the actual collection being a generic collection, but it doesn't care about the type of your reference to the collection. ItemsControl.ItemsSource is of the type System.Collections.IEnumerable, because any standard generic collection supports that interface, and so can be cast to it. But calling GetType() on that non-generic interface reference will return the actual real runtime type of the object on the other end (so to speak) of the reference:
var ienumref = (new List<String>()) as System.Collections.IEnumerable;
// fullName will be "System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
// ...or something like it, for whatever version of .NET is on the host.
var fullName = ienumref.GetType().Name;