Friday, September 10, 2004

In preparing my custom server control (webEdit and webMenu) for general distribution, I've taken the time to make the package more of a complete offering.  That is, I would not generally be comfortable simply releasing my code without any explanation of how to use it.  As a direct result of this inexplicable compulsiveness, I've enhanced the package to support design time intellisense.  I am one of  the (few/many?) developers that actually prefers to do all of my design work in the raw .aspx/.ascx environment, rarely if ever relying on the visual designers.  In fact, one of the first things I do on a fresh VS.NET install is turn off the setting to default to design view for HTML files.

In this article, I'd like to venture into the world of creating our own custom Intellisense for your web server controls that is supported in the IDE.  Note: from what I understand, this will not be necessary in the Whidbey timeframe (yipee!), but it will suffice for the time being.

I'll start at the beginning, assuming no knowledge of how to add support for Intellisense.

VS developers have been accustomed (spoiled?) by the prevalence of the code-completion support built into the IDE.  Open any code document and begin typing and you'll quickly see the help appear (e.g. <asp:…).  This assistance appears based on context and can display classes, properties, values, methods, events, and much more.  The Intellisense that appears within the IDE for .cs or .vb files is driven off of reflection.  You don't have to do any work to get it to appear other than change your code.  The Intellisence within the HTML designer, on the otherhand, is driven by metadata.  This metadata file is found in C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml\asp.xsd in a default installation.

If we want to add our own Intellisense, we have two options 1) edit asp.xsd or 2) create our own.  Needless to say, you SHOULD NOT edit asp.xsd.  No, the programming police will not come knock down your door and drag you off (though perhaps they should), but if in a patch or update the file ever gets replaced you'll lose your changes.

Instead we should create our own file and plop it in the same directory.  I start to cringe at this thought, because I don't want to have to maintain two files (one in my \dev folder and one in the VS folder).  I am loathe to have my only file in the VS folder.  If you're developing your software on Windows XP or higher on an NTFS partition (which you should be doing if you're not), here's a cool tip.  If you only want to maintain a single source file on your system (e.g. the one in your \dev folder), and still have it referenced elsewhere without creating a copy (which is easy to forget to do):


fsutil hardlink create "C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml\devstone.xsd" "C:\Dev\......\devstone.xsd"


The FSUTIL tool (File System Utility) allows us to create a hard link so that the file appears in another directory.  ACLs are shared and changes made in one file are reflected in the other because, well, it's the same file.

So to facilitate my development, I created my devstone.xsd file in my \Dev folder and a batch file called 'Register Intellisense.bat' that performs the FSUTIL operation (I only need to run it once, but if I ever get my source to another computer, it'll be there for me to run and I'll be set).

Now to creating our devstone.xsd file:

Begin by creating a new .xsd file and place the following Xml elements therein, changing the namespace to something appropriate for your situation:

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace='urn:http://www.devstone.com/schemas'
   elementFormDefault='qualified'
   xmlns='urn:http://www.devstone.com/schemas'
   xmlns:xsd='http://www.w3.org/2001/XMLSchema'
   xmlns:vs='http://schemas.microsoft.com/Visual-Studio-Intellisense'
   vs:friendlyname='Devstone Web Forms Controls'
   vs:ishtmlschema='false'
   vs:iscasesensitive='false'
   vs:requireattributequotes='false'>

<xsd:annotation>
   <xsd:documentation>
      Devstone ASP.NET Web Forms Controls schema.
   </xsd:documentation>
</xsd:annotation>

</xsd:schema>

In order to create your Intellisense then, you create <xsd:element /> entries that define your valid 'top-level' objects.  Each <xsd:element /> is tagged with a type attribute that identifies an <xsd:complexType /> defined elsewhere in the document:

<xsd:element name="WebEdit" type="WebEditDef" />

<xsd:complexType name="WebEditDef" vs:noambientcontentmodel="true">
   <xsd:attribute name="Runat" type="runat" />
   <xsd:attribute name="Enabled" type="xsd:boolean" />
   <xsd:attribute name="Height" type="ui4" />
   <xsd:attribute name="ID" type="xsd:string" />
   <xsd:attribute name="Text" type="xsd:string" />
   <xsd:attribute name="Width" type="ui4" />
   <xsd:choice>
      <xsd:element name="Toolbar" form="unqualified" type="WebEditToolbarDef" />
   </xsd:choice>
</xsd:complexType>

There are a few things to notice here.  First of all, I've decorated the <xsd:complexType /> with a schema annotation vs:noambientcontentmodel.  That's a special attribute that VS.NET reads and understands that children of this type when entered on the .aspx/.ascx should be limited to the set of valid children as defined in the <xsd:choice />.  By default, you'd get a comprehensive set of the children of the page as well.  In this case we want to see only the <toolbar> element.

Now to get this tied onto our WebForm, we add the @Register directive to the top of the page and then add a reference to the Xml namespace within an element that will (directly or indirectly) be parent to our WebControl.  The <body> tag is a good candidate for .aspx, but since .ascx pages (a.k.a Web UserControls, HTML Controls) don't have a <body> tag, you'll need to find another element, such as a <div>, <td>, or something like that.

<%@ Register tagPrefix="devstone" Namespace="Devstone.Web.WebControls" Assembly="Devstone.Web.WebControls.WebSuite" %>
<body xmlns:devstone="urn:http://www.devstone.com/schemas">

Notice that the Namespace matches identically to the targetNamespace of our schema file.  Also, the devstone namespace matches the tagPrefix within the @Register directive.  This ties our control's Intellisense to that element.  Now we would get autocompletion for our <devstone:webEdit /> control on the page.

There's a lot more to this than what I've posted here in this blog entry, but I've also included my .xsd file for download if you'd like to check it out.

Enjoy!

Friday, September 10, 2004 8:21:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, September 09, 2004

Well, here we are again.  It's the second Thursday of the month and it's time once again for our monthly .NET User Group meeting.  Last month went quite well.  I had the opportunity to speak on Design Patterns.  Specifically, we covered such patterns as Adapter, Facade, Singleton, and Observer.  I got lots of great feedback on the presentation and lots of demand for a second part of the talk.

Which brings me to the topic at hand.  Tonight it will again be my opportunity to present to our local Salt Lake City .NET User Group a Part 2 of the Design Patterns talk.  Tonight will focus specifically on the Proxy, Flyweight, Builder, and Factory Method patterns.  I hope it goes as well as my previous presentations which have historically been received very well.

Our local website contains details on the meeting place and time.  Come one and all...pizza and drinks are being provided!...oh yeah, and a cool presentation too.

Thursday, September 09, 2004 8:02:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, September 08, 2004
Don't forget to get the latest .NET Framework 1.1 Service Pack 1 and install it.  It contains lots of security updates as well as some performance enhancements.
Wednesday, September 08, 2004 4:29:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, September 07, 2004

Over the past while (mostly but not entirely in my spare time), I've been working on creating a web-based input control.  This control behaves like a standard INPUT or TEXTAREA control would except that it allows for custom formattings (e.g. bolding, italicizing, indending text, etc) and what not.  You may already be familiar with some controls out there that do pretty much the same thing (such as FreeTextBox, et al).  While these controls are all fine and dandy, and no doubt their authors put in lots of time to get them to work properly, I couldn't bring myself to use them for a variety of reasons: 1) I don't usually like to use 3rd party components and 2) they didn't offer all the functionality that I desired in the control.

I've pretty much completed the control; I've finished it enough at least for version 1.0.  I have a lot of additional bits of functionality that I strongly desire to get into the control in a version 2.0 timeframe, but for the time being, I'll have to let it suffice as is.  The UI is pretty much nailed down as demonstrated in these screen shots.  I might make some very very minor tweaks here before I actually release the code, but it's pretty much frozen.  In a future article I'll go into the technical aspects of the control and how it was written and how I accomplished the various aspects of its design and development.

Each and every aspect of this control is customizable: from the buttons, to their appearance (image, or image/text), the dropdown menus, the emoticons, everything.  And it's all done via markup in your .ASPX page so it makes it very nice to display and code.

Let me know what you think!







Tuesday, September 07, 2004 1:36:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Saturday, September 04, 2004
I've always been a Media Player user; never really getting into WinAmp and MusicMatch - though I've had my share of using them.  Just this morning I installed the latest version of Microsoft's Media Player 10 and I must say I'm very impressed.  I very much enjoy the new look and feel as well as several of the enhancements that were made.  The transition to version 10 from 9 was seamless.  Very cool stuff!
Saturday, September 04, 2004 4:51:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, September 02, 2004

For the longest time I've been writing software (be it small apps, utilities, components, controls, or even large ambitious application) pretty much by myself.  Sure, on a weekly basis I gather with some friends and we have a coding bash and accomplish some great things.  I've never known what to call these endeavors.  Today I made a discovery that impressed me.

While perusing some blogs I read, I found a link to an article by Eric Sink of SourceGear.  He posted an article on msdn.microsoft.com in which he discusses the concept of a Micro-ISV; that is, a small one-man company focused on delivering products.  I feel that I very much fall into this category of Micro-ISV-ness.  Very soon I'll have my products website up with some downloadable products and tools for people to use within their applications.

Eric offers several hypotheses about what it takes to be a successful Micro-ISV.  I wholeheartedly agree with his “Don't start too big” section.  It's far too easy when designing and coding a product to not stick with your initial goals and concepts and allow scope creep to enter the picture or simply to dream too big.  I've done this on more than one occasion...it's a hard habit to break.

Anyway, good article, Eric...I look forward to reading more articles on the 'business of software'.

Thursday, September 02, 2004 6:12:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, August 30, 2004

I'm in the process of writing a custom web control (System.Web.UI.WebControls.WebControl-based class).  This control renders client-side as a DIV tag that allows for rich text editing.  One of the biggest challenges had to do specifically with focus.  This would not have been such a problem had I simplified my UI, but I'm not one to sacrifice functionality for a crappier UI if at all possible as many of my friends would atest.

My control renders a custom toolbar (or set of toolbars).  Each of these toolbar buttons or menu items renders (presently) as a TABLE, though I'm in the process of figuring out how to update my control to use DIVs and SPANs as appropriate, but that's beside the point.  My toolbar buttons are 16x16 images contained within a TD that measures 23 pixels tall by an arbitrary width.  It so happens that if I clicked directly on the image in the button, my DIV would not lose selection and the appropriate action would take place on the entire range.  However as soon as I clicked off the image (but still clicked within the boundaries of the button), my selection would disappear on mouse down and when the click event fired the DIV would have lost its focus and the action would affect just the text at the beginning of the selection.

I spent hours attempting to figure out how to programmatically select the appropriate text within the DIV using TextRanges and what not, all to no avail.  And then it dawned on me that instead of trying to reselect the appropriate range of text, I should instead work on not allowing the selection to disappear in the event that the button was clicked (but not directly on the image).

It was approximately 23 seconds (maybe 24) after this thought occurred to me that I added the UNSELECTABLE='on' attribute to the TD that defined the button.  The UNSELECTABLE attribute is quite handy - when you click on an UNSELECTABLE element on the page the current selection is not destroyed.  It turns out this was an answer to my prayers :-)  Now I just have to finish the control as it's 95% done.

Monday, August 30, 2004 8:27:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Thursday, August 26, 2004

In an ongoing effort to keep Visual Basic .NET within the mainstream, Microsoft is making some additions to the language that will definitely keep it viable moving forward.  I'm excited with this new direction.  Read on!

Thursday, August 26, 2004 7:09:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback

For all those “Star Wars“-philes out there, those that are dying to have Star Wars be a reality (aside from the prequel debacles), look no further.  It appears that the Death Star has been found near Yavin...er, Saturn.

http://www.engadget.com/entry/1884557355723102/

Thursday, August 26, 2004 6:50:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback