Recently in ASP.NET Category

Mark Miller announced Refactor! for ASP.NET v2.2 - free code refactoring tool from Developer Express. It includes 29 refactorings, including 10 ASP.NET related ones and is available for Visual Studio 2005 and Visual Studio Orcas Beta1.

ASP.NET refactorings include:

  1. Add Validator
  2. Extract ContentPlaceHolder
  3. Extract ContentPlaceHolder (and create master page)
  4. Extract Style (Class)
  5. Extract Style (id)
  6. Extract to UserControl
  7. Move Style Attributes to CSS
  8. Move to Code-behind
  9. Rename Style
  10. Surround with Update Panel

This is awesome tool and it saves me LOTS of time (I'm using Refactor! Pro).

My favorites so far:

Those lucky like me having Refactor! Pro - don't install Refactor! for ASP.NET, instead update your Refactor! Pro installation.

In ASP.NET when you building a server control that includes an HTTP handler you have this problem - the HTTP handler has to be registered in Web.config. That means it's not enough that your customer developer drops control on her Web form and sets up its properties. One more step is required - manual editing of the config, which is usability horror.

How do you make your customer aware she needs to perform this additional action? Documentation? Yes, but who reads documentation on controls? I know I never, I usually just drop it on the page and poke around its properties to figure out what I need to set up to make it working asap.

So here is nice trick how to avoid manual Web.config editing (found it in the ScriptAculoUs autocomplete web control).

  1. Make sure your control has a designer.
  2. In your control's designer class override ControlDesigner.GetDesignTimeHtml() method, which is called each time your control needs to be represented in design mode.
  3. In the GetDesignTimeHtml() method check if your HTTP handler in already registered in Web.config and if it isn't - just register it.
Here is a sample code that worth hundred words: 
using System;
using System.Web.UI.Design;
using System.Security.Permissions;
using System.Configuration;
using System.Web.Configuration;
using System.Windows.Forms;

namespace XMLLab.WordXMLViewer
{
    [SecurityPermission(SecurityAction.Demand, 
        Flags = SecurityPermissionFlag.UnmanagedCode)]
    public class WordXMLViewerDesigner : ControlDesigner
    {
        private void RegisterImageHttpHandler()
        {
            IWebApplication webApplication = 
                (IWebApplication)this.GetService(typeof(IWebApplication));

            if (webApplication != null)
            {
                Configuration configuration = webApplication.OpenWebConfiguration(false);
                if (configuration != null)
                {
                    HttpHandlersSection section = 
                        (HttpHandlersSection)configuration.GetSection(
                        "system.web/httpHandlers");
                    if (section == null)
                    {
                        section = new HttpHandlersSection();
                        ConfigurationSectionGroup group = 
                            configuration.GetSectionGroup("system.web");
                        if (group == null)
                        {
                            configuration.SectionGroups.Add("system.web", 
                                new ConfigurationSectionGroup());
                        }
                        group.Sections.Add("httpHandlers", section);
                    }
                    section.Handlers.Add(Action);
                    configuration.Save(ConfigurationSaveMode.Minimal);
                }
            }
        }


        private bool IsHttpHandlerRegistered()
        {
            IWebApplication webApplication = 
                (IWebApplication)this.GetService(typeof(IWebApplication));

            if (webApplication != null)
            {
                Configuration configuration = 
                    webApplication.OpenWebConfiguration(true);

                if (configuration != null)
                {
                    HttpHandlersSection section = 
                        (HttpHandlersSection)configuration.GetSection(
                        "system.web/httpHandlers");

                    if ((section != null) && (section.Handlers.IndexOf(Action) >= 0))
                        return true;
                }
            }
            return false;
        }


        static HttpHandlerAction Action
        {
            get
            {
                return new HttpHandlerAction(
                    "image.ashx", 
                    "XMLLab.WordXMLViewer.ImageHandler, XMLLab.WordXMLViewer", 
                    "*"
                );
            }
        }

        public override string GetDesignTimeHtml(DesignerRegionCollection regions)
        {
            if (!IsHttpHandlerRegistered() && 
                (MessageBox.Show(
                "Do you want to automatically register the HttpHandler needed by this control in the web.config?", 
                "Confirmation", MessageBoxButtons.YesNo, 
                MessageBoxIcon.Exclamation) == DialogResult.Yes))
                RegisterImageHttpHandler();
            return base.CreatePlaceHolderDesignTimeHtml("Word 2003 XML Viewer");
        }
    }
}
Obviously it only works if your control gets rendered at least once in Design mode, which isn't always the case. Some freaks (including /me) prefer to work with Web forms in Source mode, so you still need to write in the documentation how to update Web.config to make your control working.

I was cleaning up my backyard and found this control I never finished. So I did. Here is Word 2003 XML Viewer Control v1.0 just in case somebody needs it. It's is ASP.NET 2.0 Web server control, which allows to display arbitrary Microsoft Word 2003 XML documents (aka WordML aka WordprocessingML) on the Web so people not having Microsoft Office 2003 installed can browse documents using only a browser.

The control renders Word 2003 XML documents by transforming content to HTML preserving styling and extracting images. Both Internet Explorer and Firefox are supported.

Word 2003 XML Viewer Control is Web version of the Microsoft Word 2003 XML Viewer tool and uses the same WordML to HTML transformation stylesheet thus providing the same rendering quality.

The control is free open-source, download it here, find documentation here.

I'm doing interesting trick with images in this control. The problem is that in WordML images are embedded into the document, so they need to be extracted when transforming to HTML. And I wanted to avoid writing images to file system. So the trick is to extract image when generating HTML (via XSLT), assign it guid, put it into session and generate <img> src attribute requesting image by guid. Then when browser renders HTML it requests images by guid and custom HTTP handler gets them from the session.

Having HTTP handler in ASP.NET control posed another problem - how do you register HTTP handler in Web.config automatically? AFAIK there is no out of box solution for the problem, but happily I found a solution that covers major use case. Here is piece of documentation:

When you are adding the first Word 2003 XML Viewer Control in your Web project, you should see the following confirmation dialog: "Do you want to automatically register the HttpHandler needed by this control in the web.config?". You must answer Yes to allow the control to register image handler in the Web.config. If don't answer Yes or if you add the control not in Design mode, you have to add the following definition to the Web.config in the <system.web> section:
<httpHandlers>
   <add path="image.ashx" verb="*" type="XMLLab.WordXMLViewer.ImageHandler, XMLLab.WordXMLViewer" />
</httpHandlers>

Yep. the hint is the Design mode. I'll post about this trick tomorrow.

The usage is simple - just drop control and assign "DocumentSource" property (Word 2003 XML file you want to show).

I deliberately named this control "Word 2003 XML Viewer Control" to avoid confusion. But I'll update it to support Word 2007 as soon as there is Word 2007 to HTML transformation problem solution.

Any comments are welcome. Enjoy.

Many people use converting PDF to Word as a way to change to a more easily editable document that only PDF conversion can easily accomplish--if you didn't convert PDF to Word then you'd have to manually transcribe the document, while PDF to Word software does this in a few clicks.

ScriptAculoUs autocomplete web control from SimoneB is a nice lightweight easy to use ASP.NET autocomplete textbox control with many virtues. The only problem I had with it was that dropdown autocomplete list has no scrolling and so long autocomplete lists look ugly. Happily it comes with sources so I hacked it to add scrolling, here is the solution in case somebody needs it.

  1. In Suggestion.cs add a CSS class to the UL tag generated for an autocomplete list:
    StringBuilder returnValue = new StringBuilder("<ul class=\"autocomplete\">");
  2. In a stylesheet constrain autocomplete list height and enable scrolling on overflow:
    UL.autocomplete 
    {
        height: 10em;
        overflow:auto;
    }
    
  3. In controls.js, modify "render" function to make autocomplete list automatically scrolling so a selected item is always visible:
      render: function() {
        if(this.entryCount > 0) {
          for (var i = 0; i < this.entryCount; i++) {
            this.index==i ? 
              Element.addClassName(this.getEntry(i),"selected") : 
              Element.removeClassName(this.getEntry(i),"selected");        
            if (this.index == i) {
              var element = this.getEntry(i);
              element.scrollIntoView(false);
            }
          }        
          if(this.hasFocus) { 
            this.show();
            this.active = true;
          }
        } else {
          this.active = false;
          this.hide();
        }
      },
    
That does it. Works fine in both IE and FF.

Here is one easy way:

  1. Go to xmllab.net, get free eXml Web server control and modified Microsoft's WordML2HTML XSLT stylesheet, version 1.3.
  2. Drop eXml control onto a Web form, assign DocumentSource property (WordML document you want to render), TransformSource property(wordml2html-.NET-script.xslt): <xmllab:eXml ID="EXml1" runat="server" DocumentSource="~/TestDocument.xml" TransformSource="~/wordml2html-.NET-script.xslt"/>
  3. Create new folder to store external images
  4. In code behind allow XSLT scripting and pass couple XSLT parameters - real path to above image directory and its virtual name:
    protected void Page_Load(object sender, EventArgs e)
    {
      EXml1.XsltSettings = System.Xml.Xsl.XsltSettings.TrustedXslt;
      EXml1.TransformArgumentList = 
        new System.Xml.Xsl.XsltArgumentList();
      EXml1.TransformArgumentList.AddParam(
        "base-dir-for-images", "", MapPathSecure("~/images"));
      EXml1.TransformArgumentList.AddParam(
        "base-virtual-dir-for-images", "", "images");        
    }
    
Done.

I had to add these two parameters so the WordML2HTML stylesheet could export images there and then refer to exported images in HTML. If you don't pass these parameters images will be exported into current directory - while that's ok when running WordML2HTML transformation in a command line, that's bad idea for ASP.NET environment.

Enjoy!

Yeah, my first ASP.NET 2.0 site went into production. It's a site hosted by the Israel Football Association for football referees so they can fill in game reports while relaxing at home. PowerBuilder backend - turned out to be not as bad as it could be (btw, if anyone ever want to interop with PowerBuilder from .NET via COM - never ever use OLE DB in PB code). No public access, so no URL, sorry. But what I wanted to say is that ASP.NET 2.0 and Visual Studio 2005 rock! It was just a plain coding pleasure.