Showing posts with label Code. Show all posts
Showing posts with label Code. Show all posts

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... ;)

Wednesday, March 5, 2008

Don't forget about string.IsNullOrEmpty() in .NET 2.0

I have noticed, in my travels though code written by others, that string.IsNullOrEmpty is simply being overlooked.

As most of you know by now, its much better practice, performance wise, to check if length of a string is less than or equal to zero, than comparing it to "" or string.Empty, when trying to determine if its an empty string or not. For those of you who didn't (yes you, in the back, I see you) here is a MSDN article that answers the question. Anyways, I digress and that's not the focus of this post.

The point is that in .NET 1.1 you did the classic:

if( someString == null || someString.Length <= 0 )
return;


Fantastic. However, typing that every time you want to do a "is the string empty check" gets old quite fast. Never fear - MS to the rescue of our ever precious keystrokes. It was decided that in .NET 2.0 that a static function, doing just the above, would become part of the public string interface and save us a little of the hassle.



Now the above code can be "simplified" as:

if( string.IsNullOrEmpty(someString) )
return;


It's a lot more expressive of intent, therefore more readable and as an added bonus, less of a pain to type (a massive 12 chars including white space ;P).



I have just noticed that a great deal of developers either don't now about it, simply don't use it or are still comparing strings to "", hence this post. Not exactly a "wow" or "this-going-to-change-my-life-forever-thank-you-so-much" post, but hopefully it will serve its purpose. It's just a little reminder to those who have made the move from 1.x to 2.0+ (or are just starting) that it's there... aching to be used.

Tuesday, March 4, 2008

The Object.Equals Gotcha

I haven't had the need to override the .Equals implementation on reference types all that much in my coding career. However, such a need sprang up earlier on today. While implementing it in the most rudimentary way, I just couldn't shake the feeling there was something that just didn't feel right. So I booted up Good 'ol Google and took a quick look at what was out there.

I came across this article that spells it out quite nicely (be sure to read the comments as well). Bottom line, if you are dealing with a reference type, its better to include an additional method on your public interface that will compare the contents for equality.

It's still a bit of a gray area for me. I am going to have to sit a while and mull over just what exactly the implications are, but these little bits of information are great to have stored in the back of your mind.

Saturday, October 27, 2007

When simple is just not enough...

We all have felt the pain at one time or another. Yes... you know it... its that warm fuzzy time when you have to maintain someone else's code.

Generally, its difficult enough to adapt your thought process/logic to that of someone else, however, the added fun comes when the person who actually created the masterpiece of code, did it with a complete disregard to any sort of afore mentioned logic.

Now please, don't get me wrong... I don't usually go around ragging on code that someone else has done to the best of his/her ability (in this particular case lets hope, for the sake of the creator, it was the result of something closer to an alcohol induced coma as opposed to a fair reflection of said ability), but sometimes, especially at 01:20 in the morning, having to sift through what can only be described as a waste of time, I am a little less in the forgiving mood...

I believe the following sums it up:

   1: CultureInfo cuInfo = CultureInfo.CurrentCulture;
   2: NumberFormatInfo numberFormatInfo = cuInfo.NumberFormat;
   3: string strNegativeSymbol = numberFormatInfo.NegativeSign;
   4: if ((dtbContract != null) && (dtbContract.Rows.Count > 0))
   5: {
   6:     decContracRate = Convert.ToDecimal(dtbContract.Rows[0]["PayoffAmount"],
   7:                        System.Globalization.CultureInfo.InvariantCulture);
   8: }
   9:  
  10: if ((decContracRate.ToString().IndexOf(strNegativeSymbol) > -1))
  11: {
  12:     btnPrintPayment.Enabled = false;
  13:     if (btnIsVoid.Enabled == true)
  14:     {
  15:         btnIsVoid.Enabled = false;
  16:     }
  17: }
  18: else
  19: {
  20:     btnPrintPayment.Enabled = true;
  21: }

Excluding all the other issues with this code snippet, unless I am missing something extremely sneaky - I am pretty sure the following would have done the trick:



   1: if( decContractRate < 0 )
   2: {
   3:     // oh look - its a negative
   4:     ...
   5: }

Yup... sometimes simple is just not enough...