Friday, 5 December 2008

HowTo use the ListExtender control

A feature of the AJAX Toolkit give multiple keystroke input when searching for terms in a drop down or listbox. The ten minute tutorial is here. It's pretty funky and could be very useful for some. Anyways, enjoy!

Tuesday, 2 December 2008

Registering User Controls in the web config

To save having to register your user controls at the top of the page in .net 2.0 you can register them in the web.config. It also means if you change the location of your usercontrol you only need to change it in one place. It shows up in intellisense but it seems to want the control nodes in the web.config to be the first entry in the pages node.

<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="uc" tagName="AdviceStep1" src="~/Applications/Advice/Controls/AdviceStep1.ascx"/>
</controls>

Monday, 24 November 2008

Optional Parameters

Hey All,

If you have an existing funtion with unknown dependants, and you want to add another parameter, consider using an optional parameter, like this:

Private Function MyExistingFunction(Optional ByVal NewParam As Boolean = False) As Int32

'If NewParam Then...

End Function


When specifying optional parameters, you must specify a default value, even if that is a blank string.

Using CSS classes on types of input

In IE 7 you can specify the type input[type="submit"]{} so your css for submits don't affect e.g. textboxes. Unfortunately this doesn't work in IE 6. This function appends a class to all the input elements to get around this problem. It's based on the fact that CSS is cumulative - class="class1 class2" combines the two classes. So what we are doing is taking the current class of the input and combining it with a new one which is specific to the type of input


window.onload = function changeClasses() {
currentdoc = document
var inputElements = currentdoc.getElementsByTagName('input');
for (var i = 0; i < inputElements.length; i++) {
if (inputElements[i].type == "submit") {
inputElements[i].className = inputElements[i].className + " submitbutton";
} else if (inputElements[i].type == "file") {
inputElements[i].className = inputElements[i].className + " file";
} else if (inputElements[i].type == "checkbox") {
inputElements[i].className = inputElements[i].className + " checkbox";
} else if (inputElements[i].type == "radio") {
inputElements[i].className = inputElements[i].className + " radio";
} else if (inputElements[i].type == "text") {
inputElements[i].className = inputElements[i].className + " textbox";
}
}
}

Wednesday, 19 November 2008

PNG transparency in IE6......a world of pain

Trying to get png transparencies to work in IE6 can be difficult. In certain cases you need to use the alpha transparency loader filter, which isn’t pretty. The good news is that if you simply want a html image to use an alpha transparency in IE 6 there is a simple JavaScript fix.
/*
Correctly handle PNG transparency in Win IE 5.5 &amp; 6.
http://homepage.ntlworld.com/bobosola. Updated 18-Jan-2006.
Use in head with DEFER keyword wrapped in conditional comments:
<!--[if lt IE 7]>
<script type="text/javascript" src="pngfix.js"></script>
<![endif]-->
*/
var arVersion = navigator.appVersion.split("MSIE")
var version = parseFloat(arVersion[1])
if ((version >= 5.5) &amp;&amp; (document.body.filters))
{
for(var i=0; i<document.images.length; i++)
{
var img = document.images[i]
var imgName = img.src.toUpperCase()
if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
{
var imgID = (img.id) ? "id='" + img.id + "' " : ""
var imgClass = (img.className) ? "class='" + img.className + "' " : ""
var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
var imgStyle = "display:inline-block;" + img.style.cssText
if (img.align == "left") imgStyle = "float:left;" + imgStyle
if (img.align == "right") imgStyle = "float:right;" + imgStyle
if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
var strNewHTML = "<span " + imgID + imgClass + imgTitle
+ " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
+ "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
+ "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>"
img.outerHTML = strNewHTML
i = i-1
}
}
}


See this page for a more in depth treatment: <a href="http://homepage.ntlworld.com/bobosola/index.htm">http://homepage.ntlworld.com/bobosola/index.htm</a>.

The really tricky bit comes when you want to png’s for something like a rounded css driven border. In general you can use divs nested within one another and use their background properties something like this:
CSS
#banner_main
{
background: url(/PBSNET2008/AdviceBetaGUI/Applications/Advice/Images/banner/banner_bg.png);
height:60px;
}
#banner_left
{
background: url(/PBSNET2008/AdviceBetaGUI/Applications/Advice/Images/banner/banner_bg_left.png) 0 0 no-repeat;
height:60px;
width:8px;
width:100%;
}
#banner_right
{
background: url(/PBSNET2008/AdviceBetaGUI/Applications/Advice/Images/banner/banner_bg_right.png) 100% 0 no-repeat;
height:60px;
width:100%;
padding-top:6px;
}
<div id="banner_main">
<div id="banner_left">
<div id="banner_right">
</div></div></div>

While you can use the AlphaImageLoader in your divs as a replacement for background image they aren’t proper background images. The problems are:

1. They don’t tile properly - you can ‘crop’ them so they appear once in the div or stretch them. Depending on the shape of the image and the shape of the containing div this can look OK or really bad.

2. The lack of tiling means you can’t use background position as in the code above.

3. This is really buggy. Buttons and form elements often don’t work properly. Weirdly setting the text-align property of the parent div to the left or right weirdly makes the button work. Sometimes. Depending on the size. And how many times you click it. And where about you click it. I am not the only one to have this behaviour.

4. The AlphaImageLoader doesn’t work unless you explicitly set the width or height which can make stretchy layouts difficult without Javascript hacks to set the width/height when something changes.

Here is some code for a curvy top of a rounded css driven box. Note the use of floats to get the layout to work.
#header_center
{
margin-left:30px;
margin-right:30px;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=/PBSNET2008/AdviceBetaGUI/Applications/Advice/Images/dialogs/header.png,sizingMethod='scale');
height:60px;
color:White;
font-family:Arial;
}

#header_left
{
height:60px;
float:left;
width:33px;
margin-right:-3px;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=/PBSNET2008/AdviceBetaGUI/Applications/Advice/Images/dialogs/header_left.png,sizingMethod='scale');
}
#header_right
{
float:right;
width:34px;
height:60px;
margin-left:-3px;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=/PBSNET2008/AdviceBetaGUI/Applications/Advice/Images/dialogs/header_right.png,sizingMethod='scale');
}

Dealing with joins that may produce null

This particular example was used to help resolve an issue. I wanted to list invoices and the customerid. Unfortunately some of the data was bad and the invoices didn't have parent records. In this particular situation fixing the data integrity issue wasn't an option. Also there are lots of times you may need to do this regardless of data integrity

'Create a customer object to represent the null customer

Dim null_customer As New svc.Customer
null_customer.CustomerID = -1


'In this case we are selecting into a named type

Dim Search_Flattened As IQueryable(Of Customer) = _
(From t In SearchResult _
Select New Advice.JobAbout _
With {.CustomerID = t.Customers.DefaultIfEmpty(null_customer).FirstOrDefault.CustomerID, _
.DateTimeModified = t.DateTimeModified, _
.AddedBy = t.AddedBy, .InvoiceID = t.InvoiceID})

Now the .CustomerID is -1 for the invoice if there is no parent record.

Dynamically adding where and order by clauses

This is great for creating dynamic queries which have optional where parameters. This example is using linq to entities in the data service rather than on the client


Dim c As IQueryable(Of Model.Customer) = (From cust In ctx.Customer)

If strSurname.Length > 0 Then
c = c.Where(Function(a) a.Surname.StartsWith(strSurname))
End If

If strForename.Length > 0 Then
c = c.Where(Function(a) a.Forename.StartsWith(strForename))
End If


Dim c_ordered As IOrderedQueryable(Of Model.Customer) = c
c_ordered = c_ordered.OrderByDescending(Function(p) p.DateTimeModified)

I also sort the query so linq to entities can use the skip function. The skip function throws an error otherwise.

Geting the URL your linq to ado.net data services is producing

When you use linq on your data services the linq is generating a URL to grab the data from the data service. Sometimes it isn't obvious if an error is in the data service or in your client code. If you do something like this:

Dim qry = (from i in ctx.Invoices where i.invoiceid = intInvoiceID)

You can easily get the URL by simply writing

qry.ToString

Paste that in your browser to confirm that data service is definitely working OK.

Now you've flatterned your data - what if you want to return it from a function

To make your flattened data more reuseable you may want to return it from a function. In the previous example it was an anonymous type. This is pretty straight forward - you just project it into your own type. Unfortunately you have to create it yourself but once it's done you don't have to do it again.

Dim bln As Boolean = False
Dim lst = (From t In ctx.OrderLine.Expand("Orders").Expand("Orders/Invoices") Where t.Completed.Equals(bln) And t.Username.ToLower = Trim(strUserName.ToLower)).ToList

Dim lst_aliased as List (of CustomTypes.CustomOrder) = (From t In lst Select New CustomTypes.CustomOrder With _
{.OrderLineQuantity = t.Quantity, .OrderDate = t.Orders.OrderDate _
.InvoiceNo = t.Orders.Invoices.InvoiceNo})

we can now return lst_aliased from a function as it is no longer an anonymous type


There are some other features here - mainly the use of expand to include the related data.

How to flatten your dataservice results

One problem you may have come across when getting data from your data service is the problem of how to "flatten it" so you can bind to, for example, a gridview. The problem with dataservices is that they don't allow either projection or joins. For instance this:

dim c = (from t in ctx.tblCustomers select t.customername, t.CustomerType.customertype)

will not work. Linq to dataservices can't express this as XML.

The trick is to grab all the data you need by enumerating it THEN do the join. Once enumerated the data can be manipulated via linq to objects. So this will work

Assuming you have used expand to add the related tables...


dim lst = (from t in ctx.tblCustomers).ToList

dim lst_flattened = (from t in lst select t.customername, t.CustomerType.customertype)

gvwCustomer.DataSource = lst

etc

Be aware that lst is now an anonymous type. If you need to return it from a function then you need to create your own type and then select into it.

Tuesday, 18 November 2008

Frist Post! [sic]

May I be the first to say hello, and welcome you to the IT blog. This should hopefully be a running commentary on the IT departments doings and musings, along with light relief from the hardcore tech-driven info.

Would all other developers please contact me and i'll add you to the list so you may contribute to this blog. (People without a google mail account can use the general peninsula address, see MM for details)