Search This Blog

Saturday, December 29, 2012

RDLC Basics


So less than 6 hrs of time invested and I have basic RDLC reporting working for data displayed in a table / grid.  Also created several multi-column reports.

So here is my basic approach:

My data source is a MS Access MDB file but this would work for SQL server just as easy. Simply a different connection string.

Step #1 - Create a visual dataset with table adapters that can be bound to a report.



Step #2 Create basic RDLC report without using the wizard.

  1.  Add new item, select reporting template, select Report.
  2. In report data panel, select new dataset
  3. Select data source 
  4. Select table adapter
  5. Add table / grid to body of the report.
  6. Assign columns in the table / grid to fields from the tableAdapter field list
  7. Right-click in the report designer below the report body and add page header or page footer as needed.
  8. Add Report name, execution time, and page number from built-in fields
Step #3 Fetch data for report and return it as a data table
Here is  my design approach.  I only use table adapters to bind fields in the designer.  At runtime, I like to manually fetch the data and add it to report data source.  I do this so I can re-use report design and pass slightly different data sets to the same report.  This is similar to the approach I used 10 year ago with MS Access reports.  Not for everyone but it works for me.

I create a function where I can return the data I want by passing in the report name.


public static DataTable GetReportData(string reportName, string MDBfile)
{
try
{
EnterProc(MethodBase.GetCurrentMethod());
string sql = null;
DataTable table = null;
switch (reportName)
{
case "":
break;
case "CollectionSummaryByYear":
sql = "SELECT [Year], COUNT([Year]) AS SpecimenCount FROM tblMineral_Collection WHERE (Len([Year]) > 0) GROUP BY [Year]";
break;
case "FossilLabel_SmallCabinet":
case "FossilLabel_Thumbnail":
sql = "SELECT tblFossils.ID_Text, tblFossils.CommonName, tblFossils.ProperName, tblFossils.RockFormation, tblFossils.RockAge, tblFossils.Location, tblFossils.County, tblFossils.City, lstStates.State, lstCountries.Country, Str([tblFossils].[Year]) AS strYear FROM (tblFossils LEFT JOIN lstCountries ON tblFossils.CountryID = lstCountries.ID) LEFT JOIN lstStates ON (tblFossils.StateID = lstStates.ID) AND (tblFossils.CountryID = lstStates.CountryID);";                        
break;
case "MineralLabel_Cabinet":
case "MineralLabel_SmallCabinet":
case "MineralLabel_Micromount":
case "MineralLabel_Thumbnail":
sql = "SELECT tblMineral_Collection.ID_Text, tblMineral_Collection.Variety, tblMineral_Collection.Mineral_Name, tblMineral_Collection.Mine, tblMineral_Collection.City, tblMineral_Collection.County, lstStates.State, lstCountries.Country, Str([tblMineral_Collection].[Year]) AS StrYear, tblMinerals.Formula_1, tblMinerals.Formula_2 FROM (((LabelQueue INNER JOIN tblMineral_Collection ON LabelQueue.Specimen_ID = tblMineral_Collection.Specimen_ID) LEFT JOIN lstCountries ON tblMineral_Collection.CountryID = lstCountries.ID) LEFT JOIN lstStates ON (tblMineral_Collection.CountryID = lstStates.CountryID) AND (tblMineral_Collection.StateID = lstStates.ID)) LEFT JOIN tblMinerals ON tblMineral_Collection.Mineral_Name = tblMinerals.Mineral";
break;
case "SpeciesList":
sql = "SELECT tblMineral_Collection.Mineral_Name, Count(tblMineral_Collection.Mineral_Name) AS SpecimenCount FROM  tblMineral_Collection WHERE LEN(Mineral_Name)>0 GROUP BY tblMineral_Collection.Mineral_Name";
break;
}
 
if (!string.IsNullOrEmpty(sql))
{
table = TableFromMdb(MDBfile, sql);
}
ExitProc(MethodBase.GetCurrentMethod());
return table;
 
}
catch (Exception ex)
{
ErrorLog(ex);
return null;
}

The only trick to this approach is to pass in the data using name of dataset originally used to setup report.

var rds = new ReportDataSource("DataSet1", args.ReportData);
var report = new LocalReport
{
ReportEmbeddedResource = string.Format("DRC.RDLC.{0}.rdlc", args.ReportName)
};
report.DataSources.Add(rds);



Creating Labels in RDLC


Next step in Reporting without Crystal reports using RDLC: Multicolumn labels.

See Wrox article on Multicolumn labels

Safety tip for those playing at home with VS2010.  ReportViewer control does not render report to show data spread across into multiple columns.  To save paper, user Convert to PDF as Print preview.

I was able to create a report in landscape layout with 3 columns using Tablix control to display two data fields.

Be mindful to manage column spacing and margins otherwise there will be column spill over to the next page.



Friday, December 28, 2012

Finally Replacing CrystalReports

I've had a long hate-hate relationship with crystal reports.  Latest version of Crystal Reports via SAP is a 72 MB install!?! Why....  CrystalReport 2008 was only 17 MB.  Leave it to SAP to take a poor solution and make it worse.

So, I've been on a quest to replace crystal Reports.  In VS 2010, I've found a workable client side reporting solution in RDLC.  Now, I know its going to be an uphill battle to produce Avery style labels with RDLC but I'm going uphill 75MB lighter.

The real motivation for this reporting switch is my next version of Digital Rockhound's Companion software is going to be a download users can buy.  I'll trimmed down the files my software uses.  I'm ditching my street level arcview shapefiles dated circa 1995 for Google/Bing/OSM online maps.  Last piece is really the reporting engine.

In a little under 3 hours, I was able to add in a basic grid RDLC report into DRC 3.0 and get the report to display in the report viewer, print directly to a local printer, and render to a PDF and word file formats.

Resources that helped me with RDLC reports:

Brian Hartman's code to manually print a RDLC report.  Code sample Here.

MSDN: Walkthrough: Printing a Local Report without Preview 

Get this error attempting to print a localreport without a RDLC source.
The report definition for report 'xxx' has not been specified.

A couple of useful stackoverflow posts:
Code to convert RDLC to a PDF
Code to convert RDLC to Word.

Article from dotnetsoldier blog

My code to render a simple RDLC report to a word Doc and then launch word:


var rds = new ReportDataSource("DataSet1", args.ReportData);
                        var report = new LocalReport
                            {
                                ReportEmbeddedResource = string.Format("DRC.RDLC.{0}.rdlc", args.ReportName)
                            };

                        report.DataSources.Add(rds);
                        // ReSharper disable RedundantAssignment
                        string encoding = String.Empty;
                        string mimeType = String.Empty;
                        string extension = String.Empty;
                        // ReSharper restore RedundantAssignment

                        Warning[] warnings = null;
                        string[] streamids = null;
                        string wordDocName = args.PDFFileName.ToLower().Replace(".pdf", ".doc");
                        byte[] bytes= report.Render("WORD", null, out mimeType, out encoding, out extension, out streamids, out warnings);
                        using (var fs = new FileStream(wordDocName, FileMode.Create))
                        {
                            fs.Write(bytes, 0, bytes.Length);
                        }
                        if (File.Exists(wordDocName))
                            System.Diagnostics.Process.Start(wordDocName);



//My code to print RDLC locally:

                        var rds = new ReportDataSource("DataSet1", args.ReportData);
                        var report = new LocalReport
                            {
                                ReportEmbeddedResource = string.Format("DRC.RDLC.{0}.rdlc", args.ReportName)
                            };

                        report.DataSources.Add(rds);
                        var reportPrintDoc = new ReportPrintDocument(report)
                            {
                                PrinterSettings = {PrinterName = Properties.Settings.Default.DRC_Printer}
                            };

                        reportPrintDoc.Print();


Monday, December 24, 2012

Slight tweak to proposed Handbrake improvement



Added leading zero when converting offset back to a string.


Proposed Handbrake improvement.



destinationFilename = destinationFilename.Replace(tag, (iDvdTitle - iOffset).ToString("D2",CultureInfo.InvariantCulture));

Sunday, December 23, 2012

Proposed Handbrake improvement.

I've been busy ripping DVDs from my collection and I've run into an improvement for handbrake.

While converting TV episodes on DVD, I wanted to autoname the new file using output pattern S01-E0{title+offset}  like {title+5} for disc 2 in a DVD set where the title-1 is episode 6.

Handbrake - HandBrake is an open-source, GPL-licensed, multiplatform, multithreaded video transcoder, available for MacOS X, Linux and Windows

So I downloaded the codebase and found where in Windows code tree auto naming occurs.  HandBrakeCS its in Main.cs and Handbreake WPF autoNameHelper.cs

I have this code compiled and working locally great!

Now a commentary:

Dear SourceForge:

You have to be kidding! Separate logins for main site and forums site.  What is going on?
SourceForge also give lip service only to OpenID.  I initially created my user account with OpenID and  only to find I have limited permissions and I would need to create 'real' account to do anything useful.

SourceForge your site is terrible and I would recommend handbrake moving to another open source hosting site.

destinationFilename = UserSettingService.GetUserSetting<string>(UserSettingConstants.AutoNameFormat);

                    //Add ability to auto name file using {title-offset} or {title+offset}
                    // example {title-1} or {title+3]
                    // This is extremely useful when ripping DVD of tv show episodes.
                    #region AutoName based on {title-offset} or {title+offset}

                    int startPos = 0;
                    int stopPos = 0;
                    string offsetTag;
                    var iDvdTitle = Convert.ToInt32(dvdTitle);

                    if (destinationFilename.Contains("{title-"))
                    {
                        offsetTag = "{title-";
                        startPos = destinationFilename.IndexOf(offsetTag, StringComparison.InvariantCulture);
                        stopPos = destinationFilename.IndexOf("}", startPos, StringComparison.InvariantCulture);
                        string tag = destinationFilename.Substring(startPos, stopPos - startPos + 1);
                        string offset = destinationFilename.Substring(startPos + offsetTag.Length, stopPos - startPos - offsetTag.Length);
                        int iOffset = 0;
                        try
                        {
                            iOffset = Convert.ToInt32(offset);
                        }
                        catch { }
                        destinationFilename = destinationFilename.Replace(tag, (iDvdTitle - iOffset).ToString(CultureInfo.InvariantCulture));

                    }

                    if (destinationFilename.Contains("{title+"))
                    {
                        offsetTag = "{title+";

                        startPos = destinationFilename.IndexOf(offsetTag, StringComparison.InvariantCulture);
                        stopPos = destinationFilename.IndexOf("}", startPos, StringComparison.InvariantCulture);
                        string tag = destinationFilename.Substring(startPos, stopPos - startPos + 1);
                        string offset = destinationFilename.Substring(startPos + offsetTag.Length, stopPos - startPos - offsetTag.Length);
                        int iOffset = 0;
                        try
                        {
                            iOffset = Convert.ToInt32(offset);
                        }
                        catch { }
                        destinationFilename = destinationFilename.Replace(tag, (iDvdTitle + iOffset).ToString(CultureInfo.InvariantCulture));

Saturday, December 1, 2012

Plex Media Server

Over Thanksgiving, my brother-in-law introduced me to Plex Media Server.
It's like  your own personal Netflix using MP4 ripped from your collection.

Plex Media Server is a media distribution system that can use a variety of formats (I'm using MP4/m4v) and distribute the media to different devices at once.
In my household, I am streaming to 3 xboxes, 6 computers, and 4 android tablets.

There other nice aspect of Plex Media server, it is going to allow me to purchase movies and TV shows now in MP4 format then deploy them

Plex is just the server so you will need additional software to convert DVD to MP4s. http://www.plexapp.com/

I am using AnyDVD HD from SlySoft. http://www.slysoft.com/en/anydvd.html

And I am using Handbrake for Windows. http://handbrake.fr/downloads.php

HandBrake can seems a bit complex but reading through the explanaion of the parameters and the help got me started.  I would recommend that you do a couple sample conversions and testing the resulting MP4.

The nice thing is in handbrake you can save your settings as presets.

I created a preset for action movies and went with very conservative parameters to capture motive and I tested the with Wide screen edition of Empire Strikes Back which I then viewed in a 60in TV via XBOX 360.  Handbrake ripped the movie in 2 hrs on 4 3.0 GHz Quad core XEON Dell Precison 490 with 4 GB RAM running 32 bit Windows 7.  The video came out fantastic.



Of course with any software solution, better hardware results in better performance.

Compare:  Ripping a Red Dwarf Episode using good quality video settings (identical on both).

Intel 2-CPU 2-Core Xeon 3.0 Ghz, 4GB RAM , 32bit Windows 7:  ~28 min
Intel i7-2600 CPU 3.40 Ghz, 8 GB RAM, 64bit Windows 7: ~8:30 min !!







ReSharper 7

Recently, I was reintroduced to ReSharper version 7.0.

Resharper 7 is completely awesome.  Best feature is code editing helpers.  With ReSharper, I transformed some reasonably complex nested for each  and if else statements into Linq.  I also love the invert If transformation.  It's great to reduce if else statements complexity.

I put it through its paces refactoring code in my Digital Rockhound's Companion software project.  I decided I would attempt to accept as many of the resharper suggestions as possible.  My code is so much cleaner.

I did notice that on Event delegate declarations, that on occasion Resharper was tagging an event incorrectly as being unsubscribed / unused.  I am still trying to understand the use case but it was easy to get Resharper to ignore it using comment tags.

Anyone who is doing .net development and has never used Resharper should download the trial.  You'll be hooked.

Take it for a test drive.  Any company employing .net developers will save money by using this product by getting better code.

http://www.jetbrains.com/resharper/features/index.html