April 12, 2013

Installshield 2012 Spring LE: SQL CE (Compact Edition) 4.0 Prerequisite

Recently I've did a complete reinstall of my laptop because of an upgrade to MS Windows 8. (Whether that's the first mistake in this story is entirely your opinion *grin*). After that MS SQL Server 2012 (Express), followed by Visual Studio 2012. All shiny and new as a figure of speech...

My problem

Next: import a Visual Studio 2010 solution into my new (and still shiny) Visual Studio 2012. It was a standard winforms app. One tiny (easily solvable) problem occurred: The solution contained a SQL CE  database, version 3.5. On my shiny new Win8 system SQL CE 4.0 was installed, either by windows itself, visual studio or SQL server 2012? (Who knows, who cares?) So I updated the references in my project to the new SQL CE objects and sure enough it compiled and ran smoothly.

Visual Studio 2012 doesn't support the 2010 Visual Studio Installer projects. This forced me to create a new Setup and Deployment project in Flexera's Installshield 2012 Spring Limited Edition. And here problems started to rise...

Prerequisite troubles...

To start with: The "Light Edition" is severely crippled (imho). It's really the absolute minimum which enables you to create a setup file, but there's very little room to tailor it. I guess they (flexera) think by doing so we're forced to buy the full version?!?!
Back to the problem: You can add prerequisites to the installer, this is the good news. The bad news however: there is no SQL CE 4.0 prerequisite to be found anywhere!!!!
Now what?

Problem solved

To shorten the story (I assume you're not interested in my frustration leading to the solution) I've figured out how to create a custom prerequisite to install SQL CE 4.0 with my application installer.

Install Shield 2012 LE - Prerequisites...

Step 1 

Download the SQL Server Compact redistributables from the Microsoft Download Center at http://www.microsoft.com/en-us/download/details.aspx?id=30709.
  • SSCERuntime_x86-ENU.exe
  • SSCERuntime_x64-ENU.exe
SQL Server Compact 4.0 is freely redistributable under a redistribution license agreement. You need to download both installers (x86 and x64)

Step 2

Create the prerequisite files for Installshield. First of all a big thank you for posting the most important part of the solution on Stack Overflow by Enzero This helped me big time.
With version 3.5 of CE there was a single installer for both x86 and x64. With 4.0 there are two separate installers. This is where I needed to tweak Enzero's solution. A check if my app is installed on a 32 or 64 bit OS.

So the "tinkered" scripts are now:

Microsoft SQL CE 4.0 SP1 (x86).prq

 <?xml version="1.0" encoding="utf-8"?>  
           <condition Type="4" Comparison="2" Path="[ProgramFilesFolder]\Microsoft SQL Server Compact Edition\v4.0" FileName="sqlceca40.dll" ReturnValue="" Bits="2"/>  
            <operatingsystemcondition Bits="1"></operatingsystemcondition>  
           <file LocalFile="&lt;ISProductFolder&gt;\SetupPrerequisites\SQL CE 4.0 SP1\SSCERuntime_x86-ENU.exe" URL="http://download.microsoft.com/download/F/F/D/FFDF76E3-9E55-41DA-A750-1798B971936C/ENU/SSCERuntime_x86-ENU.exe" FileSize="0,0" />  
      <execute file="SSCERuntime_x86-ENU.exe" cmdline="/i /passive" cmdlinesilent="/i /passive" />  
      <properties Id="{05DCCDB5-57E0-4314-A016-874F228A8FAD}" Description="This prerequisite installs the Microsoft SQL Server Compact 4.0 SP1. (x86 32bit edition)"/>  

Microsoft SQL CE 4.0 SP1 (x64).prq
 <?xml version="1.0" encoding="utf-8"?>  
           <condition Type="4" Comparison="2" Path="[ProgramFilesFolder]\Microsoft SQL Server Compact Edition\v4.0" FileName="sqlceca40.dll" ReturnValue="" Bits="2"/>  
            <operatingsystemcondition Bits="4"></operatingsystemcondition>  
           <file LocalFile="&lt;ISProductFolder&gt;\SetupPrerequisites\SQL CE 4.0 SP1\SSCERuntime_x64-ENU.exe" URL="http://download.microsoft.com/download/F/F/D/FFDF76E3-9E55-41DA-A750-1798B971936C/ENU/SSCERuntime_x64-ENU.exe" FileSize="0,0"/>  
      <execute file="SSCERuntime_x64-ENU.exe" cmdline="/i /passive" cmdlinesilent="/i /passive" />  
      <properties Id="{05DCCDB5-57E0-4314-A016-874F228A8FAD}" Description="This prerequisite installs the Microsoft SQL Server Compact 4.0 SP1. (x64 64bit edition)"/>  

Step 3

To make this voodoo all come to life we need one last step. When Installshield Spring LE was default installed the prerequisite folder should be located here:
C:\Program Files (x86)\InstallShield\2012SpringLE\SetupPrerequisites
At this location create 2 ".prq" files (with any text editor -notepad will do-):
  • Microsoft SQL CE 4.0 SP1 (x86).prq
  • Microsoft SQL CE 4.0 SP1 (x64).prq
Paste the XML from above in the created files. The x86 content in the x86 file and the x64 content in the x64 file. Make sure the extension of the files is ".prq" (especially when using notepad!)

In this folder you also need to create a new subfolder: SQL CE 4.0 SP1
In this "SQL CE 4.0 SP1"-folder you mustcopy the two downloaded redistributable files from Microsoft: SSCERuntime_x86-ENU.exe & SSCERuntime_x64-ENU.exe

Watch carefully when adding the folder with redistributable files. The naming is used in both .prq XML content.

That's it!
Now, when you look at the redistributables in your Visual Studio setup project, you should see the x86 and x64 SQL CE 4.0 prerequisites. If you're not sure if your app is going to be installed on a x86 Windows OS or an x64. just add both! The "<operatingsystemconditions>" part in the XML checks if the prerequisite should be installed based on the architecture.

Keep in mind...

Though this "trick" does work and does install SQL CE 4.0 with your applications setup, beware this is not an official prerequisite! I don't think it's illegal either ("freely redistributable under a redistribution license agreement")
Other then this, you're good.

It took me a few free evenings to figure this one out. I hope you find this info useful and it didn't took you that long for you to find my blog entry :o)

April 05, 2013

TSQL: Count / SUM with 'Where' condition

My problem

Doing some reporting services I found myself in need to have group aggregates. I try to solve most hurdles in the SQL queries so I don't need to much trickery in the report definitions. I've done these queries many times and usually found myself doing sub queries from the main one, or using temporary tables etc.

Imagine this question based on the tables below: How many herbivores with what total value are in my zoo?

Example tables :)

Problem solved!

I've found the answer on Stack Overflow. The trick is using a 'join' and a 'case when' in your count/sum syntax. So the query to answer the question above is like this:

 SELECT dbo.Food.[Description] as Food  
     ,COUNT(CASE dbo.Species.FoodID WHEN dbo.Food.Id THEN 1 ELSE null END) AS [Count]  
     ,SUM((CASE dbo.Species.FoodID WHEN dbo.Food.Id THEN dbo.Species.Value ELSE null END) AS Value  
  FROM dbo.Species  
  LEFT JOIN dbo.Food ON dbo.Species.FoodID = dbo.Food.Id 
  GROUP BY dbo.Food.[Description]  

And the result:
The expected result of the query

March 18, 2013

IE10: ImageButton PageRequestManagerServerErrorException

And behold the puzzling cryptic error message above, kindly displayed by Internet Explorer 10 when viewing a page from an application which is flawlessly displayed in any other browser....
Yes, including IE9 and below?!?!

They dit it again...

The boys at Redmond kept me busy for an hour or so! To make a long story short: IE10 has a problem with ImageButtons. Only God knows why, but somebody decided the image button click post back location values should change from integer to decimal and somehow jQuery doesn't agree with that.

Now what?

Roaming the web I've found numerous references to this issue and some fixes. One of them was updating the .Net framework version to 4.5 of the clients another 'fix' suggested to replace the image buttons with another control all together..
Both were not an option. After some more extensive search I've found the best solution (imho):

By forcing IE9 compatibility the problem was fixed. Adding the X-UA-Compatible tag to the master page the problem was solved and all things works as designed....

Don't you love wild goose chases once in a while?

March 13, 2013

VB.Net: SQL Server Image column to PicturBox Bitmap

There's always a debate at the office whether images should be stored in the database or in the file structure. I prefer the database, using the SQL Server "image" column data type.
This way there is no need to set authentication on the file server folders who can and cannot access the location of the images.

Usually the 'location' is still saved in a database column. When accessing the image at the stored location you have to catch exceptions if someone deleted the image from the server without updating the 'location' in the database.

When reading and writing one uses a byte array to 'communicate' the data. Consider the table above where the image column is "FactuurHeader".

Check the example "read" code:

 Public Shared Function GetHeader(ByVal cnStr As String) As Bitmap  
     'default (empty) logo  
     Dim logo As New Bitmap(100, 108)  
     Using cn As New SqlConnection(cnStr)  
       Using cmd As New SqlCommand()  
         cmd.CommandText = "SELECT TOP 1 FactuurHeader FROM dbo.tblPraktijk WHERE Actief=1"  
         cmd.Connection = cn  
         Dim dr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)  
         If dr.HasRows Then  
           If Not dr.Item("FactuurHeader").Equals(DBNull.Value) Then  
             'Here's the voodoo: from image column data to bitmap... 
             Dim imgData() As Byte = DirectCast(dr.Item("FactuurHeader"), Byte())  
             If imgData.Length > 0 Then  
               Dim stream As New MemoryStream(imgData)  
               logo = New Bitmap(stream)  
             End If  
           End If  
         End If  
       End Using  
     End Using  
     Return logo  
   End Function  

The example code takes the first record from the database table (tblPraktijk), checks if the "FactuurHeader" isn't null and returns a bitmap object. When the record does not exist (empty table) or it's image column is empty, a default (empty) bitmap is returned (first line in the method)

When writing to the column the process is reversed by creating a byte array from your bitmap. I use two static methods to do the conversion:

   ''' <summary>  
   ''' Convert Image to byte array 
   ''' (From picturebox to database data)  
   ''' </summary>  
   ''' <param name="img">Image to be converted</param>  
   ''' <returns>A byte array representing the image</returns>  
   ''' <remarks></remarks>  
   Public Shared Function ImageToBytes(img As Image) As Byte()  
     If img Is Nothing Then Return Nothing  
     Dim ms As New MemoryStream()  
     img.Save(ms, System.Drawing.Imaging.ImageFormat.Png)  '<- Use correct image format!
     Return ms.ToArray()  
   End Function 
   ''' <summary>  
   ''' Convert byte atray to Image  
   ''' (From database data to picturebox bitmap) 
   ''' PicturBox1.Image = new Bitmap(myImage) 
   ''' </summary>  
   ''' <param name="bytes">Byte arry to be converted</param>  
   ''' <returns>Image from the byte array</returns>  
   ''' <remarks></remarks>  
   Public Shared Function BytesToImage(bytes As Byte()) As Image  
     If bytes Is Nothing Then Return Nothing  
     Dim ms As New MemoryStream(bytes)  
     Dim img As Image = Image.FromStream(ms)  
     Return img  
   End Function  

Note: my default image format is PNG. Tweak the ImageToBytes function if you want a little flexibility in using different image formats...

February 20, 2013

Multiple return types? Tuple!

mathematical tuple
When coding there's one rule I don't want to break (It's my own rule btw):

Whenever a code block needs to be copied, it needs to be in a separate method.

Of course the disadvantage of this approach is that whenever you code a bug in this piece of code, it will surface wherever it's used in your application.

However, this (in my opinion) outweighs the frustration of fixing the same bug on multiple locations again and again and again and...

My problem.

In general, when using this 'generic' code, I settle for a single return value. A boolean result to indicate everything worked out fine (or not!) is enough. Sometimes I would like to have more return values then a single type. The most obvious options:
1 - Use a struct return value
2 - Use an object as return value
3 - Return a collection object (Dictionary, List, etc)
4 - Use ref parms...

Problem solved?

(1) requires a definition. Typically the return struct is specific to the function it returns. I need to define each struct or create something generic...
(2) as (1) : definitions : I'm a lazy programmer ;)
(3) when al return values are of the same type it's definitely an option, but when that's not the case...
(4) big function signature and the variables need to be declared 'upfront' even though they may remain 'empty'

Problem solved!

You can return a mixed type collection! It's not dynamic, so you cannot add elements on the fly like a 'usual' collection. Consider this (console example) code:

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 namespace ConsoleApplication1  
   class Program  
     static void Main(string[] args)  
       // the tuple for the result Item1: bool, Item2: String 
       Tuple<bool, String> result;  
       MyObject test = new MyObject();  

       result = test.TestMethod(new MyObject(true));  
       Console.WriteLine("test.TestMethod(new MyObject(true));");  

       result = test.TestMethod(new MyObject(false));  
       Console.WriteLine("test.TestMethod(new MyObject(false));");  
   class MyObject  
     public bool BooleanProperty { get; set; }  
     public MyObject() : this(true) { }  
     public MyObject(bool succes)  
       BooleanProperty = succes;  
     public Tuple<bool, String> TestMethod(MyObject obj)  
       bool succes = true;  
       String err = String.Empty;  
         if (!obj.BooleanProperty) throw new InvalidOperationException("Something went wrong!");  
       catch (Exception ex)  
         succes = false;  
         err = ex.Message;  
       return Tuple.Create<bool, String>(succes, err);  

You define the types in the Tuple declaration. Not just value types, any object can be added. You just need to keep the result Tuple 'in sync' with the expected return Tuple. The elements are stored in Item1, Item2, etc. Visual Studio intellisence shows the types of these items :)

Now we can return a 'mixed collection' 'on the fly' :)

October 18, 2012

Transparent picturebox overlay in winforms

I stumbled into an annoying problem when doing some graphics coding in a windows forms environment. The task seemed rather simple: Have a panel with a background image, drag images -in my case as listview items- onto that panel where a picturebox is shown to display the dragged image. When done, merge the picturebox images with the background to store the final picture as a single image.

My problem

When I drag an image (listview item) onto the panel a picturebox is created at the dragged location and the image is displayed. However, troubles arise when two pictureboxes overlap. Here's the catch: Setting transparency on your picturebox will make the background reflect the picturebox control background!
So the result lookst like this:
(I colored the second picturebox transparent area a little 'red-ish' to show the problem!)

Problem Solved?

I did some research on the net, but to no avail. So, I improvised:
  1. We know the picturebox transparent area will reflect the picturebox parent image/foreground
  2. I do need the complete picture anyway
  3. There's no "free painting" involved, just 'reorganizing' pictures
  4. Speed is not an issue, since by my (educated) guess, in my app there will be dragged less than well... lets say 50 images to the panel.
  5. Considering point 4, I just might 'get away' with creating the final picture every time a picturebox is created/dragged/transformed...
  6. Control tags can contain Objects (other controls!)

 Problem solved!

Now consider this layout:
  1. A panel containing the background image (background)
  2. A Panel containing the imaged where background and picturebox images are merged (canvas) and the parent of my pictureboxes
  3. The pictureboxes dragged onto the canvas

The trick now is to 'reconstruct' the panel canvas background image every tima a picturebox is changed/added.
  • We first create the background panel with the (clean) background image (pnlBackground).
  • Next we create the canvas panel where pnlCanvas.Tag = pnlBackground. The canvas is a child control of the background!
  • Every time a new picturebox is added, the picturbox.Tag = pnlBackground. The pictureboxes are children of the canvas!
Do you see where I'm going here?
Now on every event changing the pictureboxes on the canvas panel we call a function to reconstruct the canvas panel background image. Events like dragdrop on the canvas panel (adding a new picturebox) and events of the picturebox itself (dragging, rotating, etc)

The merging function should accept a picturebox and a panel (overload it). When called with a picturebox we have:
Panel pnlCanvas = sender.Tag as Panel (sender == picturebox)
Panel pnlBackground = pnlCanvas.Tag as Panel

Now, take the pnlBackground image and merge each picturebox image with it. The resulting image is your new pnlCanvas image.

The end result will look like this:

Each picturebox transparent area reflects the parent control foreground, which is the merged picture of pnlCanvas! Since the pictureboxes are still there (overlaying the pnlCanvas) they are still available for editing by the user (dragging, rotating, etc)

 There's lots of code available about merging images, so no code in this post. Just the basic idea on how I solved the problem. It might not be the best solution, but it works for me. As mentioned above: My 'canvas' will not contain over 50 pictureboxes so speed is not an issue to me, but 'reconstructing' (merging) the canvas picture will cost you...

October 11, 2012

Custom Cursor Hotspot - HowTo...

When I recently implemented some drag and drop functionality I wanted to create a nice "drag cursor". A little different from the default. A quick "Google" showed me how to use a bitmap as a cursor.

My Problem

When creating a cursor from a bitmap the new cursor hotspot is the center point of that cursor image.

Though I had a nice cursor now, using it posed a challenge for my users. People -rightfully- assume the "fingertip" to be the point where they are pointing at not the center of their cursor.

Problem solved?!

Another Google search brought me the solution. To create my custom cursor (assuming the image is one of my resources) I had this code:

 Cursor.Current = New Cursor(My.Resources.addCursor.GetHicon())  

To create a custom cursor with a custom hotspot I changed it to:

 Cursor.Current = FormHelper.CreateCursor(My.Resources.addCursor, New Point(5, 5))  

The voodoo happens in the CreateCursor -static- method. Though you're probably not interested, I copied the source anyway:

#Region "Create Custom Cursor" 
   Private Structure IconInfo  
     Public fIcon As Boolean  
     Public xHotspot As Int32  
     Public yHotspot As Int32  
     Public hbmMask As IntPtr  
     Public hbmColor As IntPtr  
   End Structure  

   <DllImport("user32.dll", EntryPoint:="CreateIconIndirect")> _  
   Private Shared Function CreateIconIndirect(ByVal iconInfo As IntPtr) As IntPtr  
   End Function  

   <DllImport("user32.dll", CharSet:=CharSet.Auto)> _  
   Public Shared Function DestroyIcon(ByVal handle As IntPtr) As Boolean  
   End Function  

   <DllImport("gdi32.dll")> _  
   Public Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean  
   End Function  

   ''' <summary>  
   ''' Create a custom winforms cursor with a designated hotspot  
   ''' </summary>  
   ''' <param name="bmp">Image to be used as cursor</param>  
   ''' <param name="hotspot">Hotspot location of the cursor</param>  
   ''' <returns></returns>  
   ''' <remarks>  
   ''' Requires imports:  
   ''' Imports System.Drawing  
   ''' Imports System.Runtime.InteropServices  
   ''' Imports System.Windows.Forms  
   Public Shared Function CreateCursor(ByVal bmp As Bitmap, ByVal hotspot As Point) As Cursor  
     'Setup the Cursors IconInfo  
     Dim tmp As New IconInfo  
     tmp.xHotspot = hotspot.X  
     tmp.yHotspot = hotspot.Y  
     tmp.fIcon = False  
     tmp.hbmMask = bmp.GetHbitmap()  
     tmp.hbmColor = bmp.GetHbitmap()  
     'Create the Pointer for the Cursor Icon  
     Dim pnt As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tmp))  
     Marshal.StructureToPtr(tmp, pnt, True)  
     Dim curPtr As IntPtr = CreateIconIndirect(pnt)  
     'Clean Up  
     Return New Cursor(curPtr)  
   End Function  

#End Region  

Don't forget the required imports! (In case you it did interest you)