Jump to content

Uwe Raabe

Members
  • Content Count

    2907
  • Joined

  • Last visited

  • Days Won

    169

Posts posted by Uwe Raabe


  1. 1 hour ago, Rollo62 said:

    but best of all as early as possible in the process,

    I'd like to ask for more details about the term early in this context. Is this targeting the initialization sections of some units or more the code in the dpr begin end section?

     

    I admit that I often fell into the trap to put too much code into the initialization section, but I found that it heavily reduces the possibility to tweak the behavior based on a configuration or on a per project basis in case these units were used in several projects. That was a driving force for the invention of Cmon.Initializing.

    Quote

    Cmon.Initializing establishes a way to control initialize code for other units into the call to Application.Initialize. This allows to make adjustments before, which would be near to impossible if the initialize code would execute in the units initialization section.

    While it is still a long way to get all (probably better most of) my projects suffering from the above drawback, this approach is almost mandatory for new projects now.

     

    As a couple of units of CmonLib make use of this, one can find some hints for usage in the examples (still successfully procrastinating the CmonLib documentation, I know).

     

    Let me show a simple example from Cmon.Messaging.Dialogs.Vcl. This is how it would be written the old way:

    var
      Instance: TDlgMessageHandlerVcl = nil;
    
    initialization
      Instance := TDlgMessageHandlerVcl.Create;
    finalization
      Instance.Free;
    end.

    As a result, simply using this unit will create the instance, which registers itself catching the corresponding messages.

     

    And this how it looks making use of Cmon.Initializing:

    var
      Instance: TDlgMessageHandlerVcl = nil;
    
    { will be called in Application.Initialize after all other initialization code has been executed }
    procedure InitHandler;
    begin
      if TDlgMessage.AutoRegisterHandler and TDlgMessageHandlerVcl.AutoRegisterHandler then
        Instance := TDlgMessageHandlerVcl.Create;
    end;
    
    initialization
      TDlgMessageHandlerVcl.AutoRegisterHandler := True;
      TInitialize.AddInitProc(InitHandler);
    finalization
      Instance.Free;
    end.

    Now creating the instance will take place during Application.Initialize, which allows us to write some code tweak the behavior. As of my personal preference I put all this pre-initialize code in s similar named procedure, called immediately before:

    program DialogsDemoVCL;
    
    uses
      Vcl.Forms,
      Cmon.Messaging.Dialogs.Vcl,
      Main.VclForm in 'Main.VclForm.pas' {MainForm},
      Utilities in 'Utilities.pas';
    
    {$R *.res}
    
    procedure PreInitialize;
    begin
      { Per default using Cmon.Messaging.Dialogs.Vcl will automatically register the containing message handler during
        Application.Initialize. To optionally disable auto registering of all DlgMessage handlers this is one place to do.
        You need to add Cmon.Messaging to the uses to make it compile. }
    //  TDlgMessage.AutoRegisterHandler := False;
    
      { You can as well disable automatic registration of indivudual handlers. That implies to leave the corresponding setting
        above at True of course. }
    //  TDlgMessageHandlerVcl.AutoRegisterHandler := False;
    end;
    
    begin
      PreInitialize;
      Application.Initialize;
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TMainForm, MainForm);
      Application.Run;
    end.

    You can find a similar approach in Cmon.Messaging.Logging.TextFile


  2. 1 hour ago, Dmitry Onoshko said:

    but leaves OnMessage (and any other event define in the base class) with the ugly TCustomProtocol<TMessageType> parameter that the users of TFooProtocol and TBarProtocol should write defining their event handlers.

    What hinders you to declare your own types for that? Then you can write your event handler methods just like before.

    type
      TFooProtocol = TCustomProtocol<TFooMsg>;
      TBarProtocol = TCustomProtocol<TBarMsg>;

     


  3. There are some constants declared in System.pas telling the history:

      OlderLocaleOverrideKey = 'Software\Borland\Delphi\Locales'; // do not localize
      OldLocaleOverrideKey = 'Software\Borland\Locales'; // do not localize
      NewLocaleOverrideKey = 'Software\CodeGear\Locales'; // do not localize
      NewerLocaleOverrideKey = 'Software\Embarcadero\Locales'; // do not localize

     


  4. You somehow misread my advice. The DefaultDrawColumnCell call should be in the non-boolean case, where all non-boolean fields are handled:

    procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer;      Column: TColumn; State: TGridDrawState);
    const
       CtrlState: array[Boolean] of integer = (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED) ;
    begin
      if (Column.Field.DataType=ftBoolean) then
      begin
        DBGrid1.Canvas.FillRect(Rect) ;
        if (VarIsNull(Column.Field.Value)) then
          DrawFrameControl(DBGrid1.Canvas.Handle,Rect, DFC_BUTTON, DFCS_BUTTONCHECK or DFCS_INACTIVE)
        else
          DrawFrameControl(DBGrid1.Canvas.Handle,Rect, DFC_BUTTON, CtrlState[Column.Field.AsBoolean]);
      end
      else
        DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
    end;

     

    • Thanks 1

  5. Delphi 11.3 is running in DPI aware mode per default. You have basically two options:

    • in the High DPI designer options switch to automatic mode. That will scale the form according to the current Windows scaling and change all the positions and sizes in your DFMs
    • start the IDE in DPI unaware mode. There should be a suitable entry in your Windows start menu.

  6. 1 hour ago, Dmitry Onoshko said:

     

    
    type
      TSomeType = MyTypes.TSomeType;
      TOtherType = MyTypes.TOtherType;
      ...

     

    This kind of approach is usually seen when types previously declared in that unit have been moved to another one. Otherwise existing code would no longer compile.

     

    My personal preference would be to add all needed units to the uses clause. As always there might be exceptions with valid reasons.
     


  7. What Delphi version are you using? TBitBtn supports Images since Delphi 10.4. If by any chance you are able to use one of the more recent Delphi versions, I suggest to use the image list approach. It is way easier to maintain, especially when you plan to support High DPI in the future.

    • Like 1

  8. If you use the Glyph property of TBitBtn, this will indeed be quite tedious. The preferred way is to use the Images property to assign an image list and set the ImageIndex/ImageName and DisabledImageIndex/DisabledImageName to the proper icon. There are also such properties for the Hot, Pressed and Selected states.

    • Like 1

  9. This usually happens when MMX has been installed for all users, i.e. as an admin. That will use HKLM for the Experts entry, which will be copied to HKCU when a user starts Delphi. As the installer didn't add these registry entries, it refuses to remove them when uninstalling (this is standard behavior of InnoSetup). 

     

    Actually, these kind of quirks are the reason why Install for me only is recommended. If you are the only user at the system it doesn't matter at all and if you are not, each user will have its own copy of MMX installed (they can even have different versions).

     

    I always try to clean up as neatly as possible when uninstalling, but I didn't find a reliable way to do that without jumping through hoops with the registry and several user folders.

    • Like 3

  10. I know, that probably won't solve your problem, but I usually get rid of these kind of enumerations in favor of proper ones supported by RTTI. The numerical values are handles by record helpers:

    type
      TReportTypeCode = (reportTypeCodeSTR, reportTypeCodeLCTR, reportTypeCodeCDR,  reportTypeCodeLVCTR, reportTypeCodeEFTR);
      TReportTypeCodeHelper = record helper for TReportTypeCode
      private
      const
        cMapping: array[TReportTypeCode] of Integer = (102, 106, 113, 14, 145);
        function GetAsInteger: Integer;
        procedure SetAsInteger(const Value: Integer);
      public
        property AsInteger: Integer read GetAsInteger write SetAsInteger;
      end;
    
    function TReportTypeCodeHelper.GetAsInteger: Integer;
    begin
      Result := cMapping[Self];
    end;
    
    procedure TReportTypeCodeHelper.SetAsInteger(const Value: Integer);
    begin
      for var idx := Low(Self) to High(Self) do begin
        if cMapping[idx] = Value then begin
          Self := idx;
          Exit;
        end;
      end;
      raise EInvalidOperation.Create('invalid Integer value for TReportTypeCode');
    end;

    Now there is no more casting to and from Integers. 

    • Like 7
×