Jump to content

PeterBelow

Members
  • Content Count

    454
  • Joined

  • Last visited

  • Days Won

    12

Posts posted by PeterBelow


  1. On 4/13/2024 at 10:50 PM, cecarnicom said:

    OK, I have solved it.  Very strange solution to me, but problem is definitely tied into the default setting of 16x16 height width in the imagelist component.  I set to maximum allowable of 256 x 256 pix and now the images are loading quite fair.  I find it surprising that a 256 pix max condition exists, and even though it is far short of rather modest image display desired of 600 pix, it will still work fine for my current need.  Ok for now, but still a mystery to me why such low limits would exist for an image component, and why they are set as the default, and what is the actual resolution limit and capacity of display.  Able to move on the next higher priority now though, thanks much,  Best regards, from Clifford.

    The reason is quite simple: the component is based on a Windows common control which is intended to serve as storage for small images/icons used for menu items, buttons, listview items. To reduce the consumption of GDI handles the control merges all added bitmaps into one large bitmap for storage. That's the reason all bitmaps added are forced to the same size, this way the imagelist can calculate where on the combined bitmap each image resides and then paint that part to the target location. There is a maximum size to a GDI bitmap, so this puts limits on the size and number of the images you can put into a TImagelist.

    Looks like the control is simply not suitable for your task.

    • Like 2

  2. Stretching bitmaps to a larger size (e.g. a 16x16 bitmap to 64x64) has this effect. The Windows API function used by the VCL is not very good at reducing the unavoidable degradation in image quality by interpolating the new pixels added between the original ones. You get much better results using vector formats like SVG as source, which can be rendered to practically any size without quality loss as long as the aspect ratio is not too far off the original.

     

    For bitmaps used for UI elements (buttons, menus, listview items etc.) Delphi offers TImageCollection and TVirtualImagelist. The former can store multiple versions (different sizes) of an image and the latter is used to then pick the best fitting version for a particular screen resolution.


  3. 20 hours ago, VilleK said:

    After updating to 12.1 I can no longer use the "Evaluate/Modify" feature (ctrl + F7). I get the message "Cannot load evaluator". Does it work for anyone else?

     

    This is a big issue for me because Ctrl + F7 is my main debugging tool and I've been using that shortcut since the Turbo Pascal days.

     

     

    Works for me without problems (Win32 project with debug build, halted on a breakpoint). But dcc32270.dll does not belong to Delphi 12.1, that uses dcc32290.dll. 270 would be Delphi 10.3 if memory serves.

    Check the registry key HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Embarcadero\BDS\23.0\Debugging\Embarcadero Debuggers\Evaluators, that names the defaullt debug evaluator.

    image.thumb.png.929d7865bf18cbfde573ddf7474de60e.png


  4. 14 hours ago, MarkShark said:

    Hi all!  Is there a library routine or api function that will compare two utf8 strings or buffers without converting them to UTF16?   I'm looking for something analogous to AnsiCompareStr.  Thanks!

     

    -Mark

     

    Depends on what you need to compare for. Equality is easy, that is a simple memory comparison. Larger/smaller implies a collation sequence, which is language-specific. Here converting to UTF-16 first is, in my opinion, the only practical way. Same if you need case-insensitive comparison, since case conversion is also language-specific and may not be applicable for the language in question at all (e.g. chinese).


  5. 8 hours ago, Noor said:

    Hello everyone,

     

    I'm seeking insights on database development practices in Delphi with Firebird. I have two specific questions:

     

    • When it comes to database development in Delphi, what is the recommended approach: utilizing data-aware components like drag-and-drop fields and linking with TFDQuery for CRUD operations, or segregating CRUD operations into separate units for forms? I'm particularly interested in understanding the balance between ease of use, efficiency and code usability. Any insights or examples you can provide would be greatly appreciated.
    • Often, we encounter situations where we need to fix a bug or implement changes in just one form out of many (over 100 forms) within our system. Currently, we update the complete executable, which includes all VCL forms in one project. What would be the best approach to handle such incremental updates efficiently without impacting all clients? I'm eager to learn about effective strategies in this scenario.

     

     

    Thank you for your valuable input.

    Warm regards,
    Noor

    Oy, that's a really open-ended subject. The RAD approach (using data-aware controls and connection/query components you create and configure at design-time) gets you results (= working application) fast but it tends to get messy fast as well, with business logic mixed into form and data moduls, poor reuse of code (unless you plan well ahead). This approach makes proper separation of application layers harder and is excellent at producing apps that are a nightmare to maintain and document. Code can be written to be self-documenting and serve as source for project documentation as well, but how do you deal with all the stuff set at designtime and scattered across dozens or even hundreds of dfm files?

     

    In my opinion RAD is workable for smaller projects with a few database tables and a few forms as well. For anything more complex and expected to have a lifetime of many years with frequent updates it pays to use a proper layered approach (e.g. model-view-controller or similar), with a object-relational mapper (ORM) as interface to the database and a client UI that works with data objects instead of directly talking to database tables, queries, or stored procedures. All the FireDac stuff would be hidden inside the ORM.

     

    Unfortunately Delphi does not come with an ORM, but there are some 3rd-party offerings in that area, including open-source projects like MORMOT. The learning curve can be steep if you have never dealt with such a design before, but it is worth it IMO. I wrote my own ORM for a larger project at work 20 years ago, took about half a year (part time, programming was not my main task) since nothing suitable was available at that time. It has served me well and some of the programs are still in some use...

     

    As for partitioning the app into modules that can be maintained independently: that is what packages are for, but it is not as straightforward as one would hope. It's DLL hell in disguise :classic_dry: and you have to invest some serious though into how you partition your application. Packages are all or nothing, every unit used can only be contained in one single package. All other units requiring such a unit have to get it from the (shared) package, so you have to deploy not only your own packages but also all used RTL and VCL (or FMX) and 3rd-party packages.

    As long as you do not change anything in the interface part of a packaged unit you can replace the build package in the production app with an updated package. If you change an interface you have to rebuild all packages using the modified one, plus the host app. Since forms etc. are declared in a unit interface that severely limits what you can change in such a form unit without major hassle.

     

    Note that this applies to units you want to use from other packages directly. It does not apply to units only used internally by a package. So it is possible to achieve a better isolation of modules if the only exposed part is a factory function for an interface the host app then uses to talk to the internal stuff of the package. In this scenario you can get away with only building with the core RTL and VCL packages and one shared package declaring the interface types used, and actually you can use DLLs (build with packages) instead of full-fledged packages. But this can still degenerate into a maintenance nightmare if you are nor careful...

    • Like 1

  6. 6 hours ago, pcoder said:

    Has anyone seen the case of globalSize() returning more than the requested size?
    It would be much more useful to get the requested size (<= allocated size) to determine the clipboard data size,
    but unfortunately I've found contradicting statements about globalSize().

     

    Pro allocated size:
    learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalsize
    stackoverflow.com/questions/22075754/how-do-i-determine-clipboard-data-size

     

    Pro requested size:
    devblogs.microsoft.com/oldnewthing/20120316-00/?p=8083
    www.labri.fr/perso/betrema/winnt/heapmm.html
    www.joelonsoftware.com/2001/11/20/a-hard-drill-makes-an-easy-battle/

    GlobalAlloc rounds the requested size up to the next multiple of 8 (at least), so it is normal for GlobalSize to return more than the requested size if that is not a multiple of the used granularity. In fact the MS docs for GlobalAlloc explicitely state that GlobalSize should be used to get the allocated size of the block requested. Depending on the use of the block you may have to store the actual data size with the data itself if the extra bytes allocated by GlobalAlloc may cause a problem for the data recipient.

    Note that the granularity used may depend on OS version, Microsoft is pretty vague on this.


  7. 2 hours ago, PeterPanettone said:

    This is not concisely logical, as it should word-wrap the Caption when the "text is too wide for the control" AND NOT PREVENT the word-wrap when it is already Multi-Line (if WordWrap = False).

     

    And, from a purely practical point of view, what sense would it make to explicitly truncate the text if it's too short for the control? Wouldn't it rather be more practical to automatically word-wrap the Caption when the text is too short for the control?

    It may not be logical but Windows has a lot of these little inconsistencies that have accumulated over time by bolting more functionality on existing control classes. TButton is a wrapper around the Windows button control, and its behaviour is determined by the set of button control styles, in this case the BS_MULTILINE style, which the Multiline property surfaces.


  8. 4 hours ago, davornik said:

    I am using DateTimePicker1.Format as ' ' to set the value to an empty string in TDateTimePicker (using it as dtkDate).

    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      DateTimePicker1.Format:=' ';
    end;
    
    procedure TForm1.DateTimePicker1Change(Sender: TObject);
    begin
      DateTimePicker1.Format:='';
    end;
    
    procedure TForm1.btnResetClick(Sender: TObject);
    begin
      DateTimePicker1.Format:=' '; //set as Empty
      DateTimePicker1.Date:=Date;
    end;

    Change event does not fire if I select today's date. How can I detect a click on the calendar if today's date is selected or clicked somewhere in the calendar itself?

    The control (a Windows common control under the VCL surtace) has no real concept of an "empty" state. The usual way to use it is to set the shown date to Today (it does that by default if memory serves) and accept that if the user does not change it. If your requirements really need a way to detect that the user has entered a date you can use an additional TCheckbox that disables the picker unless it is checked.


  9. 13 hours ago, bdw_nz20 said:

    I've just been playing a little bit with one of our applications to allow HDPI, not done much on this till now.

     

    Its working when changing the scaling for all windows i.e. change it in the display settings to 125%, 150% etc.

     

    I was just wondering if it was possible, like other applications can, to just adjust the VCL application rather than all windows.

     

    For example some windows allow Ctrl+ and Ctrl- keys to scale up and down of the window.

     

    You can experiment with the ScaleBy method all TWinControl descendants inherit, including forms.


  10. 13 hours ago, EugeneK said:

    I'm not saying doing it without interface, I mean hiding this implementation detail from the users. I.e. not allowing to manually implement this interface in code. This won't break any documented uses of anonymous methods.

    But that's the way it currently works! Of course you can design your own interface + implementor that acts exactly like anonymous methods act now, but that would still not be the same implementation the compiler creates behind the curtain now.


  11. 20 hours ago, Clément said:

    The unit I'm tracking does not appear in the graph.

    If I remove the path or rename the unit the project doesn't compile.

    I deleted all dcus from the library root to be sure only valid ".pas" files are compiled.

    But the compiler error message should pinpoint the uses clause the  (now missing) unit appears in, no?


  12. 3 hours ago, PeaShooter_OMO said:
    
    type
      TMyRec = record
        Int : Integer;
        Str : String;
      end;
      PMyRec = ^TMyRec;
    
    var
      LPointer : Pointer;
    begin
      New(PMyRec(LPointer));
      PMyRec(LPointer)^.Int := 200;
      PMyRec(LPointer)^.Str := 'Blah';
      Dispose(PMyRec(LPointer));
    end;

    I do not know why this approach bothers me but for some reason it does. I am casting a Pointer to a PMyRec to use it with New() and Dispose().

     

    I do not get a memory leak from this so I am assuming it will be operating correctly. Am I right?

     

     

     

    It will work but there is no point in doing it this way. Declare LPointer as type PMyRec and work with that. Don't use C idioms with Delphi, that only confuses things.


  13. On 3/12/2024 at 12:35 PM, Andrew Spencer said:

    Using Delphi 11.3

    I often have a situation where the .Visible property of a control depends on the .Checked property of a TCheckBox (or similar). To do this, I typically write an OnClick event for the TCheckBox, and set the .Visible property of the target control in a single line of code

    e.g.

    TargetLabel.Visible := SourceCheckBox.Checked;

    I also make sure to call this OnClick event at startup, if I happen to need to load the .Checked state from a previously-saved registry or Ini file entry.

     

    I thought that LiveBindings was designed to help me get rid of needing to write OnClick events. So to test I set up a TForm with a TLabel and a TCheckBox, with LiveBindings Designer as follows:

    image.png.d2dc98ef782e21938330c8b339a503b1.png

     

    My expectation was that, from then on, the TLabel.Visible property will simply follow the state of the TCheckBox.Checked property. No additional code would need to be written.

     

    BUT, it didn't work exactly as I expected.

     

    Question 1 : When I run this, the TCheckBox.Checked property initialises to the Design-time setting of TLabel.Visible. Setting TCheckBox.Checked to True at Design-time is ignored. Why is it not the other way around i.e. The TCheckBox.Checked value, at Design-time, determines the TLabel.Visible property at Run-time? That's the way that the arrow is pointing. This only appears to happen AFTER the application is up and running i.e. clicking on the TCheckBox will alter the visibility of the TLabel accordingly.

     

    Question 2 : How can I get this initialised, at run-time, so that the two controls start off operating correctly? I have only found that setting BOTH the TCheckBox.Checked AND the TLabel.Visible properties to the same value gets things going correctly.

     

    Question 3 : Programatically changing the TCheckBox.Checked state at runtime does not cause the TLabel.Visible property to change.

     

    Question 4 : Have I just totally misunderstood what I can do with LiveBindings, or how to implement what I want?

     

    If this is for VCL: I centralize such code by overriding the UpdateActions method of the form and updating the control states from there. Much easier than fiddling with multiple event handlers or live bindings.


  14. 4 hours ago, jesu said:

    Hello This code:


     

    
      if MessageDlg('Welcome to my Delphi application.  Exit now?',
        mtConfirmation, [mbYes, mbNo], 0, mbYes) = mrYes then
      begin
        MessageDlg('Exiting the Delphi application.', mtInformation,
          [mbOk], 0, mbOk);
        Close;
      end;
    

    shows icons in previous versions, but not in Delphi 12.

    This is too obvious to be a bug. Am I missing something?

    Thanks.

     

    This has come up before. Microsoft changed the UI guidelines and now recommends to not show an icon for confirmation dialogs. D12's vcl.dialogs unit was modied accordingly.


  15. 12 hours ago, Clément said:

    Hi,

    I'm using D12 for this project. I'm using VCL Styles, but the same behavior happens without styles.


    The first icon will call TFileOpenDialog.Execute. During this call, both TitleBarPanel.Caption and the button.caption icon "blink".

    Why is that?

     

     

    Here's the code:

     

    
    procedure TfrmMain.btnLoadFolderClick(Sender: TObject);
    begin
      FileOpenDialog1.Options := [fdoPickFolders];
      fLogFiles.edtLogFiles.BeginUpdate;
      try
        // Flicking starts
        if FileOpenDialog1.Execute then begin
        // Flicking ends
           var lEncoding := fEncodingList[fCurrentEncodingIndex];
           {...}
     finally
     end;
    end;

     

    You should not show a dialog inside a BeginUpdate/EndUpdate block in my opinion. That may be the source of your problem and it also serves no purpose i can see here.


  16. 25 minutes ago, dormky said:

    The TLabel was just for demonstration purposes, I'd like to be able to do that with all components...

    You cannot, it is only possible for TWinControl descendants.

    • Like 1

  17. You cannot, subcomponents in a custom component are created in an overridden constructor. But there is an alternative: Create a new frame in the designer and place groupbox and edits on it. Save the frame unit (after changing the Name property of the frame to something descriptive), best into a folder where you stash code units you want to reuse in other projects. To use the frame in another project just add its unit to the project.

    A frame behaves much like a component, you can drop multiple instances of it on forms, panels or other containers. But it can also hold code like a form, in event handlers or methods, and this code is shared between all frame instances. You can modify properties of the controls on the frame in the designer for each instance separately.


  18. Well, there are many ways to approach such a project. Using 20 images and 20 buttons may look appropriate at first glance but you have already identified the problem of code duplication and it is not the only one. Even getting the layout riight may be more work than you think.

    If you have a number of identical objects think array, and for visual stuff think grid. If your target platform is Windows and you intend to use the VCL instead of the FMX framework I would use a TDrawGrid as the main UI. For the start and exit buttons use TButtons hosted on a TPanel aligned alBottom on the form. The TDrawgrid is placed above the panel and aligned alClient. With 20 images you probably want 4 rows of 5 images each, so set the rowcount and colcount to 4 and 5, respectively and the FixedColCount and FixedRowCount to 0. Add a handler for the grid's OnDrawCell event, that is where you place the code to draw button (for an image not uncovered yet) or image. Then you need a way to store the state (covered/uncovered) for each cell and an identifier for the image a cell is holding. To keep the info for a cell together the natural storage is a record, so add a type definition like this above the form class:

    type
      TGameCell = record
        ImageIndex: Integer;
        Uncovered: Boolean;
      end;

    The game state is then a 4x5 array of TGameCell:

    const
      CNumRows = 4;
      CNumCols = 5;
    type
      TGameState = array [0..CNumRows-1, 0..CNumCols-1] of TGameCell;
    

    Add a field to the protected section of the form class:

      FGameState: TGameState;

    Each element of the game state array corresponds to a cell in the drawgrid. OK so far.

    Now you need some storage for the images. The most appropriate is a TImageList, so drop one on the form, set its Height and Width to the size of the images you want to use and then load the images into the list. The index of a given image will be used to identify it in the TGameCell. I asume with 20 cells you will have 10 images. Set the defaultcolwidth and defaultrowheight properties of the grid to values a bit larger than the images, say 4 pixels more. That gives you spacing between the tiles you have to draw.

     

    OK so far. Add a handler to the form's OnCreate event. There you place the code to initialize the FGameState array. The Uncovered members of the TGameCell records will start out as false, so you only need to set the ImageIndex to specify which image to show in each cell, using the index from the imagelist.

     

    That is the basic setup. Now you have to figure out how to draw the cells to resemble the usual memory game card. The event handler for the grid's OnDrawCell event has the following parameter list:

    procedure (Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState)

    Sender is the grid, you will have to draw on its Canvas, so add a variable for that to the handler sceleton the IDE created for you:

    var
      LCanvas: TCanvas;
    begin
      LCanvas := (Sender as TDrawgrid).Canvas;

    ACol and ARow tell you which cell to draw and, conveniently, these directly correspond to the matching TGameCell indices in FGameState. If you leave the DefaultDrawing property of the grid at the default True the VCL will have already drawn the cell background, so you can concentrate on drawing the image or button. Which it is you determine by looking at FGameState[aRow, aCol].Uncovered. If true you use the imagelists Draw method, passing it the LCanvas and the Left and Top members of the Rect parameter, adding 2 for the spacing to both. To draw the button the simplest way would in fact be to add a suitable image to the imagelist as well and draw it the same way. An alternative would be the DrawFrameControl Windows API method, but that is much more complicated to use.

     

    OK, so the form will now show the grid when you run the project (at designtime the grid is empty). Now you need to detect clicks on the cells, so add a handler to the grid's OnMouseDown event. Its X and Y parameter tell you where the mouse went down inside the grid but not which cell that is. Fortunately the grid has a MouseToCell method, so call that to get the cell, look at the Uncovered member of the corresponding TGameCell. If true exit directly, if false set it to true and call the grid's Invalidate method to get it to redraw. This would also be the place from which you then evaluate the game state, i. e. see if this action has uncovered the mate to the last tile uncovered (if so stop the timer), has uncovered the first tile (if so remember the cell coordinates and start the timer), and so on. If the timer fires you would just set both cells to Unconvered:= false, stop the timer, and Invalidate the grid to redraw.

     

    OK enough loafing around! Now get to work :classic_cool:

    • Like 1
    • Thanks 1

  19. What root key do you use? HKEY_CURRENT_USER depends on the user running the process in question (IIS probably) and that may not be what you expect. And which access rights do you specify when you try to open the key in question?

    • Like 1

  20. On 2/25/2024 at 3:00 PM, emileverh said:

    I know it's a compiler problem, but why can they do it with classes and not with records....

    Because classes are reference type and records are value types. A variable/field of a reference type will always have a a known size, sizeof(pointer). A value type does have the size defined by the type and that would be unknown at the point of the foreward declaration. A one-pass compiler cannot handle that.

×