General postings on bits and pieces of ASP.net and other Microsoft development that may be of use out there
Sunday 5 December 2010
Just to let you know, for updates on the blog, and useful links to development tips and tricks as I find them I am on twitter as delradie - http://twitter.com/#!/Delradie
Using Linq to remove duplicate data from a DataTable
In response to a question on experts exchange I just wanted to do a quick post on data cleanup using Linq
Linq has a lot of very useful operations that can be used to reduce the complexity of array (or dataset) operation.
In this instance the operator .Distinct() can be used to remove duplicates in a datatable, with a small priviso:
If you call the method on the datatable (using the call .AsEnumerable() to get it in to a form you can work with in linq) with no parameters you will find that it returns all rows - this is because by default it does an object comparison, and of course all the records have their own memory address, and therefore are considered to be different. The trick is to tell it to use the default DataRowComparer which will get it to do a column by column value check.
One final point to watch out for is in getting the data back in to datatable - remember that datasets/tables/rows are reference types, so clearing down the tabe will actually cause problems, so the trick is to take a copy of the datatable, clear the original, operate on the copy, then reimport the rows to the cleared down table.
All put together, the following function will handle this for you:
/// <summary>/// Removes duplicate rows from the target table/// </summary>/// <param name="sourceDataSet">Dataset containing the table to operate on</param>/// <param name="targetTableName">Name of the table to operate on</param>
{public void GetUniqueRows(DataSet sourceDataSet, String targetTableName) //Check there is data to operate on
//Take a copy of the table so clearing does not affect the operation
//Run the distinct operation
//Clear all data from the target table sourceDataSet.Tables[targetTableName].Clear();
//Re-import the distinct rows to the 'live' table
{
sourceDataSet.Tables[targetTableName].ImportRow(DistinctRow);
}
} foreach (DataRow DistinctRow in OutputRows) DataRow[] OutputRows = (from rows in OperatingData.AsEnumerable() select rows).Distinct(System.Data.DataRowComparer.Default).ToArray() as DataRow[]; DataTable OperatingData = sourceDataSet.Tables[targetTableName].Copy(); if (sourceDataSet.Tables[targetTableName].Rows.Count == 0) return;
Friday 3 December 2010
If you've found any of this information useful
I know that everyone is pretty much charitied out, but if you've found any of the information presented here useful, or just have a good heart, please support a cause that is very dear to me:
Happy Staffie Rescue (http://www.happystaffierescue.org.uk/), based in the west midlands, are a breed rescue for Staffordshire Bull Terriers - a much maligned but very loving breed (we have 3 of our own).
If you can spare even a small amount this would help make a big difference for some wonderful dogs that really do deserve much better than the hands that life has dealt them.
You can donate online via http://www.happystaffierescue.org.uk/id123.html
Merry Christmas
Happy Staffie Rescue (http://www.happystaffierescue.org.uk/), based in the west midlands, are a breed rescue for Staffordshire Bull Terriers - a much maligned but very loving breed (we have 3 of our own).
If you can spare even a small amount this would help make a big difference for some wonderful dogs that really do deserve much better than the hands that life has dealt them.
You can donate online via http://www.happystaffierescue.org.uk/id123.html
Merry Christmas
Monday 29 November 2010
Vertical GridViews in ASP.net - Part 2c: Plumbing in the custom rendering
Now, as promised, how to plumb in the custom rendering code for the cell level rendering:
The key function you will need to work in is an override to the RenderChildren function in your inherited gridview (protected override void RenderChildren(HtmlTextWriter writer)).
First off, you need to create a dictionary of your custom row renderers, one for each row in the controls Rows collection (of type GridViewRowCollection), indexed by the original row id (or a list of them in the same order as the original collection. As set up previously, the 'row renderer' is constructed by passing in the gridviewrow.
As you're overriding the child controls the one drawback is that you have to manually set up that table, using the htmlwriter to output the various rendering parameters:
if(this.BorderWidth != null)
writer.AddAttribute(HtmlTextWriterAttribute.Border, this.BorderWidth.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, this.CellSpacing.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, this.CellPadding.ToString());
if (this.CssClass != null)
writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Table);
then the important bit - for the moment we'll go with the simplified case that you are displaying all your datarows in one long row (I'll cover wrapping in a future post).
Firstly, iterate over the columns in the GridView's Columns collection (of type DataControlFieldCollection) - there is one entry in this collection for every field (BoundField,TemplateField, etc.) specified in the markup using the gridview.
Within this loop you then need to:
- Tell the html writer to write out the <TR> tag:
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
- Loop over the rows in renderer list/dictionary, and within that call your renderer's custom cell Render Method:
Renderers[RowIndex].Render(writer, ColumnIndex, IsHeader);
- Tell the htmlwriter to write out the closing </TR> tag:
writer.RenderEndTag();
After the loops you then need to tell the html writer to write the closing </TABLE> tag with a final call to writer.RenderEndTag()
With the code detailed about you now have the full basic solution. I will post more on some additions that you will most likely want - wrapping, empty cell handling, and Custom CSS properties, all of which are used in the featured product grids on http://public.presalesadvisor.com/ - but hopefully this is enough to get you started.
The key function you will need to work in is an override to the RenderChildren function in your inherited gridview (protected override void RenderChildren(HtmlTextWriter writer)).
First off, you need to create a dictionary of your custom row renderers, one for each row in the controls Rows collection (of type GridViewRowCollection), indexed by the original row id (or a list of them in the same order as the original collection. As set up previously, the 'row renderer' is constructed by passing in the gridviewrow.
As you're overriding the child controls the one drawback is that you have to manually set up that table, using the htmlwriter to output the various rendering parameters:
if(this.BorderWidth != null)
writer.AddAttribute(HtmlTextWriterAttribute.Border, this.BorderWidth.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, this.CellSpacing.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, this.CellPadding.ToString());
if (this.CssClass != null)
writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Table);
then the important bit - for the moment we'll go with the simplified case that you are displaying all your datarows in one long row (I'll cover wrapping in a future post).
Firstly, iterate over the columns in the GridView's Columns collection (of type DataControlFieldCollection) - there is one entry in this collection for every field (BoundField,TemplateField, etc.) specified in the markup using the gridview.
Within this loop you then need to:
- Tell the html writer to write out the <TR> tag:
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
- Loop over the rows in renderer list/dictionary, and within that call your renderer's custom cell Render Method:
Renderers[RowIndex].Render(writer, ColumnIndex, IsHeader);
- Tell the htmlwriter to write out the closing </TR> tag:
writer.RenderEndTag();
After the loops you then need to tell the html writer to write the closing </TABLE> tag with a final call to writer.RenderEndTag()
With the code detailed about you now have the full basic solution. I will post more on some additions that you will most likely want - wrapping, empty cell handling, and Custom CSS properties, all of which are used in the featured product grids on http://public.presalesadvisor.com/ - but hopefully this is enough to get you started.
Friday 26 November 2010
Databound google sitemaps with repeaters in asp.net
I thought I'd put out another use for the xml databinding as described in the earlier post on RSS feeds.
The basic principle can be used for anything, but one use we've put it to on the Pre-Sales Advisor site is to generate a databound xml sitemap for google submission (for full details of the format see the official site at http://www.sitemaps.org/).
In most data driven websites the data pages you really want found are displayed by calling a standard page with the correct querystring (e.g. http://public.presalesadvisor.com/Common/BuildByFamily.aspx?identity=2141&dso=en-US for Bladecenter HS22, http://public.presalesadvisor.com/Common/BuildByFamily.aspx?identity=3013&dso=en-US for system x3630 M3s). As long as you can pull out a list of valid identities through a datasource then all you need to do is set up your repeater templates as follows:
<HeaderTemplate>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.sitemaps.org/schemas/sitemap-image/1.1"
xmlns:video="http://www.sitemaps.org/schemas/sitemap-video/1.1">
</HeaderTemplate>
<FooterTemplate>
</urlset>
</FooterTemplate>
<ItemTemplate>
<url>
<loc><%# Server.HtmlEncode(String.Format("http://www.yoursite.com/page.aspx?id={0}", DataBinder.Eval(Container.DataItem, "IDENTITYCOLUMN")))%></loc>
</url>
</ItemTemplate>
and there you have it. For an example of what it comes out like have a look at the xml site map linked from http://public.presalesadvisor.com/sitemap.aspx (incidentally, if you have a look at the global part map that uses basically. the same data sources and repeater setup).
The basic principle can be used for anything, but one use we've put it to on the Pre-Sales Advisor site is to generate a databound xml sitemap for google submission (for full details of the format see the official site at http://www.sitemaps.org/).
In most data driven websites the data pages you really want found are displayed by calling a standard page with the correct querystring (e.g. http://public.presalesadvisor.com/Common/BuildByFamily.aspx?identity=2141&dso=en-US for Bladecenter HS22, http://public.presalesadvisor.com/Common/BuildByFamily.aspx?identity=3013&dso=en-US for system x3630 M3s). As long as you can pull out a list of valid identities through a datasource then all you need to do is set up your repeater templates as follows:
<HeaderTemplate>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.sitemaps.org/schemas/sitemap-image/1.1"
xmlns:video="http://www.sitemaps.org/schemas/sitemap-video/1.1">
</HeaderTemplate>
<FooterTemplate>
</urlset>
</FooterTemplate>
<ItemTemplate>
<url>
<loc><%# Server.HtmlEncode(String.Format("http://www.yoursite.com/page.aspx?id={0}", DataBinder.Eval(Container.DataItem, "IDENTITYCOLUMN")))%></loc>
</url>
</ItemTemplate>
and there you have it. For an example of what it comes out like have a look at the xml site map linked from http://public.presalesadvisor.com/sitemap.aspx (incidentally, if you have a look at the global part map that uses basically. the same data sources and repeater setup).
Thursday 25 November 2010
Vertical GridViews in ASP.net - Part 2b: Cell level rendering a GridViewRow
The key to the solution is the order in which the table cells are rendered out – in order to take over control of this we created a new class to wrap around the GridViewRow so that we could manually call the rendering on the actual cell we required. The structure of the class is quite simple:
If you’re not needing to output the cell as a header (i.e. render as a TH rather than a TD) all you need to do now is call the cell’s RenderControl method, passing in the HtmlTextWriter
If you do need to render as a header cell things aren’t quite as simple – header cells are actually set up during CreateChildControls as a different type - DataControlFieldHeaderCell rather than DataControlFieldCell.
The simplest solution we found was creating a new DataControlFieldHeaderCell and manually copying the properties that we were interesting in preserving from the original cell (in our case CssClass, HorizontalAlign, and VerticalAlign), and then reassign the child controls from the original cell to the new cell:
while (OriginalCell.Controls.Count > 0)
Once this is done call the header cell’s RenderControl as before.
I’ll leave it there for now, but will be post again soon with how to plumb this in to the standard control rendering
- A class variable to store the GridViewRow this is wrapping around
- A constructor that takes in a GridViewRow and assigns it to the class variable
- A render method that takes in a HTMLTextWriter, the index of the cell you want to render within the row, and a flag to indicate if this is to be rendered as a ‘header cell’ (more on the last in a post to follow)
If you’re not needing to output the cell as a header (i.e. render as a TH rather than a TD) all you need to do now is call the cell’s RenderControl method, passing in the HtmlTextWriter
If you do need to render as a header cell things aren’t quite as simple – header cells are actually set up during CreateChildControls as a different type - DataControlFieldHeaderCell rather than DataControlFieldCell.
The simplest solution we found was creating a new DataControlFieldHeaderCell and manually copying the properties that we were interesting in preserving from the original cell (in our case CssClass, HorizontalAlign, and VerticalAlign), and then reassign the child controls from the original cell to the new cell:
while (OriginalCell.Controls.Count > 0)
{
Control Child = OriginalCell.Controls[0];
HeaderCell.Controls.Add(Child);
}
Once this is done call the header cell’s RenderControl as before.
I’ll leave it there for now, but will be post again soon with how to plumb this in to the standard control rendering
Saturday 20 November 2010
Easy guide to databound RSS feeds in Asp.Net
Ok, while I'm making sure the next part on the vertical datagrids makes sense I thought I'd drop in a quick bit on databound RSS feeds in asp.net
I'd like to empasise first off this is in no way a method I can take credit for, but I wanted to pass it along to help out any dev's out there who get the fateful brief "oh, before we put this live can you just stick an RSS feed in"
If you've done any reading then you'll know that RSS (Really Simple Syndication) is based around a specific XML format. The key to simple databound RSS is to use a standard page and standard controls, but writing out XML rather than HTML markup.
To make the page work as a feed you need to do the following:
-Delete out all preentered content except for the @Page directive
-In the @Page directive include the attribute ContentType="text/xml"
-Include a standard datasource that will output the data that you want to publish
-Include an asp repeater with the DataSourceID set to your datasource, and with the following content:
HeaderTemplate:
<rss version="2.0">
<channel>
<title>FEED NAME</title>
<link>URL THAT THE FEED WILL BE PUBLISHED TO</link>
<description>DESCRIPTION OF THE FEED</description>
FooterTemplate:
</channel>
</rss>
ItemTemplate:
<item>
<title><%#DataBinder.Eval(Container.DataItem, "POST TITLE COLUMN")%></title>
<description><%#DataBinder.Eval(Container.DataItem, "POST TEXT SUMMARY COLUMN")%></description>
<link>CLICK-THROUGH URL</link>
<pubdate><%#DataBinder.Eval(Container.DataItem, "ARTICLE TIMESTAMP COLUMN")%></pubdate>
</item>
That's all it takes - Enjoy :)
I'd like to empasise first off this is in no way a method I can take credit for, but I wanted to pass it along to help out any dev's out there who get the fateful brief "oh, before we put this live can you just stick an RSS feed in"
If you've done any reading then you'll know that RSS (Really Simple Syndication) is based around a specific XML format. The key to simple databound RSS is to use a standard page and standard controls, but writing out XML rather than HTML markup.
To make the page work as a feed you need to do the following:
-Delete out all preentered content except for the @Page directive
-In the @Page directive include the attribute ContentType="text/xml"
-Include a standard datasource that will output the data that you want to publish
-Include an asp repeater with the DataSourceID set to your datasource, and with the following content:
HeaderTemplate:
<rss version="2.0">
<channel>
<title>FEED NAME</title>
<link>URL THAT THE FEED WILL BE PUBLISHED TO</link>
<description>DESCRIPTION OF THE FEED</description>
FooterTemplate:
</channel>
</rss>
ItemTemplate:
<item>
<title><%#DataBinder.Eval(Container.DataItem, "POST TITLE COLUMN")%></title>
<description><%#DataBinder.Eval(Container.DataItem, "POST TEXT SUMMARY COLUMN")%></description>
<link>CLICK-THROUGH URL</link>
<pubdate><%#DataBinder.Eval(Container.DataItem, "ARTICLE TIMESTAMP COLUMN")%></pubdate>
</item>
That's all it takes - Enjoy :)
Tuesday 16 November 2010
Vertical GridViews in ASP.net - Part 2a : The general solution
The approach taken was to override the rendering of the standard gridview to give the desired HTML output.
There are 2 main ways you would do this:
1) Create a new control that inherits from GridView and override the methods involved in rendering
or
2) Use a Control Adapter to provide custom rendering
Personally I don't like the idea of method 2 - unless I'm misunderstanding something here it means rely on the browser capability file to apply, and it generally seems that even if we got the required rendering the application of it would be less precise than explicity specifiying which control you are using.
For our custom control - VerticalGridView - the point where we intercept the standard GridView behaviour is function RenderChildren (protected void RenderChildren(HtmlTextWriter writer)).
In a standard GridView this would render to the output Html writer:
- The table header with attributes set based on the properties set on the grid view
- One Table Row (TR) per DataRow in the source data within the parent table (with attributes set based on the properties set on the grid view)
- One Table Cell (TH/TD) per ItemTemplate specified in the markup, rendered within the parent row (with cell type and attributes set based on the properties in the ItemTemplate)
- Closing table tags
Note that this is not a canonical representation of the standard functionality - just detail appropriate to the level that we needed to control for our purposes. For a full view of the code then I'd recommend downloading the free and wonderful RedGate Reflector and disassembling the method in System.Web.UI.WebControls.GridView, and GridViewRow in System.Web.dll
So this is where we rewrite the rules. How exactly we do that to follow shortly
Just to mention to those who don't already know, the vertical grids described here can be seen in action on the front page of http://public.presalesadvisor.com/
Incedentally, just to thank the GiantCodeMonkey himself for the inspiration for the solution, and the permission to write it up
There are 2 main ways you would do this:
1) Create a new control that inherits from GridView and override the methods involved in rendering
or
2) Use a Control Adapter to provide custom rendering
Personally I don't like the idea of method 2 - unless I'm misunderstanding something here it means rely on the browser capability file to apply, and it generally seems that even if we got the required rendering the application of it would be less precise than explicity specifiying which control you are using.
For our custom control - VerticalGridView - the point where we intercept the standard GridView behaviour is function RenderChildren (protected void RenderChildren(HtmlTextWriter writer)).
In a standard GridView this would render to the output Html writer:
- The table header with attributes set based on the properties set on the grid view
- One Table Row (TR) per DataRow in the source data within the parent table (with attributes set based on the properties set on the grid view)
- One Table Cell (TH/TD) per ItemTemplate specified in the markup, rendered within the parent row (with cell type and attributes set based on the properties in the ItemTemplate)
- Closing table tags
Note that this is not a canonical representation of the standard functionality - just detail appropriate to the level that we needed to control for our purposes. For a full view of the code then I'd recommend downloading the free and wonderful RedGate Reflector and disassembling the method in System.Web.UI.WebControls.GridView, and GridViewRow in System.Web.dll
So this is where we rewrite the rules. How exactly we do that to follow shortly
Just to mention to those who don't already know, the vertical grids described here can be seen in action on the front page of http://public.presalesadvisor.com/
Incedentally, just to thank the GiantCodeMonkey himself for the inspiration for the solution, and the permission to write it up
Tuesday 26 October 2010
Vertical GridViews in ASP.net - Part 1 : The problem
We've recently been working on a new website that involved a lot of focus on the presentation and layout of product data. In one section specifically data on featured products is displayed side-by-side, but up until now we've been content to have each one individually rendered as a child control, with each subsection of the display just flowing on with a line break, meaning that the data was not truly aligned. In the new site we wanted to improve on this, rendering out in a 'comparison-style' grid.
The obvious web control for this sort of data presentation would be a gridview - they render out as HTML tables, and a lot of standard event hookup code already existed in the codebase for use elsewhere. Here we hit the problem; gridviews render out sequentially with a table row per data item, we wanted it to have a column per data item, and of course HTML tables have no conept of a 'column' of data.
This meant that we needed to find a way of presenting data that:
The obvious web control for this sort of data presentation would be a gridview - they render out as HTML tables, and a lot of standard event hookup code already existed in the codebase for use elsewhere. Here we hit the problem; gridviews render out sequentially with a table row per data item, we wanted it to have a column per data item, and of course HTML tables have no conept of a 'column' of data.
This meant that we needed to find a way of presenting data that:
- rendered each data item vertically
- aligned each section of data horizontally
- used the standard gridview event model
After a considerable amount of research online the general consensus seemed to be to 'hack' the data source to rotate the dataset - crosstab style - or.... give up. Neither of these were really an options so what I'll try and detail in the following posts is how we got the grids as they now appear on the font page of http://public.presalesadvisor.net/
Intro - The Dev Blog
OK, I do technically have another personal blog (which hasn't been updated in 4 years), but with some of the code we've cut recently I wanted somewhere to share the occasional snippets of code and idea that are a bit too long for my twitter feed (http://twitter.com/delradie).
Hopefully someone out there will find something on here at least a little useful.
To kick off shortly, vertical (product comparison style) gridviews in asp.net as implemented on http://public.presalesadvisor.net/
Hopefully someone out there will find something on here at least a little useful.
To kick off shortly, vertical (product comparison style) gridviews in asp.net as implemented on http://public.presalesadvisor.net/
Subscribe to:
Posts (Atom)