.Net ramblings
# Friday, 17 February 2006
FtpWebRequest with ProgressBar [C# Ftp]
Screenshot of upload operationhi,
i was delighted to see .Net 2.0 has an excellent class for working with FTP.  looking back i can't believe it wasn't part of 1.1. 

i noticed there was no built-in support for reporting the progress of an FTP operation to a user interface.  I needed this functionality in an app i'm working on so i built a class and i'm posting it here in case anyone is interested.

The class FtpProgress inherits from BackgroundWorker, so you just drag an FtpProgress component onto your form, hook up events for Completed and ProgressChanged, and your laughing.  There is a mini class called FtpSettings which stores all the connect info etc to pass in as a paramter to the RunWorkerAsync() method.

I have a variable called 'ChunkSize' which determines the size of each buffer written to the FtpWebRequest output stream.  It is set to 4k by default, and you can increase this if you want for very fast networks but i don't think it will make much difference.  I think the overhead is negligible.

Download the source code and demo application [30 k].

It only does upload, so if you want download functionality, you can do the mirror-image of the upload code, i.e. instead of writing buffers to the output stream, read it from the input stream, or however that would work :) 

here is all the code you need to start an Upload, and report progress:
private void btnUpload_Click(object sender, EventArgs e)
{
// create a new FtpSettings class to store all the paramaters for the FtpProgress thread
FtpSettings f = new FtpSettings();
f.Host = this.txtHost.Text;
f.Username = this.txtUsername.Text;
f.Password = this.txtPassword.Text;
f.TargetFolder = this.txtDir.Text;
f.SourceFile = this.txtUploadFile.Text;
f.Passive = this.chkPassive.Checked;
f.Port = Int32.Parse(this.txtPort.Text);
this.ftpProgress1.RunWorkerAsync(f);
}

private void ftpProgress1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.toolStripStatusLabel1.Text = e.UserState.ToString(); // the message will be something like: 45 Kb / 102.12 Mb
this.toolStripProgressBar1.Value = Math.Min(this.toolStripProgressBar1.Maximum, e.ProgressPercentage);
}

Friday, 17 February 2006 18:09:05 (GMT Standard Time, UTC+00:00)  #    Comments [29]  .Net General | .Net Windows Forms

# Thursday, 02 February 2006
Using Xml DOM with MetaBase.xml
For some reason i can't get SelectNodes to work with the IIS 6 MetaBase.xml file.  It just doesn't return any matches.  I think the structure may be non-standard or too complex, or else i'm not using it right.  but in any case, I worked around the problem by opening the file with File.OpenText and using Regex to parse out the nodes i'm interested in, namely the IISWebServer nodes.

string MetaBase = "";
using(StreamReader sr = File.OpenText(ConfigurationManager.AppSettings["MetaBasePath"].ToString()))
MetaBase = sr.ReadToEnd();

foreach(Match m in new Regex(@"(?x:)<IIsWebServer[^>]*>.*?</IIsWebServer>", RegexOptions.IgnoreCase).Matches(MetaBase))
{
XmlDocument doc = new XmlDocument();
string IisCode = "", ServerBindings = "", ServerComment = "";
doc.LoadXml(m.Groups[0].Value);
XmlElement node = doc.DocumentElement;
// now you can access the node attributes
...


Thursday, 02 February 2006 13:38:34 (GMT Standard Time, UTC+00:00)  #    Comments [0]  .Net General | .Net Windows Forms | Asp.Net | Windows Server

CheckBoxList: Select All / None client-side script
<script language="javascript" type="text/javascript">
function Select(Select)
{
for (var n=0; n < document.forms[0].length; n++)
if (document.forms[0].elements[n].type=='checkbox')
document.forms[0].elements[n].checked=Select;
return false;
}
</script>


Select <a href="#" onclick="javascript:Select(true)">All</a> | <a href="#" onclick="javascript:Select(false)">None</a>


Thursday, 02 February 2006 13:25:33 (GMT Standard Time, UTC+00:00)  #    Comments [7]  Asp.Net

# Monday, 30 January 2006
Fix: Unable to validate data, MachineKey.GetDecodedData
i kept getting these sporadic error messages on my web applications and i could never figure out why. 
Unable to validate data at
System.Web.Configuration.MachineKey.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength) at
System.Web.UI.LosFormatter.Deserialize(String input)
searching the net just reveals a troop of similarly frustrated users without solutions.  but today i found the actual reason why, and the work-around.
this excellent post on experts-exchange has the answer.  apparently it happens when users leave a page open for a long time, and then cause a post back.  something to do with the machine key being automatically generated and it changes before the user causes the postback, and it can't validate it then. 
the fix is to set a static machine key. 
There is a kb article with some code to generate a key for you, + instructions.  

Sorted.


Monday, 30 January 2006 22:53:44 (GMT Standard Time, UTC+00:00)  #    Comments [13]  .Net General | Asp.Net

ADOX + Excel: bogus worksheets
a web site i'm working on imports excel documents, and does some processing on them for importing into a database.  I use the code from this post to do the importing, and it works nicely.  I recently came across a problem where i was encountering duplicate records, and it took me ages to figure out why.  Apparently a 'named range' of cells in a worksheet is treated as a Table by ADOX.  so you get more than you bargain for when you iterate through the tables in the resulting DataSet. 
I was able to work around the problem by discarding any tables that do NOT end in the dollar $ character.


Monday, 30 January 2006 15:30:12 (GMT Standard Time, UTC+00:00)  #    Comments [1]  .Net General | Asp.Net | Database

# Wednesday, 25 January 2006
Crystal Reports TextObject ignores newline, carriage return, \r\n
I have a text object in my crystal report, and i set the value programatically for it, using something like this:
(rpt.Section3.ReportObjects["txtDate"] as TextObject).Text = DateTime.Now.ToShortDateString();
this works fine, until you put a string with line breaks inside it, specifically \r\n or the carriage return character.  This is a bug in CR for .Net, you can read the official blurb here.  the work-around code they post is in VB, and it made no sense to me when i read it.
what they actually do is make you change the TextObject into a FormulaField (you have to view the 'Field Explorer' tab next to the toolbox, and drag on a Formula Field).  Then you set 'CanGrow' to true, and then you go back to your code, and do the most arcane work-around i've ever seen.  you set the formula to your text string, but you must surround it in single quote characters, and replace \r\n with some inline managed code as follows: ' + chr(10) + '
when i first read this, i thought they had made a syntax error, but this is the way to do it, and it works.  the new format for setting the formula field is:
rpt.DataDefinition.FormulaFields[0].Text = "'" + YourString.Replace("\r\n", "' + chr(10) + '") + "'";


Wednesday, 25 January 2006 13:52:05 (GMT Standard Time, UTC+00:00)  #    Comments [16]  .Net General | .Net Windows Forms | Asp.Net

# Thursday, 19 January 2006
The best free zip / compression software: 7-zip
i gave up using winzip a long time ago because it is way too 'naggy' in terms of making you register. 
windows 2000/XP has its own built-in 'send to > compressed folder' option when you right-click a file or rolder.  but the windows zip software is pretty rubbishy.  it takes forever to set or remove passwords on archives, or to simply delete a small file from an archive can take 20 seconds. 

by far the best zip software i have used is called 7-zip and you can download it for free at http://www.7-zip.org
it is lightning fast, and although the custom zip format '7z' does not appear to be anything great, especially because its not compatible with other zip software, set the program to use normal 'zip' by default and it works great. 


Thursday, 19 January 2006 11:57:28 (GMT Standard Time, UTC+00:00)  #    Comments [0]  General

# Friday, 13 January 2006
Localization: Setting text direction for a web page
This is the first time i have been asked to add localization support for a content management system.  One of the supported languages is Arabic, which is usually written right-to-left.  to achieve this, you could obviously change your CSS or markup with something like <p align="right"> but this is not the way to do it.

there is an attribute in the HTML tag that controls the direction of text (and controls) on the page, called 'dir', which can take values 'ltr' or 'rtl', corresponding to "left-to-right" and "right-to-left" respectively.  The advantage of setting this property is that it instructs the browser to render all direction-related elements of the page accordingly.  for example, in RTL mode, when you type in a text box, the cursor stays flush to the right and the characters spread across to the left as you type.  similarly drop-down-menu's are rendered differently, as are bullet points, and the browser scrollbar.  these changes are shown in the screenshot below:



In this content management system, i set the direction based on the user's language setting, stored in a cookie.  to set the DIR attribute programatically, i suggest the following HTML tag in your Master/Template aspx page:
<html xmlns="http://www.w3.org/1999/xhtml" runat="server" id="htmlTag">
Then you can access it in the code behind, like so:
protected void Page_Load(object sender, EventArgs e)
{
this.htmlTag.Attributes.Add("dir", Util.HtmlDir);
...
and then the method to determine the direction, based on the cookie. note that 'Language' in my code is an enumeration.
/// <summary>
/// Returns rtl or ltr, depending on the current language setting
/// </summary>
public static string HtmlDir
{
get
{
Language lang;
try
{
lang = (Language)Enum.Parse(typeof(Language), HttpContext.Current.Request.Cookies["Language"].Value.ToString());
}
catch
{
// cookie not present, default to english.
lang = Language.English;
}
switch(lang)
{
case Language.Arabic:
return "RTL"; // right to left
default:
return "LTR"; // left to right
}
}
}

public enum Language
{
English,
Arabic,
...
}

for more information on languages and their directions, and related browser issues, see this excellent article from W3C


Friday, 13 January 2006 16:28:16 (GMT Standard Time, UTC+00:00)  #    Comments [2]  Asp.Net

# Thursday, 12 January 2006
Fix: GridView DataFormatString not applied
for some bizarre reason, you have to set HtmlEncode=false on a bound column in a gridview, to get the DataFormatString to work.
i hope this helps somebody else staring at their gridview in disbelief as to why it doesn't work by default!


Thursday, 12 January 2006 16:01:38 (GMT Standard Time, UTC+00:00)  #    Comments [64]  Asp.Net

# Monday, 09 January 2006
Xml Serialization: Properties left out without 'set' accessor
I ran into a frustrating problem today where some properties of my objects weren't getting serialized.  I noticed it was the ones without 'set' accessors.  I didn't want to put set accessors in because the properties should be read-only, but apparently the xml serializer needs the set accessor to de-serialize. 
there is a good discussion on it on this thread.
Luckily we can use the ReadOnlyAttribute setting on the property to prevent it being modified by a programmer at design time.
or you can use the Soap serializer instead of xml, whatever that is.


Monday, 09 January 2006 17:57:34 (GMT Standard Time, UTC+00:00)  #    Comments [0]  .Net General | .Net Windows Forms | Asp.Net