Tuesday, 20 September 2016

Could not establish trust relationship for the SSL/TLS secure channel with authority XXXX

Just putting this here as it's come up and I couldn't remember the steps.

If you're spinning up a WCF service for testing in Visual Studio and you get the message "Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost:XXXXX'" then there is a simple 2 minute fix.

Take the URL of the svc file you are trying to access (e.g. https://localhost:XXXXX/MyService.svc) and past it into IE.

IE will prompt you saying the certificate is invalid. Accept this to carry on.

When your auto-generated service page comes up the address bar will be red. Click on the right end of the address bar (depending on the version of IE it may show a little red shield or say "Certificate Error". Click on View Certificates, then Install Certificate when it shows the details.

When to install into your User certificate store. You'll then be asked if you want to install it into the default location or somewhere else. Select the other location option and select "Trusted Root CA Authorities"

Job Done


Thursday, 27 March 2014

Free Training For Life

Free Training For Life - OK, I know I'm being selfish here, but would really appreciate any upvotes to get me this (worth a shot) :D

Wednesday, 13 June 2012

Please support the campaign to save lennox

While I know my readership isn't high I wanted to share the following in the hope that if even one of you signs the petition then we can help push up the numbers to the point where we cannot be ignored.

If you care about animals and are as gobsmacked as me by the groundless adherance to the letter of the unjust Breed Specific Legislation in Belfast then please sign the petition to save lennox - condemned to be destroyed for basically looking like a dog that wouldn't necessarily be dangerous anyway - http://www.petitiononline.com/sl190510/petition.html

http://www.savelennox.co.uk/ for more - please spread the word and try and get this ruling overturned

Tuesday, 12 June 2012

Microsoft conferences online

As this has come up a few times lately I just wanted to post some links up to the online archives for the various Microsoft conferences. I have to say the fact that you can get at these archives permanently, as well as when the conferences are still running is just one of the reasons I love MS's attitude to developer relationships.

The conferences do have different focuses, but these days you will normally find something on most subjects at any of the conferences.

Teched (which is running now, at the time I write this post) - this one is normally quite heavy on IT, but with some interesting dev pieces in there
Build - this one is for the devs, though quite a bit on design/UI as well. This first ran in 2011 to introduce us to Windows 8 / Visual Studio 2012 - http://channel9.msdn.com/events/BUILD/BUILD2011

The Professional Developer Conference (PDC) - the key's in the name - for devs mainly, was the forerunner to Build - Archives from 1991 to 2010 are at http://channel9.msdn.com/Events/PDC

MIX - mainly for designers tho with some dev in there aswell - archives from 2006 to 2011 are at http://channel9.msdn.com/Events/MIX


These are all worth a look through - there are 1000s of hours of the best insight to working with the MS platforms you could possibly hope for.

Enjoy

Sunday, 26 February 2012

Accessing Printer Status using WinSpool in C#

 

After help from ambience I’ve managed to get the code working to access accurate printer status by using DllImport to access Winspool.drv (as opposed to using WMI, which had very mixed results).

There are 3 key functions you need to be aware of:

  • OpenPrinter
  • GetPrinter
  • ClosePrinter



The GetPrinter function is the one that does the real work we are interested in – accessing the PrinterInfo2 structure that gives you all the information you could possibly want on your named printer – driver, status, configuration, etc

Once you have the PrinterInfo2 you can than use bitwise operators along with known constants to find out the current status of the printer. One thing to note is that to detect whether a printer is marked as offline rather than physically offline you need to check the Attributes rather than Status – I’ll put a followup post shortly with details of a Helper class round the PrinterInfo2 status.

Here’s the code to help you out if, like me you need to get accurate printer info in C#:

//Code written by Mark Middlemist - @delradie 
//Made available at http://delradiesdev.blogspot.com
//Interop details from http://pinvoke.net/
using System;
using System.Runtime.InteropServices;

namespace DelradiesDev.PrinterStatus
{
  public class WinSpoolPrinterInfo
  {
    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, ref PRINTER_DEFAULTS pDefault);
    
    [DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel, IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);
    
    [DllImport("winspool.drv", SetLastError = true)]
    public static extern int ClosePrinter(IntPtr hPrinter);
        
    [StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_DEFAULTS
    {
      public IntPtr pDatatype;
      public IntPtr pDevMode;
      public int DesiredAccess;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct PRINTER_INFO_2
    {
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pServerName;      
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pPrinterName;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pShareName;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pPortName;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pDriverName;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pComment;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pLocation;
      
      public IntPtr pDevMode;
      
      [MarshalAs(UnmanagedType.LPTStr)]      
      public string pSepFile;
      
      [MarshalAs(UnmanagedType.LPTStr)]      
      public string pPrintProcessor;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pDatatype;
      
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pParameters;
      
      public IntPtr pSecurityDescriptor;
      public uint Attributes;
      public uint Priority;
      public uint DefaultPriority;
      public uint StartTime;
      public uint UntilTime;
      public uint Status;
      public uint cJobs;
      public uint AveragePPM;
    }
    
    public PRINTER_INFO_2? GetPrinterInfo(String printerName)
    {
      IntPtr pHandle;      
      PRINTER_DEFAULTS defaults = new PRINTER_DEFAULTS();      
      PRINTER_INFO_2? Info2 = null;
      
      OpenPrinter(printerName, out pHandle, ref defaults);
      
      Int32 cbNeeded = 0;
      
      bool bRet = GetPrinter(pHandle, 2, IntPtr.Zero, 0, out cbNeeded);
      
      if (cbNeeded > 0)
      {
        IntPtr pAddr = Marshal.AllocHGlobal((int)cbNeeded);
        
        bRet = GetPrinter(pHandle, 2, pAddr, cbNeeded, out cbNeeded);
        
        if (bRet)        
        {
          Info2 = (PRINTER_INFO_2)Marshal.PtrToStructure(pAddr, typeof(PRINTER_INFO_2));
        }
        
        Marshal.FreeHGlobal(pAddr);
      }
      
      ClosePrinter(pHandle);
      
      return Info2;
    }
  }
} 

Thursday, 23 February 2012

Rebinding bug in WinForms ListBox when SelectionMode = None

Just a quick post to help out anyone who hits the same bug I have.

It appears that there are a couple of issues with WinForms ListBoxes when the SelectionMode is None (certainly in .net 2).

One that is pretty well documented is that if your SelectionMode is None then you can get problems on closing the form (https://connect.microsoft.com/VisualStudio/feedback/details/189700/listbox-selectionmode-none-bug)

One I've just hit on is that if you rebind/refresh a listbox's contents by changing the datasource then the list is cleared and left blank

The workaround is very simple (based on the one from Connect) - before manipulating the DataSource property on the ListBox change the SelectionMode to one of the other values, do your work, then change it back to None

Hope this helps

Wednesday, 3 August 2011

Adding MVC 3 to an asp.net web site

OK, I've been off the blog for a while with things being very busy at work (some interesting changes coming up on public.presalesadvisor.com), but I'm back (for now at least).

I've been starting to tool up on MVC, and while I won't cover that right now as there's no shortage of coverage on that, I wanted to cover one bit that seems slightly lacking in coverage, which is specifically adding MVC 3 to ASP.Net Websites (not web projects).

The most excellent Mr Hanselman has already done the vast majority of the work for anyone wanting to add mvc to asp.net (and in fact pretty much all of it if you're using a web application) with his AddMvc3ToWebForms Nuget Package

There are however a couple more steps needed to get it working with a web site rather than web project

1) Install the nuget package as normal, either by selecting AddMvc3ToWebForms from the online gallery in Visual Studio's "Add Library Package Reference" option (Tools > Library Package Manager > Add Library Package Reference), or by typing the command Install-Package AddMvc3ToWebForms in the Package Manager Console (Tools > Library Package Manager > Package Manager Console)

When I ran this I got an error at the end but as far as I can tell, this isn't actually a problem, but caused by it not having a csproj file to work with:

 PM> Install-Package AddMvc3ToWebForms
'WebActivator (≥ 1.3)' not installed. Attempting to retrieve dependency from source...
Done.
Successfully installed 'WebActivator 1.3.2'.
Successfully installed 'AddMvc3ToWebForms 0.9.1'.
Successfully added 'WebActivator 1.3.2' to extranet.
Successfully added 'AddMvc3ToWebForms 0.9.1' to extranet.
Install-Package : Method invocation failed because [System.__ComObject] doesn't contain a method named 'Add'.
At line:1 char:16
+ Install-Package <<<<  AddMvc3ToWebForms
    + CategoryInfo          : NotSpecified: (:) [Install-Package], RuntimeException
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand


If you try and compile now it will fail, complaing of not being able to find MvcApplication1. This is because the view (cshtml) files it creates reference a standard namespace (MvcApplication1) that doesn't correspond to the ASP namespace in the created model/controller files. There is also the additional issue that in a web site the 'M&C' files should live under App_Code not in the rool directory.

2a) If you aren't interested in the initial files created by the package then you could simply create Models and Controllers directories under App_Code, delete the ones in the site root, and empty out the Views directory, then get on with your own coding

2b) If you do want to keep the Account/Home/Shared files that have been created then move the Models and Controllers files to App_Code

3) To resolve the namespace issue open up ChangePassword.cshtml, LogOn.cshtml, Register.cshtml in Views\Account and remove the 'MvcApplication1.' from the @model reference at the top

You should now be ready to roll - Enjoy

Friday, 11 March 2011

Breaking down 'google' sitemaps

Just a quick follow up on my previous post on 'google' xml sitemaps :

Once you start databinding your sitemaps, especially if you have as much data as we do on presalesadvisor is to break the sitemap down, using a master index map. These look a lot like the normal sitemaps, but uses a sitemapindex top level element in place of the urlset, and sitemap in place of url. Each location (sitemapindex - sitemap - loc) should then point to individual sitemap files which google, etc will then drill down in to.

A good example of where this would be useful is on presalesadvisor, where there is a huge quantity of data being generated in multiple regional sets. When generated all in one big sitemap file the load time is huge, and risking timeout. Instead we set up our objectdatasources with a querystring parameter of the region code (e.g. en-GB, en-US) and then link to these subsets from the main sitemapindex. With this simple change load times and server load are greatly reduced (which is more important these days from the SEO angle with google taking load times in to account in their ranking algorithms), and we have an all round win.

The sitemaps themselves remain exactly the same as before, and the new sitemapindex looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
 <sitemap>
   <loc>http://public.presalesadvisor.com/Sitemap/XmlSiteMap.aspx?regionID=en-US</loc>
  </sitemap>
 <sitemap>
   <loc>http://public.presalesadvisor.com/Sitemap/XmlSiteMap.aspx?regionID=en-GB</loc>
  </sitemap>
</sitemapindex>

In terms of function all you do differently when logged into your google site admin is add the new SiteMapIndex url as your sitemap rather than any of the actual individual sitemaps

Thursday, 20 January 2011

Step by step guide to installing the Surface SDK on 64 bit windows

With the recent announcements and demos for the new Microsoft Surface I've noticed a few people talking about the version 1 sdk, and the fact they can't install it on their x64 Windows 7 boxes, so I thought I'd give a quick bulletpoint guide to how to acheive this.
To get up and running you will need the following:

XNA 2 Redist
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=15fb9169-4a25-4dca-bf40-9c497568f102&displaylang=en

Windows SDK
http://msdn.microsoft.com/en-us/windows/bb980924

Surface SDK 1.0 SP1
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=3db8987b-47c8-46ca-aafb-9c3b36f43bcc&displaylang=en

1) Once you have the above, install the XNA 2 Framework and the Windows SDK

2) Go into the windows sdk bin directory (probably C:\Program Files\Microsoft SDKs\Windows\v7.0A\Bin or similar) and run Orca.msi

3) Run Orca and open the file SurfaceSDKWE.msi
- Locate LaunchConditions in the tables list on the left
- Delete the rows 'Installed OR NOT VersionNT64'and 'Installed OR VISTASKU<>"Starter"'

Also, if you are using Visual Studio 2010 and do not have any installation of Visual Studio 2008 then you will want to also delete the following:
-'Installed OR        (VS2008SPLEVEL AND VS2008SPLEVEL >= "#0") OR        (VCSEXP2008SPLEVEL AND VCSEXP2008SPLEVEL >= "#0")'
-'Installed OR (VS2008SPLEVEL AND VS2008CSPROJSUPPORT) OR VCSEXP2008SPLEVEL'
Save the file and close orca

4) In a command prompt extract the msi file either using 'msiexec /a SurfaceSDKWE.msi /qb TARGETDIR=C:\Temp\SurfaceSDKWEExtract'

5) Open the msi in the extract directory using orca and remove the launch conditions again

5.5 - optional) If for whatever reason you are not using the visual studio command prompy set your machine's PATH environment variable to include the windows sdk bin directory, or alternatively prefix the corflags commands below with the full path

6) Run an administrator level command prompt and navigate to the directory you extracted the msi to, then in to \Microsoft Surface\v1.0\ and run
 'corflags setupcustomaction.exe /32bit+ /force'

7) Run the installer you have just modified

8) Using the command prompt navigate through the install directory and its children and run the corflags command against each of the executables

You should now be able to run the demos etc, but if you want to build and run the code samples yourself you will need to ensure that the project settings are targetting x86 CPUs explicitly

9) HAVE FUN

Tuesday, 18 January 2011

It takes all sorts

Just to show that there are all sorts in the industry I just thought I'd share one link I came across the other day (and no, I was not searching for it) - http://www.ibm-m4.com/

It really does take all sorts

Sunday, 5 December 2010

Twitter

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

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.

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

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:


  • 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)
All you need to do then is, within the render method get the correct  cell (of type DataControlFieldCell) within the Cells collection of the base GridViewRow, using the passed index.


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 :)

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

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:

  • 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/