I'll leave the fun of the Big Feature Writeups (e.g. Live Templates) to others in the blogoshpere that have already started to cover some of the new features of Delphi 2006. Instead, I'll cover some of the more mundane, but very useful new features of Delphi 2006.
Method navigation is described in the "What's New in Developer Studio 2006" file like this: "You can quickly navigate between methods in your source code using a series of hotkeys". You use hotkeys like CTRL+ALT+Up-arrow and CTRL+ATL+Down-arrow to move from one method to the next in a given unit. CTRL+ALT+Home and CTRL+ALT+End take you to the first and last method - respectively - in the unit. There is also the concept of Class Locking which will only let you navigate with those hotkeys within the current class. The documentation on how to toggle this feature is incorrect, though. The correct way to do this is to position your caret in the IDE somewhere in a class, then press CTRL+Q, followed by just the letter L. After you have enabled class lock, you are constrained to method navigation only within that class.
Note: This worked for me with the IDE Classic editor setting. I'm not sure how it works with other settings.
When using a DateTimePicker control, MS assumes that the numeric part groupings (i.e. hour, minutes, seconds, etc.) are entered using 2 digit numbers. However, this means that the text in the control and the DateTime property can often get out of sync.
Take the example where you are just editing a Time and the text of the control is set to "12:00:00 PM". At this point, the DateTime and the text are the same. If you edit the '12' portion by typing '8', no OnChange event occurs, and furthermore, if you try to evaluate the DateTime property at this point it will still be the value for '12:00:00 PM' (e.g. Use a TSpeedButton to evaluate the property since it doesn't make the DateTimePicker lose focus). If you were to type '08' instead, things would be fine. Also, if you were to use the arrow keys to select the minutes part, or tab out of the control, things would be fine. It's just the one case I mentioned above that doesn't work like I would expect.
In order to solve this for my needs, I just did a DateTimePicker1.Perform(WM_KILLFOCUS, 0, 0) before evaluating the DateTime property. Since the control was on a popup window that was going away anyway, any side-effects of doing this won't really apply.
I just got a new Dell Latitude D810. It absolutely flies. However, I was getting very weird playback when playing certain WMV files (e.g. ECO is Child's Play or the PDC sessions). The playback was twinged with orange, and lime green, and other colors where I couldn't see the video. I felt like firing up some Jefferson Airplane to complete the effect.
To solve this, I ended up opening WMP9, going to Tools | Options | Performance and turned the Video acceleration setting down to None. Problem solved.
A version of LINQ that is compatible with the RTM version of VS2005 has been released on this page.
This post doesn't contain any actual technical information. I'm making this post in order to help a Borland R∓D person to debug a very minor, but somewhat irritating problem with the Welcome Page. The problem pops up if you have a post that has <pre> and <code > tags in a post that gets summarized. Here's a sample.
procedure ScrollToLineCol(AMemo: TCustomMemo; Line, Col: integer);
var
Len: integer;
AdjCol: integer;
CharNum: integer;
begin
if AMemo = nil then
Exit;
// Find the character number of the line requested
CharNum := AMemo.Perform(EM_LINEINDEX, Line, 0);
// Make sure we keep the column in this line
Len := AMemo.Perform(EM_LINELENGTH, CharNum, 0);
if Len > Col then
AdjCol := Col
else
AdjCol := Len;
// Add the column to the caret position
AMemo.SelStart := CharNum + AdjCol;
AMemo.Perform(EM_ScrollCaret, 0, 0);
end;
Hopefully, this makes it easy to debug now!
Borland is doing another 24 hours of Delphi on October 24. I'm on from 9:50am-10:20am CDT that morning (for non-US readers, check the BDN article for time conversions)I'll be talking about migration considerations for Delphi 2006, including changes to VCL, DBX, and DataSnap. I'm excited about getting a chance to share some of the things that I've learned about Delphi 2006 and pass on my very favorable experience with that product. Hopefully you can come away from my talk with a few time saving tips when migrating your applications to Delphi 2006. Looking forward to seeing you online that day!
Since Allen Bauer spilled the beans that FastMM4 will be the default memory manager in DeXter, we have switched over to FastMM4 for all of our applications. These applications are heavily threaded MIDAS/COM servers, with internal COM/DLL references. The switchover has been a real eye-opener. Using the debug version of FastMM in order to pinpoint memory leaks, we have found some (more than a couple, but less than "a lot" :)) places throughout all of the applications where we were forgetting to free memory. Another benefit is that the servers have been performing much better under stress than they used to.
In addition, Pierre has been absolutely fantastic. I've had several email exchanges with him, and he is always willing to take bug reports and get fixes, in addition to implementing feature requests. My company has donated to the FastMM project, and I am adding another donation from my pocket since I think this is such a great tool, and a worthwhile thing to support. I would highly recommend that you check this out, and if you find it useful, throw a donation his way!
As I mentioned in my previous post about LINQ and IDENTITY fields, I was getting an exception when trying to update back to the DB. I have since tracked this down to my guess in that post where it was due to the way that I constructed the customer object via foreach. Using that technique, it seems that it keeps a DataReader open longer than needed, which results in this error. I have a workaround, so I can at least share this sample to show you how related tables with IDENTITY fields work. If you want to see the exception, replace the line where I assign the cust variable with a "foreach (var cust in query)".
Things to note in this sample:
- I am mixing and matching objects retrieved from the DB (cust) and locally created objects (o and od).
- You can assign the Order object to the customer either by setting o.Customer to point to the customer, or using cust.Orders.Add(o). The same holds true for adding OrderDetail to Order.
- IDENTITY fields are not initialized upon creation, but they are assigned the actual value that they got when getting inserted to the DB.
- You can link tables together by either using the object references or the actual data field. For example, when building the OrderDetail object, I use od.ProductID to set the value that will get written to the DB. I could also have constructed/received a Product object from the DB and assigned od.Product to do the linking via object reference.
- Linking between related tables is taken care of automatically, even when linked via IDENTITY fields
class Program
{
static void Main(string[] args)
{
Northwind db = new Northwind(
@"Data Source=(local);Initial Catalog=Northwind;Integrated Security=True");
db.Log = Console.Out;
var query = from c in db.Customers
where c.CustomerID == "ALFKI"
select c;
Customer cust = query.ToArray()[0];
Order o = new Order();
o.Customer = cust;
o.Freight = 23;
Console.WriteLine("[PRE] Order.OrderID == " + o.OrderID);
OrderDetail od = new OrderDetail();
od.Order = o;
od.Quantity = 1;
od.ProductID = 1;
Console.WriteLine("[PRE] OrderDetail.OrderID == " + od.OrderID);
//o.OrderDetails.Add(od);
//cust.Orders.Add(o);
db.SubmitChanges();
Console.WriteLine("[POST] Order.OrderID == " + o.OrderID);
Console.WriteLine("[POST] OrderDetail.OrderID == " + od.OrderID);
}
}
I was preparing a writeup on how LINQ handles IDENTITY fields, and there is some mixed news on this front. First off, the good news. The concept of how LINQ will deal with IDENTITY fields is quite solid. The goal is to have the DB return the new value for the IDENTITY field and then populate the object with that new value. Very slick. Back in Delphi 5, MIDAS introduced a property TDatasetProvider.ProviderOptions.poAutoRefresh that was supposed to do this same thing. However, it was never implemented, so it never worked. As a result, you had to use other options, like the one I outlined in my BDN article. So LINQ has a definite advantage here.
The other really cool things about LINQ IDENTITY handling is that you can build up a graph of objects (i.e. Orders and OrderDetails), add the object to a Customer object, and when you do a SubmitChanges(), the IDENTITY fields are updated properly throughout the whole object graph, in addition to linking properly in the DB.
However, the bad news is that the DLINQ implementation is throwing a System.InvalidOperationException because "There is already an open DataReader associated with this Command which must be closed first.". If you are debugging the application and pause long enough before continuing, the data will still get written to the DB. However, I have not been able to get the application to work at all when just running it (i.e. Start Without Debugging). I believe it may be due to my mixed use of retrieving a customer from the DB using LINQ, and then creating Order and OrderDetail objects locally and adding them to the Customer object. I say this because doing a simple add of a Category object all by itself doesn't yield this exception. I also had an instance one time where only the Order was inserted, and not the OrderDetail, thereby invalidating the atomicity of the transaction. I plan on cleaning up the test case and submitting it to MS. Anyone know where this kind of feedback should go?
|