Monday, October 8, 2012

EvelateDB 2.11 Just Released - Supports XE3

ElevateDB 2.11 is now available for download. If you're an existing customer, then you should be receiving an email shortly with download instructions. You can view the release notes, including any breaking changes, on the download page before downloading.

This release includes support for Embarcadero RAD Studio XE3 and a new package naming convention to make referencing the ElevateDB runtime packages easier for VCL/FireMonkey applications and other design-time 3rd party packages.

News Release
ElevateDB

Semper Fi,
Gunny Mike

Saturday, August 25, 2012

Making Sense of the Whole Delphi WinRT Thing

I'm not one of the "Low-Level" "Deep-Inside" Delphi smart guys. I'm just an average software developer who uses Delphi to create simple applications I sell to other average people. Oh, I learn a lot hanging around the smart guys but I seem to get lost when the discussions and comments take a deep dive.

It's no doubt that Allen Bauer's post created quite a stir around the community and the new release of XE3. I've tried to make sense of all the buzz going back and forth but at one point it just got way over my head.

Since I am just a Micro-ISV, I am looking for a simplistic answer to what this whole Delphi - WinRT crap is all about and I think I have figured it out.

I posted this on the Delphi Developer group on Facebook:
I've been trying to follow all the talk about what is going on with Delphi and WinRT ever since Allen Bauer's post went viral. I'm not as smart as most of you Delphi guys. Can someone please explain in very simplistic terms what it means when Delphi either can't or is excluded from creating WinRT apps? How does this effect a Micro-ISV like myself in terms of distribution and sales?
I received this response...
For example, you will probably not be able to sell your app through the MS Application Store, which could really hurt your exposure to prospective new customers.
Okay. So I started googling Microsoft App Store and discovered a couple interesting tidbits.

I started off reading a post by George Ou called ARM Battery Life Advantage Myth Lives On

This lead me to a post by Steven Sinofsky called Collaborating to deliver Windows RT PCs

Which lead me to another post by Steven Sinofsky called Building Windows for the ARM processor architecture

Here is what I think is going on and how it effects me and you who sell software to the average consumer.


Microsoft is leading a push to compete directly with the iPad. So, Microsoft invented a new operating system called WOA or Windows on ARM which in my opinion is a Reduced Instruction Set Computer. This new operating system is a subset of Windows 8 and will be available on the new WinRT PC's soon to be released.

It sounds like WinRT PC's (I'd prefer they were called tablets) are a separate line of personal computers designed to compete directly with the iPad market. No disk drive, no expansion slots, one or two USB ports and completely mobile. Which requires a long life battery, hence the first post I sited above. These devices will be RISC (Reduced Instruction Set Computers) running the WOA which includes WinRT. This limits the applications to only those applications that conform to the WinRT subset of instructions.

All other Non-WinRT PC's will come with a full blown Windows 8 OS in addition to the scaled down WOA running WinRT. This lets you run all the currently developed Windows Applications (those created using Delphi) and any of the new applications that conform to the WinRT subset of instructions.

What does all this mean to you and I...


Given that the WinRT PC's (I'd prefer they were called tablets) have a subset of the real Windows 8 OS, the only way to install software on these machines will be through the use of the Microsoft App Store. Since Microsoft controls this store they will make sure that any app in the store will run without problems on the new WinRT machines.

The bottom line, we (Delphi developers) will still have a huge user base availabe to us. Not everyone will flock to these new WinRT PC's (I'd prefer they were called tablets). When EMBT finally does get the details worked out and makes it available to us... we can retool, recompile and look forward to a windfall of new WinRT customers.

Enjoy - Semper Fi
Gunny Mike

Thursday, August 23, 2012

Priceless: Behind the Code with Anders Hejlsberg

I just watched this interview with Anders Hejlsberg for the first time. This is truly an amazing interview. It's rather long, about 1 hour, but it is so worth it. I'm not giving anything away... you'll have to just watch and enjoy.



Enjoy - Semper Fi
Gunny Mike

Tuesday, August 7, 2012

Raize Components Makes Id/Value Pair Logic A Breeze

I have known about Raize Components for a long time. The buzz I've always heard was something like this... "Oh yeah, Raize is good if you want a nice consistent look to all of your components within your program."

That's not really very exciting when you are thinking about spending $400.

Then I watched a video from Code Rage 5 where Ray demonstrates and talks about his components. Wow, did this change my mind. I purchased Raize Components 6.0 three days later. Here is the Code Rage 5 video I'm talking about UI Design with Raize Components.

There's also an XE2 Code Rage 6 video UI Design with Raize Components and RAD Studio XE2

You owe it to yourself to watch one or both of these videos.

Anyway, I have a passion for using Id/Value pairs in all of my lookup code. I find it much easier to pass around an integer Id instead of the actual string value. I guess this comes from years of creating databases with lookup tables based on nonsensical auto-increment Id's.  For example:

tblColors
IdValue
1Blue
2Green
3Red
4Yellow

So, I was very excited when I learned that the TRzComboBox component from Raize supports Id/Value Pairs.  You just right click on the control, choose Edit Items & Values and enter your Id value pairs.
 
When you want to retrieve the Value associated with the ItemIndex of the control you simply reference the TRzComboBox.Value property.

Okay that was the easy part. Now how do you go about doing the opposite, setting the control so the ItemIndex refletcs the Id value? That's a good question.

Suppose we have the value 7 which represents "Seven" stored in an interger variable and we want to set the control so that Seven is the highlighted item.


You simply iterate through the control testing each of the values to see if it matches. Upon a match you set the ItemIndex.

Raize makes this easy beacause it exposes both the Value property and the Values property. Value corresponds to the ItemIndex and Values is a TStringList of all the values.

Below is a simple little program to demonstrate how to do set the ItemIndex to the Value. It does require Raize Componets 6.0 from http://www.raize.com/

The key is a little helper procedure that accepts two parameters. The first is a TRzComboBox, and the second is the Id value. By passing the TRzComboBox by reference it allows any changes to the ItemIndex to happen directly to the control itself.

Procedure SetComboBoxValue(var cb:TRzComboBox; x:integer);
var i :integer;
begin
  for i := 0 to cb.Count - 1 do
    if x = StrToInt(cb.Values[i]) then
    begin
      cb.ItemIndex := i;
      Exit;
    end;
end;
As you can see, it simply iterates through the list of values looking for a match and when found it Exits.

Enjoy,
Semper Fi - Gunny Mike

 UPDATE 08/08/2012  9:25 PM EST

I just received an email from Ray Konopka regarding TRzComboBox.Value property. It turns out that the Value property is a read/write property. That means you can assign a value to the Value property and it will change the ItemIndex property automatically! How simple is that...

RzComboBox1.Value := IntToStr(7);

Thanks Ray and keep up the great work.




object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 203
  ClientWidth = 226
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object RzButton1: TRzButton
    Left = 64
    Top = 112
    Width = 110
    Caption = 'Value = 1'
    TabOrder = 0
    OnClick = RzButton1Click
  end
  object RzComboBox1: TRzComboBox
    Left = 64
    Top = 32
    Width = 110
    Height = 21
    TabOrder = 1
    Text = 'One'
    OnChange = RzComboBox1Change
    Items.Strings = (
      'One'
      'Two'
      'Three'
      'Four'
      'Five'
      'Six'
      'Seven'
      'One Hundred'
      'Nine Nine Nine')
    ItemIndex = 0
    Values.Strings = (
      '1'
      '2'
      '3'
      '4'
      '5'
      '6'
      '7'
      '100'
      '999')
  end
end

unit SetComboBox;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
  Forms, Dialogs, StdCtrls, RzCmboBx, RzButton;

type
  TForm1 = class(TForm)
    RzButton1: TRzButton;
    RzComboBox1: TRzComboBox;
    procedure RzButton1Click(Sender: TObject);
    procedure RzComboBox1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  Procedure SetComboBoxValue(var cb:TRzComboBox; x:integer);
var
  Form1: TForm1;

implementation

{$R *.dfm}

Procedure SetComboBoxValue(var cb:TRzComboBox; x:integer);
var i :integer;
begin
  for i := 0 to cb.Count - 1 do
    if x = StrToInt(cb.Values[i]) then
    begin
      cb.ItemIndex := i;
      Exit;
    end;
end;

procedure TForm1.RzButton1Click(Sender: TObject);
var
  i,j : integer;
begin
  i := Random(RzComboBox1.Count);
  j := StrToInt(RzComboBox1.Values[i]);
  RzButton1.Caption := 'Value = ' + IntToStr(j);
  SetComboBoxValue(RzComboBox1,j);
end;

procedure TForm1.RzComboBox1Change(Sender: TObject);
begin
  RzButton1.Caption := 'Value = ' + RzComboBox1.Value;
end;

end.

Saturday, July 7, 2012

How to deal with Programmer's Block

Eventually, we all go through a bout of Programmer's Block. What is Programmer's Block? It's very similar to Writer's Block except it happens to programmers.

I have been struggling for a very long time with Programmer's Block and didn't realize it. I knew what I wanted to do (vaguely) but just couldn't get things off to a good start. For me, it was the big picture... I could not see the big picture.

I started playing around with the little pieces. I worked on the tiny algorithms knowing I would fit them in somehow... but I just didn't have that aha, big picture moment. And then...

I posted this question on P.SE "How do you get past the Analysis to Paralysis when working on a new project?" and within 3 minutes it received 1 down vote and 1 vote to close. I was not happy. I'm thinking great... even the people on P.SE are not going to help. (try this link instead)

Soon I got some great feedback. I'm not going to repeat it all here... don't think I could. Might be copyright violation. So, you'll just have to read it there. Make sure to read the comments... there's some really good stuff in them.

I love yellow stickies.

This post is dedicated to all of you who have or will go through "Programmers Block".

Special thanks to:
haylem & ThorbjørnRavnAndersen

Enjoy! Semper Fi
Gunny Mike

Sunday, July 1, 2012

Restoring Quick Config on a TMS AdvStringGrid

I recently purchased the TMS Component Pack mainly because of the grid capabilities TMS offers over Delphi's native grid. So I started playing around with the grid and flipping things on and off to see how the grid would be affected.

I was playing around with the "Set Look" option of the Quick Config. I tried out every option and settled on Windows XP. Then I clicked the little X in the red box and ... poof it was gone. No more Quick Config. I spent the next three hours trying to figure out how to turn it back on.

Property ShowDesignerHelp determines wether the "Quick Config" is on or off. Geez, I was looking for a property called "QuickConfig"... no wonder I couldn't find it.

Semper Fi,
Gunny Mike































Sunday, June 10, 2012

Rounding the Currency type like they taught us in school

I started using Delphi's Currency type for it's accuracy only to discover it doesn't round the way I would have expected it to round. I remember being taught in school that when rounding numbers if the remainder was equal to or greater than five you round up otherwise you round down.

For Example:
  • 12.344 Rounds to 12.34
  • 12.345 Rounds to 12.35
  • 12.346 Rounds to 12.35
This is how I was taught in school.
This is how Microsoft Excel does it when you set the cell to display as two decimal places.
This is how Microsoft SQL does it when you format the display as two decimal places.

'---------------------------------
' Microsoft SQL
'---------------------------------
DECLARE @Money money
SET @Money = 12.345
SELECT CONVERT (varchar(10),@Money,1)
'---------------------------------
12.35
But that's not how Delphi does it.

'---------------------------------
' Delphi 2010
'---------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
  s : string;
  c : currency;
begin
  c := 12.345;
  s := '';
  s := s + 'Value ' + FloatToStr(c);
  s := s + Chr(13);
  s := s + Format('Formatted as money = %m',[c]);
  ShowMessage(s);
end;


So this lead me looking around for an answer. It appears that Delphi uses what is known as Bankers Rounding which means round to the closest even number. I was never taught this in school. This is how Bankers Rounding works:
  • 12.345 Rounds to 12.34
  • 12.355 Rounds to 12.36
  • 12.365 Rounds to 12.36
I do not want this type of rounding behavior... I want the rounding that I was taught in school. The problem is Delphi's internals do not support this type of rounding. (This is where I have one of those love hate realtionships with Delphi. I hate Delphi!)

So I posed a question on StackOverflow and after clarifying what I meant, got a couple interesting responses. Here is a solution I found reading through the Embarcadero discussion mentioned in my question on SO.
function RoundCurrency(const Value: Currency): Currency;
var
  V64: Int64 absolute Result;
  Decimals: Integer;
begin
  Result := Value;
  Decimals := V64 mod 100;
  Dec(V64, Decimals);
  case Decimals of
    -99 .. -50 : Dec(V64, 100);
    50 .. 99 : Inc(V64, 100);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s : string;
  c : currency;
begin
  c := 12.345;
  s := '';
  s := s + 'Value ' + FloatToStr(c);
  s := s + Chr(13);
  s := s + Format('Formatted as money = %m',[c]);
  s := s + Chr(13);
  s := s + Chr(13);
  s := s + 'Using the RoundCurrency function';
  s := s + Chr(13);
  s := s + Format('Formatted as money = %m',[RoundCurrency(c)]);
  ShowMessage(s);
end;

Now that's more like it. I love Delphi!

Enjoy
Semper Fi - Gunny Mike

Sunday, May 13, 2012

Plan Ahead When It Comes To FireMonkey & iOS

I don't intend to move to XE2 or XE(next) anytime soon, I have my hands full just porting over my old applications from D5 to D2010. However, I would like to take advantage of FireMonkey and the iOS market when the time is right so I'm planning ahead and so can you.

I am currently working on adding Inifile support to my application to keep track of window sizes and position. I asked on StackOverflow if FireMonkey has anything similar to GetSystemMetrics. Whiler posted a snice snippet of code that uses FX.Platform. (Merci Beaucoup Whiler)

My first thought was it's too bad I can't use this piece of code today. Then I had one of those aha moments. I thought to myself,
"Why not add this as a comment in your Delphi code."
So that's what I did.

Begin Code Snippet
{
----------------------------------------------------------------
FireMonkey Consideration:
----------------------------------------------------------------
Question asked on stackoverflow:
http://stackoverflow.com/questions/10564505
I'm rewriting an old application using Delphi 2010 and I'd like
to put placeholders in my code for when I port it over to XE2.
Just curious if FireMonkey has an equivilent to GetSystemMetrics.
I'm specifically interested in:

GetSystemMetrics(SM_CXSCREEN)
GetSystemMetrics(SM_CYSCREEN)
----------------------------------------------------------------
If you just need the main monitor size, and not the desktop size
(n monitors sum), you can use this:

uses ..., FMX.Platform;

var   p: TPointF;
begin
  p := Platform.GetScreenSize;
  ShowMessage(Format('X: %f' + LineFeed + 'Y: %f', [p.X, p.Y]));

From: Whiler
http://blogs.wittwer.fr/whiler/
----------------------------------------------------------------
}
unit IniFileStuff;

interface

uses
  Windows, SysUtils, Forms, IniFiles;

type
  TScreenType  = (MainScreen, PreviewScreen);

Procedure InitializeIniFile();
Procedure ResetScreenSection(ScreenType:TScreenType);
Function WindowStateToInt(WSType:TWindowState):integer;
Function IntToWindowState(WSInt:integer):TWindowState;
...
End Code Snippet

So, even though I'm not using FireMonkey today, I've documented my code so when I do go to use FireMonkey I'll have a decent head start.

Don't keep it - Pass it on.
Semper Fi - Gunny Mike

Wednesday, May 9, 2012

Keeping Screen Resolution in Mind

I'm redoing an old Delph 5 app that forced a screen resolution of 640 x 480 on the users. At the time this application was deveopled that screen resolution was acceptable but not anymore. Todays endless arrays of screen resolutions brings on new challenges. One of the things I plan on doing at application start is interrogate the current screen resolution and set my application screen size to a ratio of 80%.

I'm amazed at how much information is available. Here is a nice graph that shows the top 10 screen resolutions in use from 2008 through 2012.





Source: http://gs.statcounter.com/#resolution-ww-monthly-201201-201301-bar


Add MultiMon to the uses clause The button click event lets you move the program between two monitors to get the screen resolution.
procedure TScreenResolution.Button1Click(Sender: TObject);
var
  MonInfo: TMonitorInfo;
  ScreenW, ScreenH : integer;
begin
  MonInfo.cbSize := SizeOf(MonInfo);
  GetMonitorInfo(MonitorFromWindow(Handle, MONITOR_DEFAULTTONEAREST), @MonInfo);
  ScreenW :=  MonInfo.rcMonitor.Right  - MonInfo.rcMonitor.Left;
  ScreenH :=  MonInfo.rcMonitor.Bottom - MonInfo.rcMonitor.Top;
  Label2.Caption := IntToStr(ScreenW);
  Label4.Caption := IntToStr(ScreenH);
end;
Semper Fi - Gunny Mike

FireMonkey Update - 05/13/2012:

I'd like to thank Whiler from StackOverflow for a FireMonkey example using FX.Platform:



Sunday, May 6, 2012

Print to File Options Supported by ReportBuilder

Wow, I'm really impressed with the out of the box, Print to File options that come with ReportBuilder. All you do is set the AllowPrintToFile to True and you are done!

The default Print to File option is PDF. But there are several others. The image formats create one image per page. The HTML option creates an *_htm_images folder for any images that might be on the report.


Image modifed to show all Type options

Print to File accepts the Page Range options as well. I even tried breaking it by printing page 3 of a 2 page report. No errors popped up and no output file was created.

I am extremely pleased with my purchase. ReportBuilder has proven to be everything I expected it to be and more.

Semper Fi - Gunny Mike



A Simple Way to Learn About Methods and Properties

I've decided this time around with Delphi, I'm going to do things as best as I possibly can. A long time ago I met Jeff Duntemann (here's his blog). If you know Jeff, you know he has an astounding vocabulary. I asked him about that once and he responded:
"Michael, the reason for my vocabulary is simple, my parents. When I asked for the definition of a particular word they told me to look it up in the dictionary. And while you are reading about that word, read about every word on both facing pages."
Jeff developed this habit when he was young and he still carrys it with him today. What a simple concept. I have decided to use this idea when I go looking up a the reference to a property or method of a Delph object. Not only am I going to read about the property I was intially interested in, I will read about all the other properties and methods.

Thank you Jeff, I finally figured out how to use your dictionary lookup idea!

Semper Fi - Gunny Mike

How to get the DBDemos Database into ElevatdDB

There are several Delphi tutorials out there that use the DBDemos database that ships with Delphi. I tend to learn much better if I actually perform the tasks outlined in these tutorials rather than just reading or viewing them. However, I'd much rather perform these tasks using ElevateDB, since this is the database I have chosen for all my new applications.

I started looking around to see if someone has done this already. I was looking for a .zip file out there in internet land. I posted to the ElevateDB support group hoping someone had a .zip file I could get my hands on. What I found was much more valuable.

One of the regulars, Roy, said why don't you just use the "built-in BDE migrator" that comes with ElevateDB.
This is one of the coolest things I have done with ElevateDB. I learned so much more going through this process then I would have if I simply downloaded some .zip off the internet.This whole process takes less than 5 minutes from start to finish. It looks very complicated but believe me it's not.

Step 1 - Create a Session Called DBDemos

Make sure you have the ElevateDB Manager up and running. Right-Click on ElevateDB Manager and then choose Create New Session

General Tab

There are two things that need to be done inside the General tab.
  1. Name:  DBDemos
  2. Description:  DBDemos database that ships with Delphi
DO NOT CLICK OK . . Switch to the Local tab

Local Tab

There are two things that need to be done inside the Local tab.
  1. File Folder:  Enter the location for configuration file for this session. Don't worry if this folder doesn't exist, it will get created for you later on.
  2. Large File Support:  Make sure this box is checked.
DO NOT CLICK OK . . Switch to the Login tab



Login Tab


There are three things that need to be done inside the Login tab.
  1. User Name: Administrator
  2. Password: Enter:  EDBDefault

  3. Confirm Password:  EDBDefault
NOW CLICK OK


Click Yes if prompted to create folder.

Step 2 - Create Database Migrators

Now we need to add the database Migrators that come with ElevateDB to the DBMemos session.

  • Expand the ElevateDB Manager by clicking on the + (Plus)
  • You will see the DBDemos session we just created
  • Expand the DBDemos session by clicking on the + (Plus)
  • Right-Click on DBDemos
  • Choose Create Database Migrators


Step 3 - Create an Empty DBDemos database

Now we need to add the database Migrators that come with ElevateDB to the DBMemos session.


  • Expand the ElevateDB Manager by clicking on the + (Plus)
  • Expand the DBDemos session by clicking on the + (Plus)
  • Right-Click on Databases
  • Choose Create New Database...


There are three things that need to be done on the General tab
  1. Name:DBDemos
  2. Description: DBDemos Database that ships with Delphi

  3. Folder: Enter the location where you want the database files to reside. In my example I simply use a DB folder in the same location as the session folder.
CLICK OK WHEN DONE

Click OK when Done

Step 4 - Migrate the DBDemos Database and Into ElevateDB

 We are almost done, this is the final step.

  • Expand ElevateDB Manager by clicking on the + (Plus)
  • Expand DBDemos session by clicking on the + (Plus)
  • Expand Databases by clicking on the + (Plus)
  • Right-Click on DBDemos database
  • Choose Migrate Database...


There are seven things that need to be done on the Migrate Database Source tab.
  1. Migrator: Choose BDE
  2. Ckeckbox: Make sure Include Table Data is checked
  3. Parameters Grid: Click DatabaseName to highlight the grid row
  4. Value: Enter DBDemos
  5. Click Set Paramter Value button
  6. Veify the Parameter Grid shows DBDemos for the DatabaseName
  7.  Click OK
 

Congratulations. You now have an ElevatdDB version of the DBDemos database you can use to follow along with all the Delphi tutorials out there.

Enjoy!

Semper Fi - Gunny Mike

Thursday, May 3, 2012

Rethinking the Use of Stored Procedures

As I mentioned in a previous post, I was absent from Delphi programming for about 7 years. During that time I worked 50-60 hours a week on custom database driven websites using ASP and Microsoft SQL Server. I estimate that this resulted in 200 (plus/minus) websites.

Programming for the web taught me about statelessness, bandwith conservation, and the biggie, SQL Injection. Being the lead programmer I developed standards for the use of stored procedures in everything we did. I learned a lot, made my fair share of mistakes, and developed a deep admiration for stored procedures.

The focus became take a request from a webpage, go to the server, do as much on the server as possible while you are there, and return the results to the webpage. Works great.

So, now I'm back into Delphi and rewriting a desktop application that hasn't been updated since 2004. The jump from D5E to D2010 in itself, has been a challenge. I'm also going from a non-database application to a databse application. The database I have chosen to use is ElevateDB.

One of the reasons I chose ElevateDB was it offers stored procedures. I love stored procedures, they are like black boxes. As long as the interface doesn't change they are good-to-go and offer awesome power.

So, I'm taking all this thinking I developed throughout those web years and applying it straight to Delphi and the application rewrite I'm doing. I know that a stand alone desktop application doesn't have to worry about statelessness, or bandwith conservation, and for the most part SQL injection. But it's hard to re-learn to think... until the rubber meets the road.

The rubber met the road when I decided to build my first report using ReportBuilder. I've never truely had a reporting tool before... even in my web days I built everything from scratch. So, I took the amortization schedule I built as an ElevateDB stored procedure and made that my first report. Everything is going well, the report looks professional, I'm having fun.

Then I start thinking... wow it wouldn't be nice to add a a percentage of payment next to the principal column. Wouldn't it be nice to show the percentage of interest also. And it would be totally awesome to bunch them by the year the payment is due. So, I decide to add two calculated fields to my report, no big deal. PrinPct is simply Payment divided by Principal.

Then I realized...
  • You can't add a calculated value from a stored procedure column
  • You can't do a group by based on the the year of the payment
  • Wow, building a report based on the result set of a stored procedure is not the best approach
"My first thought was, great ReportBuilder doesn't let me do what I want to do. This sucks."
Then I realized...
"It's not ReportBuilder, it's me. My thinking is wrong. Stored procedures are not meant to be reported on... they are meant to do the heavy lifting that makes the data easy to report against"
I'm glad that this happened to me early on in the process. I would have been quite upset had I created a total database with 30-40 stored procedures all built on the assumption that they could be used as datasets for reporting.

Back to the drawing board... another lesson learned.

Semper Fi, Gunny Mike

Wednesday, May 2, 2012

Reporting Tool Survey

Here is an unsophisticated poll to see which Delphi reporting toll is used. Please only vote once.

(Participation in this poll requires a Linkedin account)



Results: Poll closed on May 28, 2012

Thursday, April 26, 2012

Composite Reporting With Mixed Orientation

I have spent the last two weeks researching different reporting tools for Delphi. I currently own Delphi 2010 Professional which comes with Rave built into the IDE. My customers have asked me for a "one-click" printing solution. Instead of previewing and printing each report one-at-a-time, they would like to simply press the [ Print ] buttton once and "presto" all the reports get printed. That is a fairly reasonable request.

Rave

So, I tried firing up the built in Rave tool. My first impression was tremendous anxiety. I literally felt like I was in the cockpit of an airplane staring at all these unrecognizable controls. The database product I use is ElevateDB and I had failed at several attempts to get hooked up to an active dataset. Finally, I somehow stumbled into a control that produced live data. I felt like on was on track.

So, now I'm looking to see how I can create a composite report. I like the idea of providing my customers with a "one-click" solution. However, the reports I need to create are a combination of portrait and landscape orientations. Now, the task has just gotten a little more trickier.

I need to create a composite report that allows a combination of portrait and landscape orientations.

I begin my quest for knowledge like I always do... StackOverflow. I quickly came across the following post regarding Rave. Migrating from QuickReport to FastReport, Rave Reports or what? This sounded interesting seeing how my older programs used Quick Reports. After reading some of the comments it further drove me away from Rave.

Quick Reports

I'm familiar with Quick Reports I even purchased the profession version when I was using Delphi 5 Enterprise. I know Quick Reports lets you create composite reports but does it allow for the mixing of portrait and lanscape orientations withing a single report. The answer is no. Here is the response I received from the developer:

Hi,

It can't be done with the present code. I'm not sure why but mixing report orientations just doesn't work.

Regards, QBS Software Ltd

So, Quick Reports is out. That's too bad because I have already spent several hours with that tool even though it was eight years ago.

FastReport

Now this looks promising. FastReport comes bundled with Delphi XE2 (which I will be upgrading to fairly soon). So far so good. I'm still waiting to here from them wether they support mixed orientation within a composit report. I'll update this post when I find out.

Report Builder (Digital Metaphors)

This is the reason I am writing. The answer is YES. Report Builder supports a composite report with mixed orientation. I sent three emails and received three emails from the sales and support team at Digital Metaphors within a 1 hour span. Not only did they answer my question, they gave me a link to Delphi code that shows an example of how to do this very thing. But wait there's more. . .

Here is their mission statement lifted right off the website...
At Digital Metaphors Corporation we believe that our existence is guaranteed only when we successfully complete the actions listed below on a daily basis:

  • Create software of the highest quality
  • Ensure that customers receive the maximum possible value from our software
  • Go the extra mile in every phase of the software business
  • Make things go right
  • Regardless of circumstances, use intelligence, common sense, and inspiration to create results which are a service to both our customers and our fellow employees
  • I started looking around their website for some nuggets and I came across a download called Learning ReportBuilder. This is a hands-on learning experience. It combines a 145 page step-by-step guide with a report builder tool that thoroughly teaches you how to use their product.

    I definitely felt like I was in the cockpit of an airplane looking at all the controls except this time, there was an instructor sitting right by my side guiding me through each maneuver. The controls will look familiar to any Delphi programmer. If you are in the market for a reporting tool, I urge you to download the Learning ReportBuilder hands-on tutorial before you make a decision. It's that good.

    I want to thank David Cornelius from cornelius concepts for telling me about Report Builder.

    Semper Fi,
    Gunny Mike

    Sunday, January 1, 2012

    Wisdom: Rekindle That Enthusiasm

    Happy New Year!

    I was thinking about my software the other day. I have a piece of software I created over 20 years ago and it still sells today. Somewhere along the line I lost my enthusiasm for this product. Maybe I got busy on other stuff, maybe sales weren't as robust as I thought they should have been. Anyway for whatever reason I seem to have lost enthusiasm for my software... until now.

    A few weeks ago I was watching a show on TV about singers and songwriters who created hits back in the 1960s. And when these artists got up to perform their songs even though these songs are more than 50 years old they sang them with enthusiam and gusto. This got me thinking that I'm missing the boat here with my software.

    If you're like me and you have a software product that's been selling for twenty years and it's still selling today, that tells you people like your stuff. I'm going to say that again. If you have a software product that's been selling for twenty years and it's still selling today, that tells you people like your stuff. So take it from me don't become complacent. Don't take that fact for granted.

    So this year starting today January 1, 2012, a brand-new year, I'm changing the attitude I have about my software. I will treat my software as though it were a hit song. My software is a hit. If you're in the same boat as me I want you to recognize the fact that you have a hit as well. Hits don't come along that often, in fact there are some songwriters who are known as one-hit wonders. They spend their entire lives creating songs and for just one, single solitary moment, everything came together just right and they produced a hit.

    Why is this important and what difference does it make? As you talk about your product are you enthused? People can tell. I need to be more enthused. If I'm not excited how can I expect my customers to get excited. When describing product features and benefits do you sound monotone or are you enthused? If you're not it is fair to expect people to get enthused. And believe it or not there's a group of people out there who have never seen your product before.

    My last assignment for the Marine Corps was Marine barracks Washington DC. And from Memorial Day until Labor Day every Friday night from five o'clock until midnight we were put on our dress blues and entertained the crowd who came to see the parade and the Marine Corps Silent Drill Team.

    The first three or four weeks of every new parade season was exciting. We haven't done this for a while. We get all dressed up in pretty clothes and we walk around representing the Marine Corps's finest. And then the thought of the long, dreaded season would set in. I remember the first time this happened to me and I thought "oh my goodness I have four more months of this."

    So from that point on I told myself, "Michael you are going to meet somebody tonight that has never been to Washington D.C. before. Not only has this person never been to D.C. before, but this is the only time this person will ever come to Washington D.C. in their entire life. You don't know who that person is. So treat each person that you meet tonight as though it's that person."

    If I can do that for the entire Marine Corps parade season, I can do that for my product and so can you. You are going to talk to somebody and your website will meet somebody, who has never seen your product before. This could be a one time shot. You don't know who this person is, so treat everybody as though it's that person.

    Semper Fi,
    Gunny Mike