Introduction
We all know the power of Microsoft Management Console (MMC). MMC is Microsoft�s standard platform for administrative and system management tools. MMC lets you use snap-ins to manage every part of Windows and even other applications from the same standard interface. The purpose of this article is to provide a MMC like interface over the web (I call it MMC Web). MMC Web represents a common view point to a variety of administrative tasks, which will make life more easy. MMC Web is made on an easy attachable framework that will let users to add / remove modules as well as reuse them over time with less hassle.
Requirements
The code has been written using Microsoft Web TreeView
control, therefore the scripts to be run to render the tree should be in their proper place. The scripts are provided in the download, under the tools folder. To make sure that the tree view works properly, all you need to do is to copy the webctrl_client folder to your IIS wwwroot directory.
Using the code
Now, assume that we want to represent a simple organizational hierarchy. To dig a little deeper, assume that there is a company, under it there are some departments, and every department is composed of some sections. In a more technical fashion, there are three entities Company, Department and Section, where departments are children of the company and sections are children of a department. Now, to achieve this form of structure in a tree, the simplest steps are:
- Implementing the
IProvider
interface: create object specific provider classes (e.g. CompanyProvider
). These classes will be responsible for fetching data from the data source for a specific ID. The ID will be provided internally by the framework according to the hierarchy defined in the XML schema.
- Implementing the
IRenderer
interface: create object specific renderer classes (e.g. CompanyRenderer
). These classes will provide the rendering option for the tree, like how the tree will be rendered.
- Construct an XML that will define the relationships among the objects and their behaviour type.
The XML format is as shown below:
<company assembly="TreeView.Objects"
provider="TreeView.Objects.Provider.CompanyProvider"
renderer="TreeView.Objects.Renderer.CompanyRenderer"
expanded = "true" >
<department assembly="TreeView.Objects"
provider="TreeView.Objects.Provider.DepartmentProvider"
renderer="TreeView.Objects.Renderer.DepartmentRenderer"
expanded = "true" >
<section assembly="TreeView.Objects"
provider="TreeView.Objects.Provider.SectionProvider"
renderer="TreeView.Objects.Renderer.SectionRenderer"
expanded = "false"/>
</department>
</company >
Each XML node contains a few attributes which are:
assembly
- defines where the renderers, providers and objects could be found.
provider
- defines the object type of the provider class.
renderer
- defines the object type of the renderer class.
expanded
- defines the status of the tree node.
All these attributes are used to build the object model dynamically from the XML.
- Finally, we need to drag the
ExtendedTreeView
control (the wonder control) to a web page and call the LoadXml
function with the proper server path at page load.
Once the loading is complete we then need the proper entity ID at each selection change, using which we will load the entity information at the right side of the tree. During construction, each tree node ID is internally constructed by an '_' separated ID and an object name format by the treeview framework, which does solve our problem. At selection, we just need to manage to get the proper tree node object from the '.' separated string node ID.
private void treeView_SelectedIndexChange(object sender,
Microsoft.Web.UI.WebControls.TreeViewSelectEventArgs e)
{
_NodeID = e.NewNode;
LoadPage( _NodeID );
}
Now, to show the entity detail, we can create controls for entity names (example: company.ascx). To make the loading of different entities possible in the most uniform way, each control needs to extend a BaseControl
class and implement an IHost
interface. The interface will make the control pluggable to the treeview framework, as on each selection change from the first part of the node ID, we load the control and load the entity information using the second part of the node string with the help of the IHost
interface.
private void LoadPage( string nodeID )
{
BaseControl control = null;
TreeNode node = treeView.GetNodeFromIndex( nodeID );
string[] nodeParts = node.ID.Split( '_' );
_PropertyID = Int32.Parse( nodeParts[ 1 ] );
_ControlID = nodeParts[ 0 ];
control = ( BaseControl ) pnlRenderer.FindControl( _ControlID );
if( control == null )
{
string controlName = "Controls/" + nodeParts [ 0 ] + ".ascx";
control = ( BaseControl ) LoadControl( controlName );
control.ID = _ControlID;
if ( control != null )
{
pnlRenderer.Controls.Clear();
pnlRenderer.Controls.Add( control );
control.PreRender +=new EventHandler(control_PreRender);
control.OnChange+=new OnChangeEvent(control_OnChange);
}
}
IHost loader = ( IHost ) control;
if ( nodeID != String.Empty )
{
loader.LoadTab( _PropertyID );
}
}
About the framework
As far as we have seen, the treeview uses a simple XML file to build its object model. Now, let's look in a little deeper and see how the job is really done. The treeview framework uses a control named extendedTreeView
that has been built on the Microsoft Web TreeView
control. It extends all the functionalities of the existing TreeView
control. In addition it gives a function that is the heart of building a tree in the most friendly fashion.
public void LoadTree( string ServerPath );
As you can see, the function expects a server path for the XML file that contains the definition of the tree structure. Once the path is provided, it loads an object model recursively from the XML.
Part of the source follows:
private Defination FillObject( XmlNode rootNode )
{
Assembly ass = Assembly.Load( rootNode.Attributes["assembly"].Value );
Defination def = new Defination();
object providerObject =
Activator.CreateInstance( ass.GetType(
rootNode.Attributes["provider"].Value, true ) );
def.Provider = ( Interface.IProvider ) providerObject;
object rendererObject =
Activator.CreateInstance( ass.GetType(
rootNode.Attributes["renderer"].Value, true ) );
def.Renderer = ( Interface.IRenderer ) rendererObject;
def.IsExpanded = bool.Parse( rootNode.Attributes["expanded"].Value );
return def;
}
After it is done constructing the object from the XML file, it then uses the model to build up a tree structure. The process is to start constructing the tree from the root node and then go down from the highest level until the leaf is encountered.
public void LoadTree( string serverPath )
{
Schema schema = new Schema( serverPath );
Defination defination = schema.RootNode;
ExecuteDef( defination, 0 , null );
}
private void ExecuteDef( Defination defination,
int parentID, TreeNode parentNode )
{
IProvider provider = defination.Provider;
IList list = provider.GetAll( parentID );
LoadList( list, defination, parentID, parentNode );
}
private void LoadList( IList list, Defination def,
int parentNodeID, TreeNode parentNode )
{
foreach( object obj in list )
{
IRenderer renderer = def.Renderer;
renderer.Set( obj );
TreeNode node = ConstructNode( renderer, parentNodeID );
PopulateNode( node, parentNodeID, parentNode );
if( def.IsExpanded )
{
LoadNode( renderer, def, node );
}
}
}
Finally
MMC Web will help users to reduce the number of lines of code by reusing the most common set of modules (Ex. password change, add new registration, and many more) which are already written by someone else. Thus it will help ISVs grow better.