Monday, 15 June 2009

Dynamically generated Visual Studio Reports

Although I was able to get reports generated using the ReportViewer I soon found that I needed the layout of these reports to be dynamically generated whether they were for a single record showing each required field on its own line or a group of associated records in a table format (but again showing only the required fields).

XSL/XSLT

I soon across Dan Shipe's blog C# Shooter with both C# and XSLT code to help the dynamic generation of reports, however this was geared towards web generated reports (WebForms) and I needed it for Windows Application (WinForms).
Then I came across an answer of StackFlow by MusiGenesis (which referred to Dan's blog) and this proved to be what I needed. The code was provided me with the basis of what I needed (but still using Dan's XSL file).
From this I was able to start dynamically generating reports.

Passing simple Parameters to XSL

Although the Dataset was being Transformed with the XSL file I needed other data to be added.
I found somewhere about the XsltArgumentList class which would allow for the adding of parameters which would then be passed through the Transform process

xform.Transform(xmlDomSchema, xslArgs, writer);

Passing Array Parameters to XSL

After many attempts I figured out that the passing of an array was unlikely through the normal method and fortunately found Oleg Tkachenko's answer on the Bytes.com website.
Here Oleg detailed the requirement 'to build XmlDocument or XPathDocument, then ask it to create XPathNavigator, select nodelist of values and pass it.'
I soon implemented his code and after many attempts I began to understand what was happening and soon tweaked it so I could it a Dictionary list (thus giving me the key name of the field and the value of the column width - which would be summed and then ratioed and appended with 'cm').

public partial class frmReport_General_groups : Form
{
private DataSet ds;
private XsltArgumentList xslArgs;
private const decimal REPORT_WIDTH = 17.75m;

public frmReport_General_groups(DataSet ds, int customerID, string customerName,
string reportName, Dictionary<string,int> columnWidth)
{
InitializeComponent();
this.ds = ds;
reportName += "s";
this.Text += reportName + " report : " + customerName +
" (" + customerID.ToString().PadLeft(6, '0') + ")";
xslArgs = new XsltArgumentList();
xslArgs.AddParam("reportWidth", "", REPORT_WIDTH.ToString()+"cm");
xslArgs.AddParam("reportName", "", reportName);
xslArgs.AddParam("customerName", "", customerName);
xslArgs.AddParam("customerID", "", customerID);

//create XPathNavigator, select nodelist of values and pass it.
StringWriter sw = new StringWriter();
XmlTextWriter w = new XmlTextWriter(sw);
w.WriteStartElement("root");
int totalWidth = 0;
foreach (KeyValuePair<string,> pair in columnWidth)
{
totalWidth += pair.Value;
}
foreach (KeyValuePair<string,int> pair in columnWidth)
{
w.WriteElementString(pair.Key,
(REPORT_WIDTH * ((decimal)pair.Value / (decimal)totalWidth)).ToString("n2") +
"cm");
}
w.WriteEndElement();
w.Close();
//XmlNodeWriter should be used instead of temporary string
XPathDocument doc = new XPathDocument(new StringReader(sw.ToString()));
XPathNavigator navColumns = doc.CreateNavigator();
xslArgs.AddParam("navColumns", "", navColumns.Select("/root/*"));
}

private void frmReport_General_groups_Load(object sender, EventArgs e)
{
string AppPath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);

Stream rdlc = RdlcEngine.BuildRDLCStream(
ds, "general_groups", AppPath + "\\general_groups.xslt", xslArgs);

reportViewer1.LocalReport.LoadReportDefinition(rdlc);
reportViewer1.LocalReport.DataSources.Clear();
reportViewer1.LocalReport.DataSources.Add(
new ReportDataSource(ds.DataSetName, ds.Tables[0]));
reportViewer1.RefreshReport();
}
}



public static Stream BuildRDLCStream(
DataSet data, string name, string reportXslPath, XsltArgumentList xslArgs)
{
using (MemoryStream schemaStream = new MemoryStream())
{
// save the schema to a stream
data.WriteXmlSchema(schemaStream);
schemaStream.Seek(0, SeekOrigin.Begin);

// load it into a Document and set the Name variable
XmlDocument xmlDomSchema = new XmlDocument();
xmlDomSchema.Load(schemaStream);
xmlDomSchema.DocumentElement.SetAttribute("Name", data.DataSetName);

// load the report's XSL file (that's the magic)
XslCompiledTransform xform = new XslCompiledTransform();
xform.Load(reportXslPath);

// do the transform
MemoryStream rdlcStream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(rdlcStream);
if(xslArgs !=null) xform.Transform(xmlDomSchema, xslArgs, writer);
else xform.Transform(xmlDomSchema, writer);
writer.Close();
rdlcStream.Seek(0, SeekOrigin.Begin);

// send back the RDLC
return rdlcStream;
}
}

I found that I needed to remove the TableColumn template call

<xsl:apply-templates select="xs:element" mode="TableColumn">
</xsl:apply-templates>

and instead put in it's place (the variable $navColumns being a parameter that had been passed)

<xsl:for-each select="$navColumns">
<TableColumn>
<Width>
<xsl:value-of select=".">
</Width>
</TableColumn>
</xsl:for-each>

Alias field names

The names being used for the Header titles were that of the field/column names which don't have spaces and thus look very poorly formatted. Finding the SQL doesn't like aliased names with spaces I decided to format spaces (based on a Field Alias column I already had that was loaded into the class record along with all the other field details) to underscores and hyphens to astericks and then when the XSL file was 'used' it would translate these back into spaces and hyphens accordingly thus giving me a workaround solution.


Richard
Visionscape

Thursday, 28 May 2009

Merchant Shipping spotting/sighting software in development

A chance meeting in Glastonbury with a friend (John) who I hadn't seen for a number of years has lead me to starting the development of spotting software - which was something I had on my mind for a few months as a good projects to start using my new C#/.NET development skills.

We meet up a few days ago and discussed the many spotting interests he has (birds, planes, ships, trains, etc) and birds and merchant ships came to the fore and soon it was decided to begin with Merchant Ships.

Enthused by the wealth of information and knowledge of John had, I was soon planning the general look and structure of the application.

Now a couple of days later (and another meeting showing John my mock up) I'll start coding the application.


Richard
Visionscape

Kiwee - Fatal error: Call to a member function getChunk()

Installed Kiwee v1.0.7b onto the beginning of a new website (using MODx cms) and soon found that when I clicked on the tabs of some of the management sections under KiweeCommerce that I was getting the following error:

Fatal error: Call to a member function getChunk() on a non-object in C:\wamp\www\countryvintage\assets\
snippets\shoppingCart\eCart\include\eValues.inc.php

on line 240


I found that the line was trying to get a Chunk called 'gDiscountTplx' which was not installed under the HTMLSnippets section. Hunting through the 'installconfig.inc' file in the installation folder for HTMLSnippets I soon found that the problem was that although the entry existed the following (and last) Tpl file 'gCouponTplx' was incorrectly using the same index number for the $html array thus it was overwriting the details for 'gDiscountTplx' and therefore it was never to be installed.

This has been fixed for the any new release (which should be called v1.0.8)


Richard
Visionscape

KiweeCommerce - a renewed start after a long break

After a break from doing any development on Kiwee (KiweeCommerce - a Ecommerce module for the MODx CMS Framework) I am now making a renewed start (though it may be filled with breaks as I am using the development of an ecommerce website, for which I'm doing as a favour, as a means to check through what I have added to Kiwee and what other bugs still need ironing out and features to smooth out and maybe introduce).

I will attempt to comment on the progress of this work whether its solving bugs/issues or changing things or adding things (features, etc) through this blog and will do my best to respond to any queries and comments posted.


Richard
Visionscape

Friday, 22 May 2009

Spambot KIller ASP.NET

It had got to the point in developing my first ASP.Net website (for my first C#/.Net application - Accommodation Booking System) that I needed to explore a good way of 'encoding' any email addresses on the website. I had seen this mentioned from time to time over the many years but it wasn't until looking for something else did I feel that now was the time to see what other people were doing/suggesting.

ASP.NET Hyperlink Control

I soon found an article by Peter Bromberg called: Spambot Killer ASP.NET Mailto: Hyperlink Control where in Peter goes through some custom code that overrides certain Hyperlink functions and "... convert everything into it's HTML Entity representation." thus making it difficult for spambots to recognize as an email address.

Registering the Custom Control

Not having had much experience of Custom Controls I was suddenly finding it difficult to figure out how to Register this new feature.

Soon I found that the 'web.config' file could hold details of this and thus also making it globally available to the site, so I added the code under the 'controls' section

<add tagprefix="pab" namespace="PAB.WebControls"></add>

and sure enough now my page that was trying to use it was no longer complaining and I could remove my attempt at trying to Register it before the HTML code.

<pab:emaillink id="supportHyperLink" runat="server" tooltip="Support email address">

As you will notice I have missed out the NavigateURL attribute as well as Text. I have done this so I could add these in the code-behind file

supportEmail = ConfigurationSettings.AppSettings["SupportEmail"];
supportHyperLink.Text = supportEmail;
supportHyperLink.NavigateUrl = "mailto:" + supportEmail;

The supportEmail variable is a public string that I declared in the Class. This has the value of a AppSetting called 'SupportEmail' from the web.config file and then this value is attributed to Text and NavigateURL (prefixed with 'mailto:').

<pab:emailLink ID="supportHyperLink" runat="server" ToolTip="Support email address" / >

Adding/Changing the EmailLink Class

I noticed (of course) that the Text generated (which in my case is also the email address) was kept as plain text and although the function had done the job of obfuscating/encoding the email address as part of the hyperlink I calso needed the same process to happen to the Text attribute otherwise I would expect it would be all for nothing. I decided to change the else if relating to 'mailto:' code block under the Render function

link.NavigateUrl = HtmlObfuscate(link.NavigateUrl);
writer.WriteAttribute("href", link.NavigateUrl);
if(Text.IndexOf('@') > -1) Text = HtmlObfuscate(Text);

thus if the Text attribute is identified as having an '@' symbol in it it should be obfuscated as there is a good chance it is an email address.

In addition to this I added

if (!string.IsNullOrEmpty(link.ToolTip))
{
if (link.ToolTip.IndexOf('@') > -1) link.ToolTip = HtmlObfuscate(link.ToolTip);
writer.WriteAttribute("title", link.ToolTip);
}

again to make sure if it appeared to be an email address within the ToolTip (if there it wasn't null or empty) that it was obfuscated.


Richard
Visionscape

Friday, 1 May 2009

Installing and Running VS2008 developed application with Vista's UAC enabled

The problems with UAC

After testing my new application developed in Visual Studio 2008 (with C#) and seeing it working on both my development machine (running Vista) and my laptop with XP I thought I had covered the basics of the main Windows OSes but then when testing it on another Vista machine I realised I had problems, and that problem was User Access Control (UAC).
This hadn't been an issue on the development machine as I had UAC turned off (it bugged me too much - can't wait to see what Windows 7 various levels are like). So it was back to searching the net for help.
After searching on the errors that appeared as soon as the application was trying to write to the registry or write to the database (lack of permissions eg. 'Access to the database file is not allowed. [File name = ....'), I found mention of changing their permissions to overcome the errors. However, this didn't seem to work.
I decided to do some proper testing on my development machine with UAC enabled and soon found an issue with a Cryptography process

rsa = new RSACryptoServiceProvider(cspParams);

saying that the object already existed - this was possibly an issue, again, with UAC.
More searches and then I seemed to start getting somewhere.

Application Manifest file

First I came across:
VS2008: Embedding UAC Manifest Optionsthen

Enabling Your Application for UACthis latter article was just what I was looking for and (though I created the manifest per the former article). But like always another issue.


ClickOnce doesn't like 'requireAdministrator'

I had added my Application Manifest File (app.manifest), as suggested.
Changed the 'requestedExecutionLevel' to make 'level=requireAdministrator', but now when I tried to build my application I was getting a critical error complaining about this new setting.
Well then I need to disable ClickOnce as I'm deploying my application with Inno Setup - how do I disabled ClickOnce?

Disabling ClickOnce

I found the following thread on MSDN
How to remove "clickOnce" from an applicationwhere TaylorMitchell advises to check both the Signing and Security tabs of the project's properties - there may have been one or two other pointers in the thread so if those two don't switch ClickOnce off read through the rest.

Now with ClickOnce disabled I was able to continue with the previous article (Enabling Your Application for UAC) and soon I had my application with the Vista security shield showing.

After processing my application files as a setup file with my Inno configuration I found my last hurdle with UAC (well I hope it is :)).

Create Process failed; code 740

After the setup process came to an end and it then tried to launch my application the following error message showed

Unable to execute file c:\ .....

Create Process failed; code 740.

The requested operation requires elevation.


After more Googlings I soon found that the answer was down to a near simple running the setup
program as an Administrator (right-clicking on it and selecting 'Run as administrator').

This worked. The setup program completed as expected and then launched my application. The application ran without the errors that showed in relation to writing to the database or the registry - registry entries are being written using
Cryptography process on every use.


Richard
Visionscape

Thursday, 16 April 2009

Inno Setup - a simple, free setup program

Another first when developing my C# application was considering how I was going to publish it.

Although there is a Publish tool in Visual Studio/C# and it seemed simple (with lots of extras - some of which I knew I would have to read up on) there was one issue I seemed to find hard to get around and much Googling seemed to show that other people were saying the same thing.

Inclusion of .NET download feature

Yes, how do you include this. There was talk about using Visual Studio (2008) Setup and Depolyment Templates - I followed one or two tutorials and soon gave up when I didn't seem to have any idea of where you could (or how you could) add the ability for the system not only to download the required .NET Framework but to check the users system to see if existed and if so then to bypass the download.

Several hours of searching online and reading peoples comments and thinking I was almost there I came to the conclusion that there seemed to be one Setup application that was a front runner when I found details (code) that would allow the users system to be checked before consdiering whether the .NET download was required.

Inno Setup

Inno Setup (Jordan Russell's Software) was the one that appeared to be the answer. Not only was it getting sufficient praise/recommendation but it appeared to be both not overly difficult to program but also because of its program nature had the ability to offer good download conditioning.

I had soon installed Inno Setup Compiler (v 5.2.3) and was beginning my education in its way of setting up the process of the Setup program through it's INI structure.

The numerous examples and those found online plus the help documentation allowed me to get started and soon I had created a Setup app that allowed for the checking and then (if required) the downloading/installing of .NET 3.5 framework.

Then I added to this the need to check for and install the following
  • SQL Server Compact Edition 3.5 SP1 (file: SSCERuntime-ENU-x86.msi)
  • Report Viewer 2008 SP1 (file: reportviewer.exe)
  • MS Office 2003 Primary Interop Assemblies (file: O2003PIA.MSI)
With these last three I couldn't figure out from the MS website whether they should be downloaded (which is stated by them and other people in relation to the .NET framework) and as such I have included them as files which are installed (again if certain Registry enteries are missing - the latter one I had to work out as I couldn't find it in my searches on the internet).

For a FREE piece of software and with what was available (examples and other peoples work plus the supporting documentation) the Inno Setup application is just perfect to allow me to get started 'delivering' my first proper application.

The ISS file.


Richard
Visionscape

Dr. Explain not capturing all fields - problem fixed

In the initial stages of using Dr. Explain I was 'blown away' by the way it itemised all the fields (with an exception of a few fields), I soon found myself finding that I was having problems with almost no fields being captured.

I realised that the screen captured before had little or no TextBox fields and this seemed to be the issue. After much playing around with the layout of the Windows Form to see if that was the issue I decided it was time to ask for help to see if there was something I could change in Dr. Explain or the layout of my application to fix the issue (I was certianly hoping it would be the former).

After submitting my issue to Dr. Explain I very soon (within 24hrs) had a reply from Dennis (Dr. Explain Team) asking if h could see the 'offending' application.

after a few days spent by me stripping the application down to just one of the 'offending' forms I emailed it off to Dennis (Friday) and (on my Sunday) received a reply with a solution to my issue.

"The easiest way to overcome the issue is to capture your GUI in Win32 mode. Simply unselect the third check box (Accessible objects) in 'Capture an object' dialog. So, all your editbox will be recognized."

And sure enough this resolved my issue.

It's always good to know that not only when you buy a product like this you are getting a very useful tool that helps simply and speed up a process that could be so terrible in the time and complexity, but that there are people (like Dennis) who offer such speedy and helpful support (thanks again Dennis).


Richard
Visionscape

Sunday, 29 March 2009

Adding Help to Visual Studio 2008 application with Dr.Explain

Feeling that, although not fully completed, it was about time to move from the core development of my first C# application (Accommodation Management System) I found that there were a number of areas I needed to investigate/learn - one of these was creating the Help feature of the application.

I trawled through the Net, like you do, trying to find a glimmer of guidance on what you use, where you start from, tutorials, anything and everything with relation to Visual Studio (preferably 2008) and Help files.

I eventually turned up with the usual MSDN pages and other sites with their wares/apps.

Firstly I tried the MS tutorials (in conjunction with Innovasys Help Studio Lite) and although HelpStudio Lite potentially seemed very impressive I felt somewhat despondent by the way that the help files had to be 'integrated' with the Visual Studio project - several command line entries and additional code that to me as a recent beginner of C# made me reel - there had to be a better way even if it meant not having something that wasn't quite so fancy as MS Help 2.

Soon I was surfing and reading more entries but still it was very hard (if not impossible) to find anything that shouted Visual Studio and Help files/generator (or not at least as I was looking for). Soon I decided that I had trawled enough and I have found a select few possible apps to help and read through them and downloaded their software to trial. It wasn't long before I began to realise which appeared to be offering me hope.

Dr. Explain
Although there was some information I found it to scarce for me to again understand whether this would do what I wanted - or probably more likely would I be able to understand what I would need to do with it for it to give me the end result (a simple help generator that I could 'link' to my project/app).
Well like any good new experience I've had my moments of frustration not being able to understand what certain things did and what I had to do through Visual Studio but since installing it late last night (less than 24 hours ago) I have had my moments of feeling wonder and bliss at finding something that I have been able to use that meets my requirements (yes its quite possible that I am not using it to its full extent but I feel that I have a good base in which to explore and get more out of it).

How does it work with Visual Studio 2008?
... I can almost hear you say (well I certainly wish I could have heard someone say for me several days ago).
I was fortunate to come across some interesting posts

Dr.Explain Mini-Review a 'mini' review by tinjaw on the DonantionCode.com website.

Help for controls with VB .NET from the Help Information website

The first inspired me and the latter with its screenshots/examples gave me some idea of what I should be looking at and even doing.

My steps (in brief)
  1. When creating your help file (CHM) with Dr. Explain ensure that all the objects that you want to reference from your project/app has an Anchor string - this is available when you click on the object and is under the Properties section just below the Title property).
  2. Save your CHM file by clicking on the Generate CHM icon (the second/middle icon with the green up arrow on the toolbar).
  3. Ensure that you have the correct path for the HTML Help Workshop folder (otherwise download/install before proceeding).
  4. Click Start export and you will be asked to browse where you want the CHM file to be saved.
  5. In Visual Studio drag a HelpProvider component from the Toolbox onto your form.
  6. Click the on helpProvider1 component(which should be under your form) and open its properties window. Set the HelpNamespace to the location of the CHM file you created.
  7. Select a control on the form which created an Anchor reference for.
  8. Change the setting for 'HelpKeyword on helpProvider1' to the format [Window Topic name].htm#[Anchor name of Control] (eg. 'main_screen.htm#address').
  9. Change the setting for HelpNavigator on helpProvider1' to 'Topic'.
  10. Select the form and change the 'HelpButton' setting to 'True' - this will then show the '?' icon at the top-right of the form.
Run your project and you should be able to select the '?' icon and then click on the control that you changed the Help settings of and the Help file (CHM) should load with the related Help topic (eg. 'main_screen') with the relevant object showing near the top (eg. 'address') - just like a webpage. F1 key also works the same way but will actioned the focused object. As this was problem then to get the form (and thus the main part of the Help Window showing as opposed to the individual objects) I added a Help option in my MenuStrip with the following code for the item when clicked:

private void helpContentsToolStripMenuItem_Click(object sender, EventArgs e)
{
Help.ShowHelp(this, helpProvider1.HelpNamespace, HelpNavigator.Topic, "main_screen.htm");
}

Final thoughts on Dr. Explain
Again its early days but I am very happy to have found Dr. Explain as I have now been able to implement a Help system into my application.
Maybe I'm easily amazed and having not explored the other Help 'generators' in part or full I'm not sure whether it is part of the other packages but when I first tried to add a Window and it launched the capture utility (and had a couple of miss tries - the first locked up and the other was a mouse action that gave me the wrong result) and then presented me with the image I was wowed by it labelling with its objects almost all of the objects from the Form it captured - I do love things like this that make work so much easier and fun.

tinjaw has given a much more in depth look at Dr. Explain and I'm not going to try to do anything like that. I'm very happy (as you probably guessed) but I know at the same time that over time and given time to ponder and compare I could probably could say issues that need addressing but what software doesn't.

Hopefully this has proven usual even if its just for one person.


Richard
Visionscape