.Net ramblings
# Wednesday, 21 December 2005
GridView Databinding error "Field or property ... not found on the selected data source"

A field or property with the name 'xyz' was not found on the selected data source

I get this error when i bind a GridView to an array of custom objects with public variables. 
The recommended pattern for designing classes is to have private variables with public properties with get/set etc, but for my current application i'm not bothering with that. Apparently because of the Reflection methods used in GridView databinding, you have to have properties to bind fields in the GridView to your object fields. 

bit of a pain.


Wednesday, 21 December 2005 15:51:28 (GMT Standard Time, UTC+00:00)  #    Comments [3]  Asp.Net

# Tuesday, 20 December 2005
GridView - accessing row information inside RowCommand
in .net 1.1, the GridCommandEventArgs (or whatever) used to give access to the row that triggered the command. 
in .net 2.0, it isn't as obvious as that.  Fritz posted an excellent discussion on the matter, and i'm just posting his 2-line solution here for reference.

If your button is a ButtonField, then you can access the index of the item via e.CommandArgument.  This may be enough information to do whatever you need.

If your button is a LinkButton in a TemplateField, you have to add the CommandArgument as an attribute to the LinkButton:
CommandArgument='<%# Eval("id") %>'
Then in the code behind for RowCommand, you can use the following syntax:
string id = (string)e.CommandArgument;
thanks Fritz!

Tuesday, 20 December 2005 17:52:22 (GMT Standard Time, UTC+00:00)  #    Comments [0]  Asp.Net

# Friday, 16 December 2005
GridView with ObjectDataSource, Delete parameters not working...

i'm just experimenting with the gridview control, and i got stuck trying to delete an object with the auto-included delete button.  the delete method would get invoked properly, but the ID parameter was always zero, which means the int parameter was uninitialised.

my problem was that i didn't have the DataKeyNames property set in the Gridview. I just set it to 'ID' and it worked fine then.


Friday, 16 December 2005 14:21:53 (GMT Standard Time, UTC+00:00)  #    Comments [18]  Asp.Net

# Thursday, 15 December 2005
Usability problems with 'directory listing' in .Net 2.0

Microsoft love small fixed fonts for some reason

Correction: this only applies to the Asp.Net development server (v8), so it's not all that important...

i noticed if you browse a folder on a .net 2 virtual directory, you get the directory listing.  but the font styles are set to 8 point verdana which is uncomfortably small for some users on some resolutions.  the fact that they hard-coded it in the css styles means it ignores the browser font-size setting, so it breaks a very important accessibility guideline.  

this is just another reason to use firefox which lets you increase font sizes for fixed sizes. surely MS would be trying to hang on to their user population who still uses IE.  
i looked in the source of the directory listing, and i see they have the file and folder info formatted inside <pre> tags, keeping the tabs aligned nicely.  but i took a copy of the source and removed the font sizes, and the alignment is still fine when you change the browser font size!

come on MS... why the love affair with "px" and "pt" for font sizes?  
i remember gotdotnet.com had fixed sized fonts also.  it's a silly decision in my book, let your users control the font size.  

i've just been experimenting with VS2005 and i like the improved support for xhtml and accessibility, but lots of people actually use directory listings, so why not make them accessible also?


Thursday, 15 December 2005 17:18:34 (GMT Standard Time, UTC+00:00)  #    Comments [0]  Asp.Net

Serializing an object in Xml, with .Net 2.0
i was experimenting with how .Net 2.0 does xml serialization of objects, and i got it serializing nicely with the following code. 
XmlSerializer xs = new XmlSerializer(typeof(PageCollection));
xs.Serialize(fs, pages); // 'fs' is a FileStream to my xml file, and 'pages' is a collection class of objects

the problem was when i tried to deserialize it, like so:
XmlSerializer xs = new XmlSerializer(typeof(PageCollection));
pages = xs.Deserialize(fs) as PageCollection;

i got this error:

xmlns=''> was not expected

i found this post on google which described the same errors, and a workaround (by adding an empty namespace) but it didn't work for me.  perhaps it is a difference in the serialization process with with .Net 2.0.  what fixed it for me was setting an XmlRootAttribute for the class i was serializing. like so:
[XmlRootAttribute("CmsPages", Namespace = "http://www.whatever.com/Cms", IsNullable = false)]
hope this helps someone else out there with the same problem.
Thursday, 15 December 2005 13:28:01 (GMT Standard Time, UTC+00:00)  #    Comments [0]  .Net General | Asp.Net | Database

# Sunday, 11 December 2005
How Not to Develop Software

Usability quote of the day

i'm doing some contract work developing a content management system for a software/design company, and the boss doesn't think it's a good idea to involve the client at all in the process, basically they want a localizable CMS with built-in publishing / approval process, and they are going to be given something along those lines (which i'm hired to develop) and be told how to use it.  That's about as far as the communication goes with the client.  this is bad enough, but yesterday there was a certain design decision we had to make that would seriously affect the client and how they can use the system, and i suggested asking the client for their opinion...

"  oh jeez no!  "

this was the response from the boss of this un-named company, "don't talk to them until the thing is finished ".  i had to hold back the laughter at such a dim view of developing software.  i have a distinct feeling the client would like it done differently but what can you do!  i should buy this guy a ticket to a jakob nielsen conference on usability...


Sunday, 11 December 2005 16:28:44 (GMT Standard Time, UTC+00:00)  #    Comments [1]  General

# Friday, 09 December 2005
Micrsoft Word: System Call Failure
I laughed out loud when this message popped up, typical of Microsoft - the inventor of useful error messages. 
i was in outlook, using word as the email editor, and it wouldn't save draft or send the message at all.  i had to kill the winword and outlook processes and restart outlook. luckily it let me copy/paste into notepad so i didn't lose anything.

no idea what caused it.


Friday, 09 December 2005 19:31:13 (GMT Standard Time, UTC+00:00)  #    Comments [0]  General

# Friday, 02 December 2005
ADO.NET timeouts when filling a DataAdapter in the middle of a transaction

I had a transaction comprised of about 5 commands, and in the middle of it, i needed to do a SELECT on a table to know what values to insert for one of the commands.  I encountered a timeout just after i called Fill on the DataAdapter.  it took me a while to figure out that it was because the transaction had already taken out a lock on the table i was selecting from.  makes sense now, but i spent ages on it!  just posting it here in case anyone else runs into the same problem.


Friday, 02 December 2005 12:26:22 (GMT Standard Time, UTC+00:00)  #    Comments [0]  .Net General | .Net Windows Forms | Asp.Net | Database

# Wednesday, 23 November 2005
Clean Word HTML using Regular Expressions

Introduction

I've spent a long time trying many different approaches at getting rid of MS Word HTML, when importing or pasting text into my content management system, with very mixed success.  Previous efforts involved using the MSHTML Element Dom but this was slow and difficult to implement.  i think i've finally found a satisfactory and fast solution using only regular expressions.  Please feel free to use it in your applications, and post any improvements you may find.

The Code

/// <summary>
/// Removes all FONT and SPAN tags, and all Class and Style attributes.
/// Designed to get rid of non-standard Microsoft Word HTML tags.
/// </summary>
private string CleanHtml(string html)
{
// start by completely removing all unwanted tags
html = Regex.Replace(html, @"<[/]?(font|span|xml|del|ins|[ovwxp]:\w+)[^>]*?>", "", RegexOptions.IgnoreCase);
// then run another pass over the html (twice), removing unwanted attributes
html = Regex.Replace(html, @"<([^>]*)(?:class|lang|style|size|face|[ovwxp]:\w+)=(?:'[^']*'|""[^""]*""|[^\s>]+)([^>]*)>","<$1$2>", RegexOptions.IgnoreCase);
html = Regex.Replace(html, @"<([^>]*)(?:class|lang|style|size|face|[ovwxp]:\w+)=(?:'[^']*'|""[^""]*""|[^\s>]+)([^>]*)>","<$1$2>", RegexOptions.IgnoreCase);
return html;
}

Samples of non-standard Microsoft Word HTML

<SPAN lang=EN-IE style="mso-ansi-language: EN-IE">
<p class="MSO Normal">
<UL style="MARGIN-TOP: 0cm" type=circle>
<o:p>&nbsp;</o:p>
<li class=MsoNormal style='mso-list:l3 level1 lfo3;tab-stops:list 36.0pt'>

Explanation of Regular Expressions

I've spent a good deal of time examining the problematic tags that MS Word inserts in its HTML, some examples are shown above.  The above code is based on a few requirements for my CMS:

  • remove all FONT and SPAN tags, because all the content in my CMS is done through style-sheets.
  • remove all CLASS and STYLE tags because they mean nothing outside of the original word document
  • remove all namespace tags and attributes like <o:p> and < ... v:shape ... >

The first regular expression removes unwanted tags, and is broken down as follows:

<[/]?(font|span|xml|del|ins|[ovwxp]:\w+)[^>]*?>
  • match an open tag character <
  • and optionally match a close tag sequence </  (because we also want to remove the closing tags)
  • match any of the list of unwanted tags: font,span,xml,del,ins
  • a pattern is given to match any of the namespace tags, anything beginning with o,v,w,x,p, followed by a : followed by another word
  • match any attributes as far as the closing tag character >
  • the replace string for this regex is "", which will completely remove the instances of any matching tags.
  • note that we are not removing anything between the tags, just the tags themselves

The second regular expression removes unwanted attributes, and is broken down as follows:

<([^>]*)(?:class|lang|style|size|face|[ovwxp]:\w+)=(?:'[^']*'|""[^""]*""|[^\s>]+)([^>]*)>
  • match an open tag character <
  • capture any text before the unwanted attribute (This is $1 in the replace expression)
  • match (but don't capture) any of the unwanted attributes: class, lang, style, size, face, o:p, v:shape etc.
  • there should always be an = character after the attribute name
  • match the value of the attribute by identifying the delimiters. these can be single quotes, or double quotes, or no quotes at all.
  • for single quotes, the pattern is: ' followed by anything but a ' followed by a '
  • similarly for double quotes. 
  • for a non-delimited attribute value, i specify the pattern as anything except the closing tag character >
  • lastly, capture whatever comes after the unwanted attribute in ([^>]*)
  • the replacement string <$1$2> reconstructs the tag without the unwanted attribute found in the middle.
  • note: this only removes one occurence of an unwanted attribute, this is why i run the same regex twice.  For example, take the html fragment: <p class="MSO Normal" style="Margin-TOP:3em"> 
    the regex will only remove one of these attributes.  Running the regex twice will remove the second one.  I can't think of any reasonable cases where it would need to be run more than that.

Suggestions!

If you have any suggestions or improvments, please post them here as comments.
Thanks :)

p.s. thanks to BinBin for the fix to preserve attributes like 'align=center'.
Wednesday, 23 November 2005 15:40:36 (GMT Standard Time, UTC+00:00)  #    Comments [27]  .Net General

# Friday, 11 November 2005
Asp.Net generally useful datagrid code

i'm just posting this here for reference.  it is a datagrid that supports standard paging and sorting, and displays the current set of record indices e.g. "1-15 of 1000 records"

private void bindGrid()
{
    DataSet ds = new DB.Audits().SelectAllPendingAudits();

    // the sorting is always retrieved from viewstate, if it exists or not.
    string sort = String.Concat(ViewState["Sort"], "");
    if(sort != "")
        this.lblSort.Text = "Sorted by " + sort + " in ascending order";

    int numRows = ds.Tables[0].Rows.Count;
    if(numRows > 0)
    {
        int start = this.DataGrid1.CurrentPageIndex * this.DataGrid1.PageSize + 1;
        int end = Math.Min(numRows, start + this.DataGrid1.PageSize - 1);
        this.lblHeading.Text = String.Format("Displaying {0}-{1} of {2} records", start, end, numRows);
        this.DataGrid1.AllowPaging = (numRows > this.DataGrid1.PageSize);    // don't show pager unless relevant
        this.DataGrid1.Visible = true;
        DataView dv = new DataView(ds.Tables[0]);
        dv.Sort = sort;
        this.DataGrid1.DataSource = dv;
        this.DataGrid1.DataBind();
    }
    else
    {
        this.lblHeading.Text = "No records";            
        this.DataGrid1.Visible = false;
    }
}

private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
    if(e.CommandName == "Sort")
    {
        ViewState["Sort"] = e.CommandArgument.ToString();    // is picked up in bindGrid() function
        this.DataGrid1.CurrentPageIndex = 0;
        this.bindGrid();                
    }            
}

private void DataGrid1_PageIndexChanged(object source, System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
    this.DataGrid1.CurrentPageIndex = e.NewPageIndex;
    bindGrid();
}

Friday, 11 November 2005 11:58:32 (GMT Standard Time, UTC+00:00)  #    Comments [0]  Asp.Net