Work Spaces and Smart Part Info Provider

As I understand it, the MVP (Model/View/Presenter) design pattern states that all presentation decisions are the Presenter’s responsibility.

In the CAB world, this should include the Smart Part Info.

But the IWorkspace interface only allows you to provide the Smart Part Info (a class that implements the ISmartPartInfo interface and each work space uses a specific one), or the the smart part (the View in the MVP design pattern) should know what information to provide to the work space it lives in by implementing the ISmartPartInfoProvider interface.

To enable the presenter to provide the smart part info changes need to be made to the IWorkspace interface and its implementations. New Show and ApplySmartPartInfo methods need to be added:

/// <summary>
/// Applies the smartPartInfo to the smartPart.

/// </summary>
void ApplySmartPartInfo(object smartPart, ISmartPartInfoProvider smartPartInfoProvider);

/// <summary>
/// Shows SmartPart using the given SmartPartInfo
/// </summary>
/// <param name="smartPart">Smart part to show.</param>
/// <param name="smartPartInfoProvider"></param>
void Show(object smartPart, ISmartPartInfoProvider smartPartInfoProvider);


And it should be implemented in all the classes that implement IWorkspace:


/// <summary>
/// Applies the smartPartInfo to the smartPart.
/// </summary>
public void ApplySmartPartInfo(object smartPart, ISmartPartInfoProvider smartPartInfoProvider)
{
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPart, “smartPart“);
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPartInfoProvider, “smartPartInfoProvider“);
    ThrowIfUnsupportedSP(smartPart);
    ThrowIfSmartPartNotShownPreviously((TSmartPart)smartPart);

    TSmartPart typedSmartPart = (TSmartPart)smartPart;
    TSmartPartInfo typedSmartPartInfo = GetSupportedSPI(smartPartInfoProvider.GetSmartPartInfo(typeof(TSmartPartInfo)));
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(typedSmartPartInfo, “typedSmartPartInfo“);

    OnApplySmartPartInfo(typedSmartPart, typedSmartPartInfo);
}

/// <summary>
/// Shows SmartPart using the given SmartPartInfo
/// </summary>
/// <param name=”smartPart”>Smart part to show.</param>
/// <param name=”smartPartInfoProvider”></param>
public void Show(object smartPart, ISmartPartInfoProvider smartPartInfoProvider)
{
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPart, “smartPart“);
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPartInfoProvider, “smartPartInfoProvider“);
    ThrowIfUnsupportedSP(smartPart);
       
    TSmartPart typedSmartPart = (TSmartPart)smartPart;

    if (smartParts.Contains(typedSmartPart))
    {
        ApplySmartPartInfo(typedSmartPart, smartPartInfoProvider);
        Activate(typedSmartPart);
    }
    else
    {
        TSmartPartInfo typedSmartPartInfo = GetSupportedSPI(smartPartInfoProvider.GetSmartPartInfo(typeof(TSmartPartInfo)));
        Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(typedSmartPartInfo, “typedSmartPartInfo“);
        smartParts.Add(typedSmartPart);
        OnShow(typedSmartPart, typedSmartPartInfo);
    }
}

Enhancing CAB’s ManagedObjectCollection

For those who don’t know, the ManagedObjectCollection registers its elements using the objects running type as part of the key.

Recently, I had the need to add and retrieve a component to the WorkItem‘s Items collection independently of its running. I only wanted to retrieve the component (using a ComponentDependency) with a specified name. Needless to say that I couldn’t, because I was trying to retrieve an object, and that was not the running type of the object added to the WorkItem.

I started thinking of the ability of the ServiceCollection to add items registering them with a different type would be very helpfully to the ManagedObjectCollection. So, decided to add some changes.

To be able to build elements and register them with a different type, I had to change the Build method: 

private TItem Build(Type typeToBuild, Type typeToRegisterAs, string idToBuild, object item)
{
	if (idToBuild != null && Contains(idToBuild, SearchMode.Local, true))
		throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
			Properties.Resources.DuplicateID, idToBuild));

	if (item != null && Object.ReferenceEquals(item,
		locator.Get(new DependencyResolutionLocatorKey(typeof(WorkItem), null))))
		throw new ArgumentException(Properties.Resources.CannotAddWorkItemToItself, "item");

	if (item == null)
		item = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, idToBuild, null);
	else if (!container.Contains(item))
		item = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, idToBuild, item);
	else
		BuildRepeatedItem(typeToBuild, typeToRegisterAs, idToBuild, item);

	return (TItem)item;
}

private object BuildFirstTimeItem(Type typeToBuild, Type typeToRegisterAs, string idToBuild, object item)
{
    item = builder.BuildUp(locator, typeToBuild, NormalizeID(idToBuild), item);

    if (typeToRegisterAs != typeToBuild)
    {
        locator.Add(new DependencyResolutionLocatorKey(typeToRegisterAs, idToBuild), item);
        locator.Remove(new DependencyResolutionLocatorKey(typeToBuild, idToBuild));
    }

    return item;
}

private void BuildRepeatedItem(Type typeToBuild, Type typeToRegisterAs, string idToBuild, object item)
{
    locator.Add(new DependencyResolutionLocatorKey(typeToRegisterAs, NormalizeID(idToBuild)), item);
}

And, to assure compability with the existing methods:

private TItem Build(Type typeToBuild, string idToBuild, object item)
{
    return Build(typeToBuild, typeToBuild, idToBuild, item);
}

Now, all I needed was to add the new methods to add elements:

public void Add(Type typeToRegisterAs, TItem item)
{
    Add(typeToRegisterAs, item, null);
}

public void Add(Type typeToRegisterAs, TItem item, string id)
{
    Guard.ArgumentNotNull(item, "item");
    Guard.TypeIsAssignableFromType(typeof(TItem), typeToRegisterAs, "typeToBuild");

    Build(typeToRegisterAs, item.GetType(), id, item);
}

public void Add<TTypeToRegisterAs>(TTypeToRegisterAs item)
    where TTypeToRegisterAs : TItem
{
    Add<TTypeToRegisterAs>(item, null);
}

public void Add<TTypeToRegisterAs>(TTypeToRegisterAs item, string id)
    where TTypeToRegisterAs : TItem
{
    Guard.ArgumentNotNull(item, "item");

    Build(item.GetType(), typeof(TTypeToRegisterAs), id, item);
}

public TItem AddNew(Type typeToBuild, Type typeToRegisterAs)
{
    Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToBuild");

    return AddNew(typeToBuild, typeToRegisterAs, null);
}

public TItem AddNew(Type typeToBuild, Type typeToRegisterAs, string id)
{
    Guard.TypeIsAssignableFromType(typeToBuild, typeof(TItem), "typeToBuild");
    Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToRegisterAs");

    return Build(typeToBuild, typeToRegisterAs, id, null);
}

public TTypeToBuild AddNew<TTypeToBuild, TTypeToRegisterAs>()
    where TTypeToRegisterAs : TItem
    where TTypeToBuild : TTypeToRegisterAs
{
    return (TTypeToBuild)Build(typeof(TTypeToBuild), typeof(TTypeToRegisterAs), null, null);
}

public TTypeToBuild AddNew<TTypeToBuild, TTypeToRegisterAs>(string id)
    where TTypeToRegisterAs : TItem
    where TTypeToBuild : TTypeToRegisterAs
{
    return (TTypeToBuild)Build(typeof(TTypeToBuild), typeof(TTypeToRegisterAs), id, null);
}

Using the ASP.NET Development Server

Not everyone knows that the ASP.NET Development Server is not part of Visual Studio, but, in fact, is part of the .NET Framework 2.0.


The ASP.NET Development Server is an executable (WebDev.WebServer.EXE) located in the framework installation directory (usually: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727).


To find out how to use it you one just has to run the executable. Nevertheless, here are the instructions:


ASP.NET Development Server Usage:
WebDev.WebServer /port:<port number> /path:<physical path> [/vpath:<virtual path>]

port number:
[Optional] An unused port number between 1 and 65535.
The default is 80 (usable if you do not also have IIS listening on the same port).

physical path:
A valid directory name where the Web application is rooted.

virtual path:
[Optional] The virtual path or application root in the form of ‘/<app name>’.
The default is simply ‘/’.

Example:
WebDev.WebServer /port:8080 /path:”c:\inetpub\wwwroot\MyApp” /vpath:”/MyApp”

You can then access the Web application using a URL of the form:
http://localhost:8080/MyApp

IE 7 is out

Windows Internet Explorer

Download: IE7 Final For Windows XP 32-Bit
http://download.microsoft.com/download/3/8/8/38889DC1-848C-4BF2-8335-86C573AD86D9/IE7-WindowsXP-x86-enu.exe

Download: IE7 Final For Windows Server 2003 32-Bit
http://download.microsoft.com/download/D/1/3/D1346F12-F3A0-4AC6-8F5C-2BEA2A184957/IE7-WindowsServer2003-x86-enu.exe

Download: IE7 Final for Windows XP/Server 2003 64-Bit
http://download.microsoft.com/download/1/1/4/114D5B07-4DBC-42F3-96FA-2097E207D0AF/IE7-WindowsServer2003-x64-enu.exe

WebBrowserControl for the .NET Framework 1.1

Finally, I published “my” WebBrowserControl for the .NET Framework 1.1.

I wanted to do a nice article on the buts, hows and whys, but never got the time for it. And since .NET 3.0 is knocking on our door, pretty soon no one will care about .NET 1.1 stuff.

I’ve already done some work to port it to .NET 2.0. My wish was to enhance the WebBrowser control supplied, but they made it near impossible to extend.

Once more, many thanks to Igor Tandetnik, João Melo, Luís Abreu, Oleg Mihailik and many more.

Using Remoting with CAB

Anyone who has ttried to add a service implemented through Remoting to the WorkItem‘s services collection was sadly surprised that it can’t be done.

But why?

When we use the following code:

WorkItem.Services.Add<IServiceContract>(serviceInstance);

It comes to something like this:

if (!typeof(IServiceContract).IsAssignableFrom(serviceInstance.GetType())
	throw new ArgumentException();

And how do we solve this problem?


easy: generics.


All we need to do is make some changes to the ServiceCollection class.


We change the Add<TService>(TService serviceInstance) method to look like this:

public void Add<TService>(TService serviceInstance) 
{ 
	Guard.ArgumentNotNull(serviceInstance, "serviceInstance"); 

	Build<TService>(serviceInstance); 
} 

At this point we need to refactor the Build(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) method to have two entry points to the common its logic:

private object Build(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) 
{ 
	Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToBuild"); 

	return BuildImplementation(typeToBuild, typeToRegisterAs, serviceInstance); 
} 

private TService Build<TService>(TService serviceInstance) 
{ 
	return (TService)BuildImplementation(serviceInstance.GetType(), typeof(TService), serviceInstance); 
} 

private object BuildImplementation(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) 
{ 
	if (locator.Contains(new DependencyResolutionLocatorKey(typeToRegisterAs, null), SearchMode.Local)) 
		throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Properties.Resources.DuplicateService, typeToRegisterAs.FullName)); if (serviceInstance == null) serviceInstance = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, null); else if (!container.Contains(serviceInstance)) serviceInstance = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, serviceInstance); else BuildRepeatedItem(typeToRegisterAs, serviceInstance); return serviceInstance; }

Easy, isn’t it? And I suspect that that’s not the only improvement like this that we can get into CAB.

Oh! And why would we wanto to use Remoting? To use NMock2 in the unit tests, of course!


Updated: November 14th, 2006


Changed:

private TService Build<TService>(object serviceInstance)
{
	Guard.TypeIsCompatibleType<TService>(serviceInstance, "typeToBuild");
return (TService)BuildImplementation(serviceInstance.GetType(), typeof(TService), serviceInstance); }

Removed:


And add the TypeIsCompatibleType<T>(object providedInstance, string argumentName) method to the Guard class:

public static void TypeIsCompatibleType<T>(object providedInstance, string argumentName) 
{ 
	if (!(providedInstance is T)) 
		throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Properties.Resources.TypeNotCompatible, typeof(T), providedInstance.GetType()),
argumentName); }

Fighting with CAB visualizers and visualizations

Anyone who has previously developed with CAB knows that documentation and information are scarce, but when one faces something like I recently faced, everything becomes even more difficult.


When I tried to use a visualizer with my application, I was faced with something very strange (to say the least).


(For those who don’t know it, a visualizer is a CAB component that collects information about the WorkItems in the application and makes that information available to visualizations that will display it.)


Finding out that I needed a visualizer element in my configuration file it wasn’t easy. But nothing prepared me to the fact that this element is just a collection of visualizations.


The configuration section looks something like this:


<visualizer>
	<add type=visualization type/><add type=visualization type/>
</visualizer>

Would it be more logical to have something like this?

<visualizer type=visualizer type>
	<visualizations>
		<add type=visualization type/><add type=visualization type/>
	<visualizations>
</visualizer>

We could even configure the type of the visualizer, which we can’t with the current architecture.


And this pearl came out of PAG.