Thursday, April 24, 2008

Enter: the Clone control...

I haven't posted in a while, so I thought I would post a little bit of ASP.NET goodness that I whipped up too, very quickly, fill a gap.

If you have ever needed to duplicate a control on your page completely (behaviour, look & feel), but just alter the position then I think this could help. I think its wonderful in its simplicity, but then I would. I wrote it. :D

A little bit of background: I needed to have the same button at the top and the bottom of a GridView control - it made sense from a usability point of view. I wasn’t happy with creating a UserControl or to copy/paste a duplicate wired up the same way, so I whipped this baby up. This clone control has some basic design-time support. The property page of the control will display a list of controls already placed on your page, will allow you to select one of them. This will be the control that will be duplicated. Pretty basic stuff (take a look and modify the FilterControl method of the CloneControlConverter if it doesn't meet your needs).

DISCLAIMER: Consider this code as Alpha quality. I am sure there will be issues with the more complex controls (just for giggles I tried it out on a GridView and it seemed to work :P ). But it’s completely up to you to determine if its production ready. If nothing else it may help point you in the right direction. So with no further ado… drum roll please... say hello to my leeedle friend!

internal class CloneControlConverter : ControlIDConverter
{
protected override bool FilterControl(Control control)
{
return !(control is CloneControl) &&
!(control is Page) &&
!(control is DataSourceControl) &&
!(control is HierarchicalDataSourceControl) &&
!(control is Content);
}
}
[ToolboxData("<{0}:CloneControl runat=\"server\" />")]
public class CloneControl : Control
{

[DefaultValue(""), IDReferenceProperty, TypeConverter(typeof(CloneControlConverter))]
public string ControlToClone
{
get
{
string value = this.ViewState["__controlToClone"] as string;
return value ?? string.Empty;
}
set
{
if (value == null)
this.ViewState.Remove("__controlToClone");

this.ViewState["__controlToClone"] = value;
}
}

protected Control FindControlToClone(string controlId)
{
Control control = this.NamingContainer.FindControl(controlId);
if (control == null)
control = this.Page.FindControl(controlId);

return control;
}

protected override void Render(HtmlTextWriter writer)
{
Control c = this.FindControlToClone(this.ControlToClone);
if (c == null)
return;

c.RenderControl(writer);
}
}














And that’s it. Nifty eh? :)



Enjoy!

Thursday, April 17, 2008

The asp:radiobutton and the tale of the repeating control...

How many times have you needed to use a radio button in a repeating control, such as a GridView? Simple right? Just create a template column, drop in an asp:radiobutton and you are off. Sounds fantastic, yet it's a little more tricky than it first appears. The problem is that a radio button uses the name attribute to determine the group in which it belongs. That way it gives you the "only have one selected" effect.

The problem is that all server controls, housed within a repeater of sorts, are more than likely going to be created within an object implementing an interface called INamingContainer. This is a “token” interface that instructs the framework to create a unique name for any child controls that the control contains, at runtime, based on its own name (i.e. the name of the parent). That way, you can define a row template, have a data source with multiple rows, and not have a naming clash. Actually, its a requirement of the framework that all controls have a unique name. Technically, I am being incorrect, the framework requires all controls to have a unique ID, but the asp:radiobutton will generate a unique ID and a unique name (generally its exactly the same as the ID). However, as you can probably guess, the problem in this particular case, is the "name clash" effect on the name attribute is exactly the behaviour we need! Remember, it defines a “group” of radio buttons. The problem is that this behaviour is exactly what INamingContainer was created to avoid.

So enough chat... how do we get around this limitation without having to go to the trouble of rolling our own server control? The answer is actually quite simple. Don't use a server control.

For example, in the markup, within the repeater control (in this my case it was a GridView, if you were curious), define a templatefield as such:


<
asp:templatefield>
    <
itemtemplate>
         <
input name="rbSelected" type="radio" value="<%# Eval("ID") %>" />
    </
itemtemplate>
</
asp:templatefield>

As you can see, its a normal, plain old HTML element (admit it, you didn't even consider using one of these babies did you? :D ). Take note that it does not have a runat="server" attribute and is therefore (yes you guessed it) not a server control. This will make it exempt from the naming rules. I have however, used the databinding syntax to bind a useful value into the value property of the element, to demonstrate that while we are not using an asp:radiobutton server control, we can still make use of the funky databinding expressions that we know and love (and sometimes hate). This is because the row housing the element is a server control and when its databinding expressions are evaluated, databound values will be in the output. In my fictional case, this would be the primary key of the item I wanted to select. This could be anything you like or anything that is useful in your context.

Right, so the next question is: how do we access this value from the codebehind? This turns out to be very easy, just interrogate the Form value from the Page.Request object like so:

int value; 
if (!int.TryParse(this.Request.Form["rbSelected"], out value))
value = -1;
return value;














It's that simple.



However, to reiterate, the radio button in this example is not a server control. That means that we do loose a lot of the yummy-goodness that comes with having the control available on the server side (server side events to name just one). Therefore, this approach could be somewhat limited. However, for a simple option select, within a repeating control, you can not beat the simplicity and ease in which this solution can be implemented.



Enjoy!

Wednesday, April 9, 2008

T4 and the framework 3.5...

I have been playing around with the T4 template engine for the last few days now. One of the first things that I noticed was that the wonderful "var" keyword and lambda expressions were not recognised. Obviously, the template generator was generating against the framework 2.0. I couldn't believe this was the only option we had available, especially with the release of VS2008. Google wasn't all that helpful and revealed nothing.

Looking at the "official" documentation also revealed very little, except that the using the template directive <@# template #> you could change the language from C# (the default) to VB. Fantastic. Not what I wanted, but I felt like it was the right place to begin my search.

So out came good’ol trusty Reflector and off I went, with my little code explorer hat on. After a few minutes of digging around I managed to track down this little bit of code:

string x = str.Substring(2);
if (StringComparer.OrdinalIgnoreCase.Compare(x, "v3.5") == 0)
{
    session.LanguageOptions["CompilerVersion"] = "v3.5";
}

Aha! Look at that... A quick change to the language parameter of the template directive and I was in business. :) So, should you want to compile your T4 template using all the 3.5 goodness that is on offer, use the following:
<@# template language="C#v3.5" #>
... or ...
<@# template language="VBv3.5" #>

And you should be in business... something I do think they need mention in the doc's somewhere.

 

Enjoy!

Sending fax-emails through RightFax using the COM API...

Captris RightFax is a very funky document delivery system. It will handle the conversion of documents to and from "Fax" ready formats (e.g. TIFF) and send them on. My experience with it has been extremely limited but from what I have seen I am duly impressed. It is built in a modular format so additional modules can be purchased/ignored depending on your particular needs. For instance there is an additional OCR component that can be purchased (albeit for quite a hefty price tag) and snapped into the installation for use. However, I only had the immediate need of only one of the basic features and that was to send a "fax" through the API to an email address.

Using the object model its very simple. Fill in the required email address, add the attachments, and send. This seems to work a treat, however, I was stymied when all the emails I had sent, ended staying in the "Waiting to send" status indefinitely. At first I thought this had to be a setup issue, but using their client, emails could flow fast and furious. I then consulted the documentation for the API which, while it semi-explains everything (its better than nothing I guess), it didn't answer any of the questions I had about anything relevant. I tried every permutation of the sending code as I could think of.

Finally, I stumbled upon the solution out of pure frustration. Here it is in c#:

//assume server is the RightFax server object, and userID is a valid RightFax user
Fax fax = (Fax) server.get_CreateObject2(userID, CreateObjectType.coFax);

fax.IsINLJob = BoolType.True;
fax.ToEmailAddress = "bill@gates.com";
fax.EmailSubject = "RightFax Fax Email...";

fax.IsProductionFax = BoolType.True; // <--- critical
fax.Send(); // <--- critical

The critical two items here, seem to be that you have to set the .IsProductionFax property to BoolType.True and you have to send the fax with the .Send() method. There is a .Save() method that takes a BoolType as a parameter that can be used to perform a similar action to the straight out .Send(). It works with normal faxes, but in the case of an email - no dice.  For the life of me, I can't imagine why these two small things would make a difference nor why its not mentioned in the documentation. And speaking about documentation... this is quoted directly from the official documentation regarding the .IsProductionFax property.

"IsProductionFax
Data type: Boolean Read/Write
Specifies whether or not the fax was generated by the RightFax Production module."

And that's it. Nothing else in the entire document. Not even a *hint* there.

At least I don't feel stupid for missing it the first couple of times around... ;)

Microsoft Office Document Imaging woes...

It’s that time of the morning... that wondrous time when I hope that my final round of testing, with my brand-spanking-newly developed component is going to work... maybe.

And all this because it seems as though there are a series of hoops that one has to jump through to make any Microsoft Office COM component to operate smoothly. And by smoothly, I refer to, in particular, convincing the COM component to let go of any/all resources it was consuming and/or quit. These two things are surprisingly hard to achieve.

It has been a night fraught with dangerous code refactoring, extensive use of the Marshal.ReleaseComObject and Marshal.FinalReleaseComObject, AppDomains and various other craziness. I thought I had it at one stage where I would load the assembly of the component into a temporary AppDomain, taking great care as to not let anything cross the Domain boundaries in an attempt to make sure there were no cases of rogue assembly leakage, ensuring that when I unloaded the AppDomain all would be right with the world. The long and the short of that approach is that it didn’t help.

(WARNING: to save those time looking for some direction should they be having similar issues, let me just stop you right here and tell you up front, I hacked it. The working solution I came up with was quick, dirty, and above all nowhere near elegant. But it works! You do these things when a demo to the boss is looming, first thing the next morning, and its just hit 02:30 AM).

Now, to the background of my tale of woe... We have had a windows service application developed by a 3rd party that makes use of MODI (Microsoft Office Document Imaging). They use this component to facilitate OCR (Optical Character Recognition). MODI seems a fantastic alternative (far from perfect but it gets the job done for the most part) for businesses out there who don't want to (or can’t) fork over the wads of moolah for those high end OCR components - especially when they, almost certainly, already own a licence for Microsoft Office. The best part about it? Its actually rather good at what it does (right, enough about MODI now, perhaps I'll do a very quick post about it - not many people know about it as an alternative OCR engine you can manipulate through code). However, this particular windows service had issues (I am being very nice with that statement), and unfortunately required extensive reverse engineering and recompilation. This poor service has/had a lot of issues, and one of the components not working correctly was the OCR (to give you an example, the vendor only OCR'ed the first page looking for a specific text string, so if the client sent the fax with a cover sheet, it was all over). During our testing, I noticed errors popping up with the MODI component. These would happen after a few documents had been OCR'ed successfully (not necessarily having that text found, just passing through the engine). Obscure sounding errors about the COM server throwing this and that and a good amount of GoG (Good 'ol Googling) resulted in nothing concrete.

Enter my "Bright Idea(tm)" of rewriting the OCR component. It should not have been a big deal. Just clean up the messy code and the COM component takes care of the rest? Right? Well yes, it did the rest and more. Like holding onto the file it was supposed to OCR for dear life. After managing to pry the file handle out of MODI's tight little clutches by Marshalling my way to success and trying to make sure there simply could be no dangling references to MODI lying around... anywhere... ever… ever ever. I thought it was going to be clear sailing. Initial unit tests made me feel all warm and fuzzy with those green lights of goodness burning bright and having the service chug away at a few documents and finding once lost reference numbers filled me with giddy glee.

That is, however, till I saw the RPC_E_SERVERFAULT errors popping up like crazy in the event log. The odd (or perhaps not so odd) thing was that if the service was restarted MODI would once again begin to function correctly. That is, until a few documents had been processed and/or some random time had passed.

Then the problems would line up. Take a number, stand in line.

Now, I am no stranger to “COM Interop” in .NET (but I am by no stretch of the imagination a Guru), and I performed all the good COM house cleaning I could and then some, to the point of overkill. Try-Catch blocks everywhere there should be with Finally blocks to make sure things were released after useage. Still nothing. Same issues. As I mentioned previously, I even went as far as to create a temporary AppDomain and try and load everything through there... Still the same errors.

Finally I cracked. Desperate times, desperate measures and all that. I pushed the OCR component out into an executable and now a whole new process is spawned when the OCR needs to happen - not just a thread. That way the entire process would close down and ensure (that's the theory anyway) all references were severed and objects cleaned up.

The good news is that it works. It has continued to work for the last hour. I am overjoyed to say the least. However, I really don't like it as a solution. I need to know why MODI was faulting in such a spectacular way. I just can't figure it out. If I had it in me - I would post some code, but right now I am too bushed.

I just hope I don't have to face that particular problem again for a little bit. And if I do, I would like it to be when I don't have a deadline looming over my head. At least, now, I don't have to tell the powers-that-be that its completely broken this morning...

Little victories.

 

 

   

Monday, April 7, 2008

T4 and something to keep in mind...

A friend of mine recently did a post on the T4 framework (Text Template Transformation Toolkit) here. I had been following the technology for a little bit but I haven't had the opportunity to actually use it (just literature till now). After reading that post, I decided it was time to put on the water-wings and give it a whirl from within VS2008 (no additional downloads for VS2008). Needless to say, its pretty good. Honestly, its about time they built a template engine directly into the IDE. I think this will encourage a host of folk to rally around the banner of Code Generation goodness.

With all that said, there is a small thing to keep in mind. It's something that, when you think about it, is quite obvious, yet because of its nice "integration" with VS2008 its easy to overlook (well for me it was anyway).

What I wanted to do was, quite simply, using code inspection, create a set of interfaces and base implementation classes for a set of types. For example:

public interface INodeVisitor
{
bool Walk(BlockStatement node);
void Postwalk(BlockStatement node);

bool Walk(ScopeStatement node);
// .... continue on
}



I thought it would be a funky "first time" use of the template engine as if I added a new type, both my IVisitor and the various default implementations could be generated. I was quite proud of myself for finding such a nice "practical" use for T4 (I really didn't want to do the atypical "generate the business objects from the database" routine). The problem is, even though the template "looks" like it is part of your solution - it is important to realise that only the generated file is actually part of your solution, and not the template itself (ok, technically, that's wrong. The template is part of your solution, its just not within the same "code scope" - if I can call it that...). That is to say, you don't have access to types, from within the template (via template statements and/or features), that are defined within the same solution.



The reason for this is actually very simple and straightforward. The default T4 compiler kicks in via an "external" command line call with, compiles the template file, applies the transforms and generates your file. Therefore, like I have mentioned, its important to keep in mind that the template compilation and file generation happens outside of the scope of your own solution's compilation cycle. However, the generated file will still be compiled as part of your solution's normal compilation cycle.



Now before everyone pipes up at once, I do realise that using template directives, you can reference another assembly. The only issue is, that from what I saw, it required the full path to the assembly (i.e. "C:\development\someproject\bin\debug\somedll.dll"). I am not a huge fan of this, because of the potential for different build configurations (do you import the debug version of the assembly or the release version?). It just felt a little dirty to me. Also, we wont even step into issues like what happens on the first time build when the assembly doesn't yet exist. I guess you could use the hostspecific="true" and try and interact with the code model using the EnvDTE (from what I know the Visual Studio T4 host implements IServiceProvider) but I haven't gone that far just yet. Something to look into. Perhaps the topic of another post...



Anyway, like I said, nothing major, and not something that should stop anyone from using T4 - generally most (if not all) the other template engines should suffer the same limitation. It's just something to keep in mind when designing/creating your templates. It's way to easy to assume that because its within your solution explorer, that it has the built-in ability to access all types available within your assembly.



To sum up. T4 - I like it. I'll probably make more use of it where I can - especially in the area of boilerplate test cases - even if I have to "full path" the assembly reference (or use some DTE magic - is that even possible?) The simple fact that I don't have to bounce out to another application to compile the template and that there are no additional downloads, makes it a winner for me. It's really useful when you just want to quickly whip something up to "fill that gap" and not have to alt-tab for it and contend with project file changes and reloads...



My only gripe? No built-in intellisense! I guess that if you don't want to have download and install something like this T4 Editor, template edition is going to have to be done without all our new fan-dangled toys and back to the notepad roots! (for now, at least ;D).