The AGT (Argentum Tela) series of articles is an effort to do two things. Usually an idea is presented only in its finished form. The first goal is to do some Reality Blogging, to show an idea evolve over time without pulling any punches. The second goal, and the example vehicle for the evolution aspect, is an extensible Design Surface for Silverlight similar to what we have in Visual Studio 2008. This type of application has all sorts of interesting uses. My example is a Home Theater layout tool. Read the entire saga: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx
What’s in a name?
If you have worked in the Windows Forms designer, ASP.Net ViewState, ASP.Net Control Templates, or ever used a design surface of any kind you are likely familiar with the concepts of a Naming Container and Name Providers. The first Label I drag onto a form might get a name of “Label0”, then “Label1” and so on. Many visual technologies will throw an exception if two items inside the same container have the same name. We will need this idea for AGT very soon, so now is the time to implement it.
Design Goals
The design goals for this next step are fairly simple:
- We must have a notion of A Container. This container has children, this container will use some sort of Name provider to provide a default name for each new Child added to it.
- The Type of the children should be inspect able, such that we can supply names like “Foo0”, etc.
- We do not need to support a parent-child relationship between Containers (like asp.net does).
- We should let the Caller set the name on its child
The only remaining decisions are small but important.
- Is there any value to having an INamingContainer interface or can we use an existing mechanism to expose children?
- Is there any value to having an INameable interface for items to be named?
I’m going with “yes”, and “no”, respectively.
INamingContainer
I’m going to create a simple INamingContainer interface which can be implemented by containers (like a design surface) that want their users to have a unique name. I could make this part of the IDesigner interface but I’d like this to be optional. I am still in my IDesignableControl-centric world though.
namespace DamonPayne.AGT.Design
{
/// <summary>
/// Represents something that has children needing unique names
/// </summary>
public interface INamingContainer
{
IList<IDesignableControl> Children{get;}
}
}
This is all we need, we can now enumerate the children and generate a unique name.
INameProvider
The interface for INameProvider is equally simple.
namespace DamonPayne.AGT.Design.Contracts
{
public interface INameProvider
{
string GetUniqueName(INamingContainer container, IDesignableControl newChild);
}
}
As I mentioned before, I’d like to follow the familiar convention of ControlType[number], so I’m ready to build a default implementation for INameProvider. This is pretty easy using LINQ.
namespace DamonPayne.AGT.Design.Services
{
/// <summary>
/// A name provider that uses the last part of type name plus
/// </summary>
public class TypeScopedNameProvider : INameProvider
{
public string GetUniqueName(INamingContainer container, IDesignableControl newChild)
{
var type = newChild.GetType();
string namePart = type.Name;
int existingCount = (from c in container.Children where c.GetType() == type select c).Count();
return namePart + existingCount;
}
}
}
Hooking up DesignSurface
In order to finish up, I only need to hook my implementation up using Unity and call the name provider whenever a new item is added to the DesignSurface.
[Dependency]
public INameProvider NameProvider { get; set; }
private void EnsureName(IDesignableControl dc)
{
if (string.IsNullOrEmpty(dc.DesignTimeName))
{
string name = NameProvider.GetUniqueName(this, ((DesignSite)dc).HostedContent);
dc.DesignTimeName = name;
}
}
I can drag things out without a name now, and everything looks the way it should with the generated naming. My glorious purple couches and poorly drawn speakers have names! Now that I’ve been learning Expression Design I should really take another crack at drawing those speakers.
I’ve really got to fix the way that PropertyGrid looks too.
Conclusion
The Service/Module based approach continues to prove itself out as the correct approach for making it easy to add new functionality to the system. Adding a name provider was a small feature, but the next step won’t be so easy.
You can get the source code from codeplex. This article represents change set 28479.
The live demo has been updated and can be viewed here.