First off I need to state that learning to use ExtJS has been extremely difficult for me. The roadblocks are:

  • I’ve made a career out of saying, “I don’t do no stinking UI”
  • I don’t know HTML or CSS very well
  • The tutorials, articles and APIs aren’t the best.
  • I have no Intellisense for ExtJS

Some of these problems are my own but some of them are ExtJS too. I’ve decided to fix the problems that are mine by learning more about HTML and CSS. The problems with ExtJS resources are they are mainly of the introductory type. That is fine if all you want to do is use their ‘templates’. Of course I want to modify and change from the ‘templates’. This has taken me a lot of time with the ‘experiment and run’ iterations. The Intellisense issue is being addressed but the project is being attached to VS 2008 so if you’re using VS 2005 you’ll be out of luck.

Here is what I’ve now got:

Wizard1

This is similar to what I had using pure CSS/HTML but now it is being rendered using ExtJS. Here is the HTML:

   1: <html>
   2: <head>
   3:     <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
   4:     <link rel=”stylesheet” type=”text/css” href=”../../Content/extJS/resources/css/ext-all.css” />
   5:     <link rel=”stylesheet” type=”text/css” href=”../../Content/extJS/resources/css/genealogist.css” />
   6:     <script type=”text/javascript” src=”../../Content/extJS/adapter/ext/ext-base.js”></script>
   1:  
   2:     <script type=“text/javascript” src=“../../Content/extJS/ext-all-debug.js”>
   1: </script>
   2:     <script type=“text/javascript” src=“../../Content/js/default.js”>

</script>

   7:     
   8:     <title>CrawBuck Genealogist</title>
   9: </head>
  10: <body>
  11:     <div id=”west”></div>
  12:     <div id=”north”>Genealogist</div>
  13:     <div id=”center2″></div>
  14:     <div id=”center1″></div>
  15:     <div id=”south”>Copyright 2008 CrawBuck</div>
  16: </body>
  17: </html>

Here is the default.js file:

   1: Ext.BLANK_IMAGE_URL = ‘../../Content/images/s.gif’;
   2: Ext.onReady(function() {
   3:   
   4:     var viewPort = new Ext.Viewport({
   5:         layout:‘border’,
   6:         items:[
   7:                 new Ext.BoxComponent({ // raw
   8:                     region:‘north’,
   9:                     el: ‘north’,
  10:                     height:100,
  11:                     style:‘background-image:url(../Content/images/BannerBackground.png);background-repeat:repeat-x;border:solid 1px #1D6241;text-align:right;vertical-align:middle;padding-right:10px;font-family:Georgia;font-size:75px;color:#ED7014;’
  12:                 }), 
  13:                 new Ext.BoxComponent({ 
  14:                     region:’south’,
  15:                     el: ’south’,
  16:                     height:25,
  17:                     style:‘background-image:url(../Content/images/FooterBackground.png);background-repeat:repeat-x;border:solid 1px #1D6241;text-align:center;font-family:Verdana;font-weight:bold;color:#ED7014;padding-top:5px;’
  18:                  }),{
  19:                     region:‘west’,
  20:                     id:‘west-panel’,
  21:                     title:‘West’,
  22:                     split:true,
  23:                     width: 200,
  24:                     minSize: 175,
  25:                     maxSize: 400,
  26:                     collapsible: true,
  27:                     margins:‘0 0 0 5′,
  28:                     layout:‘accordion’,
  29:                     layoutConfig:{
  30:                         animate:true
  31:                     },
  32:                     items: [{
  33:                         contentEl: ‘west’,
  34:                         title:‘Navigation’,
  35:                         border:false,
  36:                         iconCls:‘nav’
  37:                     },{
  38:                         title:‘Settings’,
  39:                         html:‘<p>Some settings in here.</p>’,
  40:                         border:false,
  41:                         iconCls:’settings’
  42:                     }]
  43:                 },
  44:                 new Ext.TabPanel({
  45:                     region:‘center’,
  46:                     deferredRender:false,
  47:                     activeTab:0,
  48:                     items:[{
  49:                         contentEl:‘center1′,
  50:                         title: ‘Close Me’,
  51:                         closable:true,
  52:                         autoScroll:true
  53:                     },{
  54:                         contentEl:‘center2′,
  55:                         title: ‘Center Panel’,
  56:                         autoScroll:true
  57:                     }]
  58:             }) // end of TabPanel
  59:         ] // end of items
  60:     });
  61:   
  62: }); // eo function onReady
  63:   
  64: // eof

This is modified from the Complex BorderLayout template. This template provides north, south, center, east and west regions. If you look at the HTML you will notice I have removed the east region. For the north region I have added the banner rendering and in the south region I have added the footer rendering. The west region will be an accordian panel and the center region will be a tab panel. The west and center will have to interact with one another.

I also want to create components that can be used to render the accordian and the center regions. ExtJS allows this but I’m again stuck learning this through ‘experiment and run’ iterations.

I will explain one of the problems I had in getting this to render. This is what I started with for the south region. The problem was that the height wasn’t taking and the south region was rendering much more than 25 pixels.

   1: }),{ 
   2:     region:’south’,
   3:     el: ’south’,
   4:     height:25,
   5:     style:‘background-image:url(../Content/images/FooterBackground.png);background-repeat:repeat-x;border:solid 1px #1D6241;text-align:center;font-family:Verdana;font-weight:bold;color:#ED7014;padding-top:5px;’
   6: },{
   7:     region:‘west’,

So I tried to put the footer inside a BoxComponent like this:

   1: }),{ new Ext.BoxComponent({ 
   2:     region:’south’,
   3:     el: ’south’,
   4:     height:25,
   5:     style:‘background-image:url(../Content/images/FooterBackground.png);background-repeat:repeat-x;border:solid 1px #1D6241;text-align:center;font-family:Verdana;font-weight:bold;color:#ED7014;padding-top:5px;’
   6: }),{
   7:     region:‘west’,

This won’t render and I didn’t know why. This is where Intellisense would have really helped. It turned out that I shouldn’t have the ‘}’ in the }),{ new Ext.BoxComponent. The correct syntax is this:

   1: }),
   2:     new Ext.BoxComponent({ 
   3:     region:’south’,
   4:     el: ’south’,
   5:     height:25,
   6:     syle:‘background-image:url(../Content/images/FooterBackground.png);background-repeat:repeat-x;border:solid 1px #1D6241;text-align:center;font-family:Verdana;font-weight:bold;color:#ED7014;padding-top:5px;’
   7: }),{
   8:      region:‘west’,

That only took me about 3 hours to figure out.

I thought I would literally take a snapshot of my bookshelves. These first two are within two feet of my left hand. I would consider these the ones I look at the most.

HPIM3706 HPIM3707

These next two shelves are the ones that I used maybe 5 - 10 years ago but there still located in my home office.

HPIM3708 HPIM3709

This last one is in the ‘little house’ or mother-in-law house. These are at least 10+ years ago.

HPIM3704

I started down this path to create a genealogy project inspired by Ben Lovell’s ‘Incremental development with Monorail’ postings. So I’m now completing my first page based on his Part One.

I’m deviating somewhat from this first part in that I’m dividing my unit tests into two types of tests: unit and integration. In my Genealogist.Tests I have Controllers directory where I place the PersonController tests. The Integration directory is where I place the PersonControllerIntegration tests.

Wizard3

PersonControllerTest contains the first test, ShouldRenderAddPersonForm. This extends the BaseControllerTest class from the Castle.MonoRail.TestSupport assembly. I had trouble getting this to work and it turned out that I have an old version of the Castle.MonoRail.TestSupport assembly down in the GAC. Once I removed it then the test worked.

   1: using Castle.MonoRail.TestSupport;
   2: using Genealogist.Controllers.Persons;
   3: using NUnit.Framework;
   4:  
   5: namespace MRProjectTest.Controllers {
   6:     [TestFixture]
   7:     public class PersonControllerTest : BaseControllerTest {
   8:         private PersonsController personController;
   9:  
  10:         [SetUp]
  11:         public void SetUp() {
  12:             personController = new PersonsController();
  13:             PrepareController(personController,@”Persons”, “addPerson”);
  14:         }
  15:  
  16:         [Test]
  17:         public void ShouldRenderAddPersonForm() {
  18:             personController.Add();
  19:             Assert.IsNotNull(personController);
  20:             Assert.AreEqual(personController.SelectedViewName, @”Personsadd”, @”Expected view: PersonAdd wasn’t rendered”);
  21:         }
  22:  
  23:         [TearDown]
  24:         public void Teardown() {}
  25:     }
  26: }

PersonControllerIntegrationTest contains the first test, ShouldRenderAddPersonForm. This is the first time I have used WatiN. All it took was to add two references to my project: WatiN.Core and Interop.SHDocVw. I had a small issue here getting the test to work because I used http://localhost:16489/Persons/add.brail. Once I changed it to http://localhost:16489/Persons/add.castle the test worked.

   1: using NUnit.Framework;
   2: using Rhino.Mocks;
   3: using WatiN.Core;
   4:  
   5: namespace MRProjectTest.Integration {
   6:     [TestFixture]
   7:     public class PersonControllerIntegrationTests {
   8:         private MockRepository mock;
   9:  
  10:         [SetUp]
  11:         public void SetUp() {
  12:             mock = new MockRepository();
  13:         }
  14:  
  15:         [Test]
  16:         public void ShouldRenderAddForm() {
  17:             using (IE browser = new IE(“http://localhost:16489/Persons/add.castle”)) {
  18:                 Assert.IsTrue(browser.ContainsText(“Add Person”));
  19:             }
  20:         }
  21:  
  22:         [TearDown]
  23:         public void TearDown() {
  24:             mock.VerifyAll();
  25:         }
  26:     }
  27: }

Using TDD both of these test failed but with some small amount of code both of these succeed. The PersonsController code is:

   1: using Castle.MonoRail.Framework;
   2:  
   3: namespace Genealogist.Controllers.Persons {
   4:     public class PersonsController : Controller {
   5:         [Layout(“default”)]
   6:         public void Add() {
   7:             RenderView(“add”);
   8:         }
   9:     }
  10: }

The add.brail code is this:

   1: Add Person

Be sure to add the Person controller to the controllers.config file. I forgot to and it took me a couple of minutes to figure out why it wasn’t working.

   1: <?xml version=“1.0″ encoding=“utf-8″?>
   2: <configuration>
   3:     <components>
   4:         <component id=“home.controller” type=“Genealogist.Controllers.HomeController, Genealogist” />
   5:     <component id=“persons.controller” type=“Genealogist.Controllers.Persons.PersonsController, Genealogist” />
   6:     </components>
   7: </configuration>

When it is all said and done I got this:

Wizard2

While this doesn’t seem like much it is the beginnings of something that has taken me hours to understand. All of the Castle stack is in place and I’m using ExtJS to help with the rendering of the page using the Brail ViewEngine. This is good.

I was attempting to write my first controller test using Castle.MonoRail.TestSupport class but was getting a System.TypeLoadException when I ran the unit test. So with some wasted hours trying to figure out what I was doing wrong I finally stumbled across a Castle forum topic that said the problem was caused by a “conflict of the trunk TestSupport’s assembly with the one in the GAC”. Removing the Castle.MonoRail.TestSupport dll from the GAC fixed it more me.

Last week I had visitors from out of town so I didn’t get much time to work on this when I came home. I now have a final layout that I will work from.

Wizard1

The default.brail file is this:

   1: <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
   2: <html xmlns=“http://www.w3.org/1999/xhtml” >
   3: <head>
   4:     <title>Master Layout</title>
   5:     
   6:     <link rel=“stylesheet” type=“text/css” href=“../../Content/extJS/resources/css/ext-all.css” />
   7:     <link rel=“stylesheet” type=“text/css” href=“../../Content/extJS/resources/css/genealogist.css” />
   8:     <!– GC –>
   9:     <!– LIBS –>
  10:     <script type=“text/javascript” src=“../../Content/extJS/adapter/ext/ext-base.js”></script>
  11:     <!– ENDLIBS –>
  12:     <script type=“text/javascript” src=“../../Content/extJS/ext-all.js”></script>
  13:     <script type=“text/javascript” src=“../../Content/js/default.js”></script>
  14:        
  15: </head>
  16: <body>
  17: <div id=“Banner”>Genealogist</div>
  18: <div id=“MainContent”>
  19:     <table id=“MainContentTable”>
  20:         <tr>
  21:             <td id=“FunctionPanel”></td>
  22:             <td id=“DisplayContent”>${ChildOutput}</td>
  23:         </tr>
  24:     </table>
  25: </div>
  26: <div id=“Footer”>CrawBuck Copyright 2008</div>
  27: </body>
  28: </html>

The genealogist.css is this:

   1: body
   2: {
   3: }
   4: #Banner
   5: {
   6:     background-image:url(../images/BannerBackground.png);
   7:     background-repeat:repeat-x;
   8:     border:solid 1px #1D6241;
   9:     text-align:right;
  10:     vertical-align:middle;
  11:     padding-right:10px;
  12:     font-family:Georgia;
  13:     font-size:75px;
  14:     color:#ED7014;
  15: }
  16: #MainContent
  17: {
  18:     height:654px;
  19: }
  20: #Footer
  21: {
  22:     background-image:url(../images/FooterBackground.png);
  23:     background-repeat:repeat-x;
  24:     border:solid 1px #1D6241;
  25:     text-align:center;
  26:     font-family:Verdana;
  27:     font-weight:bold;
  28:     color:#ED7014;
  29:     padding-top:5px;
  30: }
  31: #MainContentTable
  32: {
  33:     width:100%;
  34:     height:100%;
  35: }
  36: #FunctionPanel
  37: {
  38:     width:20%;
  39:     border:solid 1px Black;
  40: }
  41: #DisplayContent
  42: {
  43:     width:80%;
  44:     border:solid 1px Black;
  45: }

Nothing real fancy but it will allow me to start building on the Castle stack using ExtJS.

I’ve worked on the CSS for the Genealogist and have added it to the Banner portion of the page. I added a new Genealogist.css file:

   1: #Banner
   2: {
   3:     background-image:url(../images/BannerBackground.png);
   4:     background-repeat:repeat-x;
   5:     border:solid 1px #1D6241;
   6:     text-align:right;
   7:     vertical-align:middle;
   8:     padding-right:10px;
   9:     font-family:Georgia;
  10:     font-size:75px;
  11:     color:#ED7014;
  12: }

and now it looks like this:

Wizard1

The first attempt at bringing together Brail and ExtJS was admittedly a little more than cut-and-paste. Now I want to create a simple page using Brail, ExtJS and the HomeController. One of the goals is to have a way to get most of the ExtJS scripting out of the Brail page and into a .js file. http://localhost:1999/home/BlowItAway.castle is the URL to get this to render in VS 2005.

Wizard1

I admit this doesn’t look like much but it is the beginning foundation that can be built upon to create what I hope will be a rich internet application. What I have done here is create a default.brail page that is analogous to a Master Page in ASP.NET. The default.brail page leverages a default.js file to reduce that amount of javascript that would normally be found between the <head></head> tags. I think this makes the default.brail page easier to understand.

   1: <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
   2: <html xmlns=“http://www.w3.org/1999/xhtml” >
   3: <head>
   4:     <title>Master Layout</title>
   5:     
   6:     <link rel=“stylesheet” type=“text/css” href=“../../Content/extJS/resources/css/ext-all.css” />
   7:     <!– GC –>
   8:     <!– LIBS –>
   9:     <script type=“text/javascript” src=“../../Content/extJS/adapter/ext/ext-base.js”></script>
  10:     <!– ENDLIBS –>
  11:     <script type=“text/javascript” src=“../../Content/extJS/ext-all.js”></script>
  12:     <script type=“text/javascript” src=“../../Content/js/default.js”></script>
  13:        
  14: </head>
  15: <body>
  16: <div id=“Banner”>Banner</div>
  17: <div id=“MainContent”>${ChildOutput}</div>
  18: <div id=“Footer”>Footer</div>
  19: </body>
  20: </html>

As you can see I have the <link> tag for the stylesheet location that I’m not really using at this time. There is also the ext-base.js and the ext-all.js script locations. Default.js allows me to move much of the code that is needed to support ExtJS into its own file.

   1: Ext.onReady(function(){
   2:     var banner = Ext.get(‘Banner’);
   3:     banner.setHeight(100);
   4:     banner.boxWrap();
   5:     var mainContent = Ext.get(‘MainContent’);
   6:     mainContent.setHeight(500);
   7:     mainContent.boxWrap();
   8:     var footer = Ext.get(‘Footer’);
   9:     footer.setHeight(25);
  10:     footer.boxWrap();
  11: }); 

Granted that this doesn’t do much now but the goal was to get it out off default.brail page and that goal has been met. I’m hoping to use this technique to build some generic RIA that will be reusable.

The HomeController is very simple right now too.

   1: using Castle.MonoRail.Framework;
   2:  
   3: namespace Genealogist.Controllers {
   4:     public class HomeController : SmartDispatcherController {
   5:         public void Index() {}
   6:  
   7:         [Layout(“default”)]
   8:         public void BlowItAway() {}
   9:     }
  10: }

BlowItAway.brail looks like this:

   1: Here it is

On the one hand I can’t believe it has taken me so long to get this far. I’m not sure if this is a reflection of my lack to intellect or the lack of good documentation. It’s probably more likely to be some combination of both. Being new to the Castle project (MonoRail, ActiveRecord, Windsor Container, Brail) and ExtJS definitely slows me down. I’m not new to MVC but so far their is very little MVC here.

There was a thread on Alt.Net the other day about the importance of knowing the fundamentals. This got me to thinking about my own fundamentals. The Agile Software Development Principles, Patterns and Practices book has a section on Agile design. Casey Charlton has a blog about ‘What Determines High Quality Code?’. In both of these there is mention of the Single Responsibility Principle, SRP.

I’ve come across references to this principle before. What this boils down to is that any object should have only one responsibility. If you violate this principle and have more than one responsibility then this causes coupling. If change is necessary this coupling makes it difficult to make the change without causing side effects. Side effects make object rigid and this can cause unexpected results.

If you don’t have SRP in your object then changes may require rebuilding, retesting and redeployment in areas of the application that would not have to change except for the coupling.

This is one of the fundamental principles that any good developer should understand and sometimes one of the hardest principles to get right. Robert Martin in his book does note a corollary, “An axis of change is an axis of change only if the changes actually occur”. Let it be understood that in some situations it may be acceptable to have a coupling of responsibilities if the object will not be changed.

There isn’t a lot of good documentation on how to pair up Brail View Engine with ExtJS. Through experimenting I have gotten this to show up in the Firefox browser:

Wizard1

This is done by redirecting from the Default.aspx page to ~/home/index.castle in the OnLoad event.

The code for the Default.aspx page is:

 

 

   1: <%@ Page Language=“C#” %>
   2: <script runat=“server”>
   3:   protected override void OnLoad(EventArgs e)
   4:   {
   5:     Response.Redirect(“~/home/index.castle”);
   6:     base.OnLoad(e);
   7:   }
   8: </script>

The code for the index.brail page is basically a cut-and-paste for the ExtJS ext-2.1/examples/layout/complex.html. I did have to do some modification to make it work with my directory structure. My directory structure for the ExtJS files looks like this:

Wizard1

The /adapter directory contains the ext-base.js. I have the cascade style sheets and the images in the resource directory. I had to add the examples.js file to the Content/js directory.

I ended up making modification to all of the ’src=’ references in the script tags to point to these locations.

 

 

   1: <link rel=“stylesheet” type=“text/css” href=“../../Content/extJS/resources/css/ext-all.css” />
   2: <!– GC –>
   3: <!– LIBS –>
   4: <script type=“text/javascript” src=“../../Content/extJS/adapter/ext/ext-base.js”></script>
   5: <!– ENDLIBS –>
   6:
   7: <script type=“text/javascript” src=“../../Content/extJS/ext-all.js”></script>

The HomeController code just had an empty Index() method. It took me about 1 hour to figure this out.

There are a lot of little pieces to use ExtJS. I still need to understand how it all fits together. I’ll work on that next.

Last night I left off with the creation of a Castle MonoRail template project and got it to display the ’sample demo’. Tonight I’m going to add ExtJS components to the project and remove the ’sample demo’ components from the project.

I blogged about ExtJS earlier with a link but what you also need to know is the licensing agreement. It comes in three flavors: Commercial, Open Source and OEM/Reseller. I’m not planning on commercializing the Genealogist project so I can go with the Open Source (GNU GPL license v3). At my day job we are seriously considering going with this and if our lawyers bless it we will be using the Commercial license.

This is what the Solution Explorer looks like after creating it using the Castle MonoRail Wizard3 project template.

I kept the files in the config directory but I opened the controllers.config file and deleted the login.controller and contact.controller components from the file. I kept the home.controller component.

In the Controllers directory I deleted the ContactController and the LoginController. I also deleted both files, ContactInfo.cs and Country.cs from the Models directory.

The Views directory had a Contact and Login directories that I deleted but I kept the Home, layouts and rescues directories.

In the layouts directory I replaced the existing code in the default.brail file with ExtJS code from the Writing a Big Application in ExtJS tutorial.

   1: <html>
   2: <head>
   3:   <meta http-equiv=“Content-Type” content=“text/html; charset=utf-8″>
   4:   <link rel=“stylesheet” type=“text/css” href=“../../Content/ext/resources/css/ext-all.css”>
   5:   <link rel=“stylesheet” type=“text/css” href=“../../Content/css/application.css”>
   6:   <script type=“text/javascript” src=“../../Content/extJS/adapter/ext/ext-base.js”></script>
   7:   <script type=“text/javascript” src=“../../Content/extJS/ext-all-debug.js”></script>
   8:   <script type=“text/javascript” src=“../../Content/js/application.js”></script>
   9:   <title>A Big Application</title>
  10: </head>
  11: <body>
  12:     <div>Banner</div>
  13:     <div>MainContent</div>
  14:     <div>Footer</div>
  15: </body>
  16: </html>

The Default.aspx page has no code behind and only contains a script section that overrides the OnLoad event of the page lifecycle.

   1: <%@ Page Language=“C#” %>
   2: <script runat=“server”>
   3:   protected override void OnLoad(EventArgs e)
   4:   {
   5:     Response.Redirect(“~/home/index.castle”);
   6:     base.OnLoad(e);
   7:   }
   8: </script>

Even though this redirects to the ~/home/index.castle page the ~/layout/default.brail page gets run and there is no content holder, ${ChildOutput}, to render the ~/home/index.brail page in. So what is rendered is this:

Wizard2

If you’re paying attention you will notice that the redirect is to ~/home/index.castle and there is no ~/home/index.castle file. This confused me at first until I realized that in the web.config file this section mapped the .castle extension to the .brail extension.

   1: <httpHandlers>
   2:     <add verb=“*” path=“*.castle” type=“Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework” />
   3:     <!– block direct user access to template files –>
   4:     <add verb=“*” path=“*.vm” type=“System.Web.HttpForbiddenHandler” />
   5:     <add verb=“*” path=“*.njs” type=“System.Web.HttpForbiddenHandler” />
   6:     <add verb=“*” path=“*.brail” type=“System.Web.HttpForbiddenHandler” />
   7:     <add verb=“*” path=“*.brailjs” type=“System.Web.HttpForbiddenHandler” />
   8:     <add verb=“*” path=“*.st” type=“System.Web.HttpForbiddenHandler” />
   9: </httpHandlers>

You can replace the path=”*.castle” with whatever you want as the extension and it will map the extensions: .vm, .nis, .brail, .brailjs and .st to it.

For a long time in my software career I made the statement, “I don’t do no stinkin’ UI”. The UI I just rendered above shows how little I know about UI but at this point I’m going to start working with .css and images and other UI components. This is just the start.

« Previous PageNext Page »