Wednesday, September 18, 2013

New 2013 Delphi Developer Days Event

I am pleased to announce a special edition of Delphi Developer Days 2013 and Mobile Development Workshop, to be held this December 4th, 5th, and 6th near London's Heathrow Airport. This three-day event includes the classic two-day Delphi Developer Days 2013 sessions by me and Delphi expert Bob (Dr.Bob) Swart, where Bob and I present joint sessions, where we present in-depth topics together, as well as separate sessions, where we break out into separate rooms to present diverse topics.

The optional third day is the Delphi Developer Days Mobile Development Workshop, where Bob and I work with you to develop and deploy mobile applications to your iOS * or Android ** mobile device. This one-of-a-kind workshop includes focused discussions by Bob and me where we explain aspects of mobile development, followed by hand-on sessions where you implement the concepts we describe. During this time we are available to answer questions, provide guidance, and help you build and deploy your sample applications.

You can attend all three days of this special event, just the first two days, or only the Delphi Developer Days Mobile Development Workshop. Discounts are available to previous Delphi Developer Days attendees, and their is a special discount for the hands-on workshop available to attendees of this spring's Delphi Developer Days. There are also discounts for early registration.

Lunch is provided on each day that you attend. All attendees registered for the first two days will receive our Delphi Developer Days 2013 course book, containing more than 400 pages of material covered in our presentations. Similarly, attendees of the Delphi Developer Days Mobile Development Workshop will receive material on iOS, Android, and FireDAC mobile development written by Bob and me. Space is limited, so register now to reserve your seat.

Visit www.DelphiDeveloperDays.com for more information, including registration, agenda, software and hardware requirements, location details, and more.

* iOS devices include iPhone, iPad, and iPad mini devices
** Android devices must be based on ARMv7 compatible CPUs. Android Jelly Bean is preferred, though most Ice Cream Sandwich and Gingerbread devices are supported

Wednesday, August 7, 2013

Dragging and Dropping in DBGrids Revisited

Please note: a bug was found in the original code upload. It was reported in a comment below.

I have uploaded a code example that fixes the problem (see link at the of this post). However, that code can only handle drag/drop operations in a DBGrid so long as the Options property of that DBGrid includes the dgTitles flag.

I am leaving this post, and a link to the updated code, live on this blog, as I know that the feature described here is a valuable one. I will continue to work on addressing the remaining issue, and will update this blog and the code upload once I have accomplished that.

Once again, thank you for your patience.

About a year ago I posted a blog that described in detail how to implement drag and drop operations with a DBGrid. The technique that I described employed a ClientDataSet as a key element in the drag and drop process, and it is one that I have used extensively in the year since.

There was one glich, however. During this past spring's Delphi Developer Days tour with my colleague Bob Swart (a.k.a Dr.Bob) one of the attendees asked what would happen if the DBGrid displayed less than all records in the underlying ClientDataSet. It turns out that this is an issue with my original code example (and it has since been pointed out elegantly in a comment posted to the blog post I linked to above). If the first record displayed in the DBGrid does not correspond to the first record in the underlying ClientDataSet, my original code would incorrectly place a record dropped into the top position of the DBGrid in the top-most position of the ClientDataSet, instead of above the record onto which the drop operation targeted.

Initially I did not worry about this discrepancy too much, selfishly I will admit. In my real world usage, the number of records being dragged or dropped never exceeded the visual display capacity of the DBGrid. But it is a problem, and Michael Riley's posted comment on my earlier blog motivated me once again to look for a solution.

As that last paragraph implied, I did try to find a solution earlier, in response to the question posed by the Delphi Developer Days attendee. However, I could not see a way to determine the record number of the underlying DataSet when the visible rows of the DBGrid were greater than the number of records in the DataSet, and the DBGrid was scrolled down.

Having taken up the challenge again, I turned to every coder's best friend, Stack Overflow. There I posted my question, hoping that someone had discovered an appropriate mechanism. Even though I posted the question on a Saturday, long-time Stack Overflow contributor Uwe Raabe was quick to post an answer. With this information in hand, I was able to quickly adapt my earlier drag and drop example to correctly perform drag and drop into and within a DBGrid under all relevant conditions.

What Uwe proposed was the creation of a class helper for the TDBGrid class. His class helper looked like the following:

TDBGridHelper = class helper for TDBGrid
public
    function RecNoFromVisibleRow(Value: Integer): Integer;
end;

His implementation of the RecNoFromVisibleRow method is shown here:

function TDbGridHelper.RecNoFromVisibleRow(Value: Integer): Integer;
begin
  Result := DataSource.DataSet.RecNo - Row + TopRow + Value;
  if dgTitles in Options then
    Dec(Result);
end;

This worked like a charm, and solve the problem that I specifically asked about in my Stack Overflow post, which concerned the first visible record in the DBGrid. Testing my updated drag and drop example revealed another issue that I had not anticipated. Specifically, if the DBGrid contained more records than the number visible in the DBGrid, and the user dropped a new record past the end of the last visible record, that record would be placed at the end of the underlying ClientDataSet, rather than after the last visible record.

This problem was easy to solve by modifying Uwe's code in the following manner.

function TDBGridHelper.RecNoFromVisibleRow(Value: Integer): Integer;
begin
  if Value = -1 then
  begin
    Result := DataSource.DataSet.RecNo - Row + TopRow + VisibleRowCount
  end
  else
  begin
    Result := DataSource.DataSet.RecNo - Row + TopRow + Value;
    if dgTitles in Options then
      Dec(Result);
  end;
end;

Over the past several days I have tested this code pretty hard, and it appears to work under all conditions. A link to the download for the final code example is located at the end of this blog post.

I will leave it up to you to follow the link I provided at the outset of this post to read the details about how this dragging and dropping in DBGrids works. I will, however, point out the minor changes that the introduction of this class helper has on the original code. In addition, I will discuss the use of class helpers, and offer an alternative solution, in the form of an interceptor class.

My original OnDragDrop event handler on the DBGrid began like the following:

procedure TForm1.DBGridDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  GridRow: Integer;
  OriginalRow: Integer;
begin
  GridRow := DBGrid.MouseCoord(X,Y).Y;
  if GridRow = 0 then
    GridRow := 1;
  if (Source is TListBox) then

It now looks like this, where the initial adjustment of the GridRow variable is replaced by a call to the RecNoFromVisibleRow method:

procedure TForm1.DBGridDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  GridRow: Integer;
  OriginalRow: Integer;
begin
  GridRow := DBGrid.MouseCoord(X,Y).Y;
  GridRow := DBGrid.RecNoFromVisibleRow((*ClientDataSet.RecNo, *)GridRow);
  if (Source is TListBox) then

However, when dragging and dropping within the DBGrid itself, GridRow values of 0 do need to be adjusted to 1, so that happens later in this method method, once a within grid drag/drop is confirmed, as shown here:

  if Source = Sender then
  begin
    //We are dragging within the DBGrid
    if ClientDataSet.IsEmpty then exit;
    OriginalRow := ClientDataSet.RecNo;
    if (OriginalRow = GridRow) or (GridRow = -1) then
      exit
    else
    if GridRow = 0 then GridRow := 1;
    MoveRecord(ClientDataSet, OriginalRow, GridRow);
  end;

I also added an additional line of code to reposition the current record in the underlying ClientDataSet at the end of the segment associated with a drop from the ListBox (dropping from outside of the DBGrid). This line, and the several above it, are shown here:

  //Insert the new item at the drop position
  ClientDataSet.InsertRecord( [GridRow,  TListBox(Source).Items[TListBox(Source).ItemIndex], RandomRange(1, 101)]);
  //Make the dropped record the current record
  ClientDataSet.RecNo := GridRow;

Other than the declaration of the class helper and the implementation of RecNoFromVisibleRow, all of the remaining original code is untouched.

Now, let me address the use of the class helper. First let me say that using a class helper is a perfectly sound solution. It does, however, have one drawback. You can have only one class helper for a given class. If you already have a class helper for TDBGrid (or if Embarcadero introduces one), this new class helper will make that one unavailble.

An alternative solution is to use an interceptor class. An interceptor class is one that has the same class name as the existing class, and extends that existing class, but which has a scope closer to the code that uses it than the original class. When those conditions exist, the interceptor class takes precedence, and its overridden or introduced methods, properties, fields, and such, are available to your code. In addition, since the interceptor class is a descendant of the original, any protected (other than strict protected) members of the original class are visible to it. This is important, since Row and TopRow, which are protected members of the Vcl.DBGrids.TDBGrid class, need to be access from within the RecNoFromVisibleRow method.

In my code sample, the interceptor class is declared prior to the TForm class, which results in any TDBGrid instances appearing on the form being instances of my interceptor class. This class declaration looks like the following (in which I have included a little bit of the TForm declaration as well):

type
  TDBGrid = class(DBGrids.TDBGrid)
  public
    function RecNoFromVisibleRow(Value: Integer): Integer;
  end;
  TForm1 = class(TForm)
    ListBox: TListBox;
    DBGrid: TDBGrid;

Of course, you must implement the RecNoFromVisibleRow method in the interceptor class. In this case, since the interceptor class is named TDBGrid, the only difference between the implementation of the interceptor method and class helper method is the class name (TDBGrid versus TDBGridHelper). As a result, I will not repeat that code here. Otherwise, the remaining code is exactly the same. A helper class adds the RecNoFromVisibleRecord method to the Vcl.DBGrids.TDBGrid class, while the interceptor class introduces this method, and TDBGrid instances on the form are instances of the interceptor, instead of the interceptor's ancestor.

One final note. While I declared the interceptor class in the same unit as my form declaration, I could have just as easily declared it in some other unit. The only requirement for the use of the interceptor class in my form is that the unit in which the interceptor class is declared must appear in the interface section uses clause, and later in that uses clause than the Vcl.DBGrids unit.

Here is the link to the updated code. Please enjoy.

Friday, April 19, 2013

Delphi Developer Days 2013 Course Book

Over the past month and a half, Bob Swart and I have been writing the material for Delphi Developer Days 2013 (http://www.DelphiDeveloperDays.com). In all we produced seven chapters each. Bob and I each wrote two of the joint sessions, and we each wrote our own four individual sessions. We also each wrote our half of the Tips, Tricks, and Techniques session. This is why the course book has 14 chapters while Delphi Developer Days includes 13 unique sessions.

Loy Anderson, who manages Delphi Developer Days, had given us an early April deadline in order to have the course books printed in time for Chicago. It was a lot of work, but we did it, and Loy worked hard to compile our chapters into a unified book as we finished each chapter. She submitted the book for printing on April 7th, and we fully anticipated getting the books in time for our Chicago event, which begins on May 6th.

To our amazement, the books arrived this week, both those intended for Chicago as well as a European delivery of books for our Frankfurt and Amsterdam events. The book is over 400 pages in length, and we are not talking slideshows here. As you can see in the following picture, our chapters are detailed, and include screenshots and code samples.

Of course, this detail is necessary for our individual sessions. During these sessions, Bob and I break out into separate rooms to present our specific topics. If you decide to go to Bob's presentation, you can always catch up on my presentation by reading my chapter on the topic in the book. But we feel that detailed chapters are also important for our joint sessions, giving you something to refer back to long after Delphi Developer Days is over.

I am especially pleased this year with our content. Like I have done in the past with Marco Cantù, Bob and I worked hard to create a solid selection of topics that should be of interest to almost every Delphi developer. Some of our sessions cover the absolute latest information on Delphi, including Delphi XE4, which was announced just over a week ago. These talks include iOS development, Delphi's new NextGen compiler, and FireDAC, the new data access component framework.

There is also plenty of material to engage developers using older versions of Delphi. For example, there are talks on multithreaded development, DataSnap, debugging, browser-based clients, and Windows services. When appropriate, these talks discuss features added in recent versions of Delphi. However, the bulk of the information in these chapters applies to older versions of Delphi (some going back as far as the original Delphi).

In addition to working to find a balance of topics, Bob and I also worked to organize the talks intelligently. For example, talks on data access (including FireDAC) and multithreaded development are presented prior to those on DataSnap (which assumes knowledge about data access and multithreaded programming).

Likewise, we tried to pair our individual presentations in a meaningful way. When one of us is speaking about one of the most recent versions of Delphi, the other is presenting a topic that appeals to developers using older versions. Likewise, we tried to match an Internet-related presentation with one that applies to traditional workstation applications.

Although the book is printed (and in our hands), we all continue preparing for the actual events. Loy has a lot of organizational details to complete, including the printing of name badges and onsite signage, as well as arranging for lunches and our various guest speakers. Bob and I continue to work on our talks, creating our slideshows, and adding to, and improving, the demo projects that we'll use.

We are really looking forward to this year's event, and I am looking forward to this first year presenting with Bob Swart. For those of you who have already registered, we look forward to seeing your there. While Chicago has sold out, we still have space available in both Frankfurt and Amsterdam, but we expect these cities to sell out as well. Furthermore, at the time of this posting we still have a 10% discount for early registration in Europe, which ends April 30, 2013. See http://www.DelphiDeveloperDays.com for details and pricing.

Delphi Developer Days is fun, and we are looking forward to it. We hope you are, too.

Thursday, February 14, 2013

Happy 18th Birthday, Delphi!

It's Delphi's 18th birthday, and I am celebrating by coding in Delphi XE3 (the 16th version) and posting this picture of my first copy of Delphi.

Monday, February 4, 2013

Delphi Editor Key Combination Table Updated


In May of 2009 I blogged about a table of Delphi editor keystrokes that I assembled and made available online. That table, which was relatively complete (incomplete might be a more honest appraisal), was more or less current as of Delphi 2009. I had intended to keep this table up-to-date, but like so many casual obligations, intention met reality, and the table has been left untouched, until now.

What motivated the update is that this past December I had the pleasure to cross paths once again with fellow Embacadero MVP and Delphi advocate Brian Long. We were speakers at the annual Sofware Developer Network conference in The Netherlands, were one of Brian's sessions was titled "IDE Productivity Tips." A big part of his presentation covered useful editor keystrokes.

We got talking after his presentation, and I mentioned my negleted key combination table and asked if he'd care to take a look and help bring it up to date. He agreed, but I really don't think either of us could imagine how much work it was going to take. Now, two months later, after many email exchanges, and hours of review and research, we are prepared to share our efforts and publish the updated table of Delphi editor keystrokes and key combinations.

The updated table now includes the many key combinations that have been added in the four releases of Delphi since I published the original table, and boasts a whopping 185 different editor key and key combinations. The new list also includes those editor keystrokes that are active during debugging, which were missing entirely from the original list.

Brian has a keen eye. That, combined with his extensive knowledge of Delphi, significantly improved the scope and consistency of the original table. I am grateful for his effort, and hope that you will appreciate it as well.

Edit: I will make an effort to keep this table updated, and have made several changes since the original 4 February 4 2013 post. The most recent version of this table was updated on 6 February 2013.

You can access the latest version of the updated table using the following link: http://www.JensenDataSystems.com/DelphiEditorKeyTable.pdf

Wednesday, January 23, 2013

Delphi Developer Days 2013 Cities and Dates

I am pleased to announce the cities and dates for this year's Delphi Developer Days tour. We will be in Chicago May 6th and 7th, Frankfurt on May 30th and 31st, and Amsterdam on June 3rd and 4th.

Delphi Developer Days are intense, two-day events that blend the in-depth coverage of formal training with the atmosphere and networking opportunities of a small conference. Joining with me in offering this year's tour is renowned Delphi expert Bob Swart (Dr. Bob). Bob is an Embarcadero MVP, winner of the coveted Spirit of Delphi award, as well as a developer, consultant, and author. His skills and Delphi knowledge make him uniquely qualified to replace my previous Delphi Developer Days partner, Marco Cantù, who became the Delphi Product Manager this past fall.
 
Delphi Developer Days includes joint sessions, presented by both Bob and me, as well as simultaneous tracks, where Bob and I break out into separate rooms to present focused topics. Like past Delphi Developer Days tours, we will be covering many of Delphi's newest features as well as topics that apply to older versions of Delphi. This year's offerings include database development, mobile applications, SOAP (Simple Object Access Protocol) and REST (REpresentational State Transfer), DataSnap, Windows services, debugging, migrating to FireMonkey, and the always popular Tips and Tricks session. Additional sessions cover threads and thread synchronization, browser-based clients, as well as gestures and touch. For a full list this year's sessions, visit www.DelphiDeveloperDays.com.
 
Also making a return is the popular Delphi Developer Days course book. This extensive manual includes papers written by Bob and me and covers all of our Delphi Developer Day sessions. This means that while you'll have to select which talk to attend when Bob and I break out into our separate sessions, you will still receive the material, including source code, from all of our talks. Past year course books have run hundreds of pages in length, and have included more content than most books on software development. We expect this year's course book to be no different, and Bob and I are in currently in the process of writing it.
 
In addition to our sessions, Delphi Developer Days includes a keynote presentation from an Embarcadero representative, and well as a different guest speaker in each city. We also include open Q&A sessions each day where you can ask questions about any Delphi-related topics. We also have a raffle at the close of the second day where you have an opportunity to win valuable prizes contributed by our many sponsors.
 
All of our Delphi Developer Days 2013 locations are easy to get to. Our Chicago event is close to Chicago's O'Hare airport, and the hotel offers a free shuttle bus to and from that airport. Our Amsterdam location is across the street from Amsterdam's Central Station, and our Frankfurt location is one and a half kilometers (one mile) from Frankfurt's main train station (Frankfurt am Main Hauptbahnhof), from which you can take a taxi. There is also a city bus that runs from the train station and stops right in front of the hotel. Hotel locations, travel details, and more are available at www.DelphiDeveloperDays.com.
 
Seating is limited to 42 attendees in each city. These limits ensure that attendees get an opportunity to interact with me and Bob, as well as network with each other. However, we sold out a number of cities last year, and anticipate that this will happen again this year. As a result, you should register early in order to ensure your spot in the city of your choice.
 
You can also earn a discount if you register early. There are additional discounts for previous Delphi Developer Days attendees, as well as for companies that register three or more employees.
Bob and I, as well as Delphi Developer Days coordinator Loy Anderson, are very excited about this year's tour. We think we've put together an exceptional program, and we hope to see you there.
 
For more information, and to register, please visit www.DelphiDeveloperDays.com.