Thoughts from Dan Miser RSS 2.0
# Tuesday, September 14, 2004
Yup, it's time to talk about ActiveForms again. :-)

If you're using ActiveForms, then you need to know about an ugly little secret. If you use MyDelphiForm.ActiveControl := Edit1, then you will get an exception stating 'Cannot focus a disabled or invisible window'. The reason for this is due to the implementation of TCustomForm.SetActiveControl. This setter method is called when you try to set ActiveControl on a form. The first thing it does is this:


    if not ((Control = nil) or (Control <> Self) and
      (GetParentForm(Control) = Self) and ((csLoading in ComponentState) or
        Control.CanFocus)) then
      raise EInvalidOperation.Create(SCannotFocus);
Note the part where it is trying to assert that the root ParentForm for the Control you are trying to set focus to is the same as the current form. This works fine in regular windows applications, but unfortunately in ActiveForms, the root ParentForm is the actual ActiveForm. Most people use that form as nothing more than a container to then host their forms inside. I wrote about this technique in a previous Delphi Informant, and it has been posted plenty of places on the Internet as well (e.g. Conrad Hermann had the first mention of this that I can remember).

The workaround is to use code like this to set the ActiveControl:


var
  ParentForm: TCustomForm;
begin
  ParentForm := GetParentForm(Self);
  ParentForm.ActiveControl := Edit1;
Tuesday, September 14, 2004 10:37:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Delphi
# Thursday, September 09, 2004
An ActiveForm will not receive a WM_ACTIVATE message when initializting since WM_ACTIVATE only goes to the top level windows, or in this case, the IE browser window. By sending our own WM_ACTIVATE, we get initial focus set to the ActiveForm, but more importantly, the WMActivate method in TCustomForm calls TCustomForm.SetActive. This in turn, sets the Active property which means that the form has focus. This is important later on, e.g. in TCustomForm.SetActiveControl, the focus will never be set to the ActiveControl.

For example, the code below will do nothing in the default case. It will work fine after you apply the work-around mentioned later on.


var
  ParentForm: TCustomForm;
begin
  ParentForm := GetParentForm(Self);
  ParentForm.ActiveControl := Edit1;
end;

The bad behavior is also apparent when using TPageControl, since TPageControl.ChangeActivePage tries to set the ActiveControl when changing pages.

The simple work-around for this is to call the following in your ActiveForm code: PostMessage(Handle, WM_ACTIVATE, WA_ACTIVE, 0);

Since I use PARAM tags in my OBJECT tag to pass parameters, I am doing this in IPersistPropertyBagLoad method after creating my Delphi form. After doing this, everything is working great.

Thanks to Steve Trefethen for listening to my original vent. :-)

Thursday, September 09, 2004 10:01:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Delphi
# Tuesday, September 07, 2004
There. I said it. I feel better now. I did some digging on how to remove it from my XP system and came up with a couple of links:
Microsoft's sanctioned approach
Unofficial removal

I used the second method since I recall using it before on my machine (before my latest rebuild).

So what do I use for an IM client? Right now, I use Trillian. I like the fact that it has multi-protocol support. Everyone always thinks their IM client is best. Who am I to argue? I'd rather be neutral and still be able to chat with people without having 4 different IMs installed.

Postscript: I also looked at GAIM a while back. It was OK. Maybe it's gotten better over the past year or so, but I still came back to Trillian.

Tuesday, September 07, 2004 11:35:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [6] -

# Friday, August 06, 2004
Whiteboard with Anders Hejlsberg is a must-watch video from an informal presentation he gave at TechEd 2004. The conciseness, clarity and depth of knowledge is impressive. He covers a range of topics and talks about design decisions that were made to help us understand why something is or isn't part of C#. All I can say is I want to be like Anders when I grow up. Hmmm. Probably too late. :)
Friday, August 06, 2004 10:16:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
.NET
# Monday, July 19, 2004
OK, so I'm not the first, and not even the second to welcome Mark Edington aboard. But I thought that my former roomie would appreciate the well-deserved props. Mark has been a force in the DB area, which is obviously near and dear to my heart. Abstract datasets, MIDAS, ADO, XML, and BDP have Mark's fingerprints all over them - and that's a real good thing. I'm also looking forward to a rousing blog entry on how to use $L properly. ;-) One piece of advice though, Mark. Don't take a vacation. It's hard to get back into the swing of blogging after a vacation, oh, say for 10 days in DisneyWorld.
Monday, July 19, 2004 4:07:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [1] -

# Tuesday, July 06, 2004
In order to find out all of the columns that have been changed in a DataSet, you need to resort to code similar to the following. I sure liked the way MIDAS/DataSnap handled this better. A simple call to NewValue, and off you go. Another thing to keep in mind, DataRowVersion.Proposed is only valid between BeginEdit()/EndEdit() calls, so you are forced to write code like this instead:

      if (!dataSet1.HasChanges())
        return;

      DataSet ds = dataSet1.GetChanges();
      foreach (DataTable tbl in ds.Tables)
      {
        foreach (DataRow dr in tbl.Rows)
        {
          for (int i=0; i < tbl.Columns.Count; i++)
          {
            if (dr[i, DataRowVersion.Current] != null && !dr[i, DataRowVersion.Current].Equals(dr[i, DataRowVersion.Original]))
              textBox1.AppendText(tbl.TableName + "." + tbl.Columns[i].ColumnName + " changed (" + dr[i, DataRowVersion.Original] + " -> " + dr[i, DataRowVersion.Current] + ")" + Environment.NewLine);
          }
        }
      }
Tuesday, July 06, 2004 2:04:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [2] -
.NET
# Sunday, June 13, 2004
Even though this is a .NET post, the concept absolutely transfers to classic Delphi as well. The other day, I needed to solve a problem in C#. Given a pseudo-code architecture like this:

class Base {}

class Concrete : Base {}

class Normal
{
  public Concrete ConcreteProp { get; set; }
}
and usage like this:

Base prop = new Concrete();
Normal obj = new Normal();
obj.ConcreteProp = prop;
I would receive (rightfully) a compiler error. Now, understand a few points before firing off comments.
  • The architecture must remain as-is. The classes above are actually FCL classes which I obviously can't change.
  • The usage needs to remain similar to the one shown above. I want to be able to store a reference to any class that descends from Base and use it in Normal.ConcreteProp later on.
  • Obviously, I could just throw a typecast in here to make things work, but this is the end-result of the test case. My requirements also dictated usage of Activator.CreateInstance and Reflection. I can't add references and typecasts to things I don't know about, so this must be truly dynamic.
.NET (and Delphi) don't have any concept of dynamic typecasting. So in order to solve the problem, I ended up taking a different approach. By using an extra Activator.CreateInstance to create a new Concrete obejct, I can then use that new object to assign over with no problems. After creating the object, I then copy over all of the base properties to the newly created object. So far, this has worked pretty well. However, it is not without it's downsides. The main drawbacks I see to this approach are:
  • I am not using the same object reference I stored, which means more memory usage
  • I cannot easily assign all properties from the cached Concrete object to the newly created one. In order to do this fully, I'll have to use Reflection to walk through all of the properties and set them all (if possible). For now, I know which properties absolutely must be assigned, so I just assign those.

Edited to add: OK, so I butchered the problem description in my haste. Mea culpa. I think I just stumbled into one of my own pet peeves. :-) Here's the actual code that doesn't work in case you're interested:


System.Data.Common.DbDataAdapter adapter = sqlDataAdapter1; 
System.Data.SqlClient.SqlCommandBuilder cb = new System.Data.SqlClient.SqlCommandBuilder(); 
cb.DataAdapter = adapter; 

 

Sunday, June 13, 2004 4:49:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [3] -
Delphi
# Thursday, June 10, 2004
This may be old news to some of you, but I just came across a paper titled Simple Application Framework for VCL by DevExpress. DevExpress has some killer third-party products, including the QuantumGrid. They offer support on a variety of platforms and languages, including Delphi, Kylix, ActiveX, and .NET. Their components work very well, are well-designed, and add pizazz to your application. This paper covers some best practices for Delphi application development. However, if you're a .NET guy, they have a version of the paper for .NET, too.
Thursday, June 10, 2004 12:02:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Delphi
Navigation
Archive
<September 2004>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Dan Miser
Sign In
Statistics
Total Posts: 375
This Year: 3
This Month: 0
This Week: 0
Comments: 654
Themes
Pick a theme:
All Content © 2012, Dan Miser
DasBlog theme 'Business' created by Christoph De Baene (delarou)