Jump to content

PeterBelow

Members
  • Content Count

    560
  • Joined

  • Last visited

  • Days Won

    13

Everything posted by PeterBelow

  1. 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
  2. PeterBelow

    ISAPI and regedit

    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?
  3. Well, it may work but I fail to see the usefulness of such a construct.
  4. PeterBelow

    What new features would you like to see in Delphi 13?

    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.
  5. Generics in Delphi don't work the way you seem to think they do. To use the generic type T inside a method of the generic class with a specific type the compiler needs some minimal information on what T can be. That is what generic constraints are for. Unfortunately the constraints supported at the moment are quite limited and will not cover what you intend to do.
  6. PeterBelow

    Do runtime created forms close the TFDQuery connections?

    All components that have the form as Owner (which includes all components dropped on the form in the IDE designer) are automatically destroyed when the form is destroyed. So your query components are destroyed but whether that automatically releases serverside resources depends on how the component, especially its destructor, is implemented. But a TFDQuery will close the query when it is destroyed, so your scenario is OK.
  7. PeterBelow

    Two strange probems in D11 (code+video)

    One thing may be a problem. Both the cmtx22 and cmtx33 functions use a (global ?) variable cmtx but do not clear it first. So the array elements not set by the functions will have values carried over from previous uses of this variable. Sorry, but the whole design looks fishy and error prone to me and seems to use plain procedural programming 40 years out of date. You define a large fixed size array type, variables of which will not only waste a lot of memory for elements never used, but you also have no convenient way to figure out from a variable what the dimensions of the actually used part are. In my opinion you should build a TConfusionMatrix class, which internally uses a 2D dynamic array (array of array of extended) to store the values of the matrix. The constructor of the class would take the needed dimension and size the internal array accordingly. You could have additional constructors to build the matrix from your string representation or a 1D array of values, methods to return a string representation, to compare an object of the class to another, properties to return the dimensions of the matrix or to get or set element values. Since the matrix object knows the size of the internal array it would be easy to check report indexing errors and so on.
  8. PeterBelow

    Class named TQuickRep already exists

    Does this project use QuickReports? If so, did you install this package on that machine? The exception class indicates that the error occurs while loading components from a DFM or perhaps a report template but the error message is a bit weird. Perhaps it is worded incorrectly and should say that only one instance of TQuickRep is allowed in this template...
  9. PeterBelow

    Share FireDac Tables over DLL boundries

    Using Sharemem may give you a common memory manager for host app and dlls, but you still have separate code "instances" for all RTL and VCL stuff you use on both sides, which includes any global variables the units in question may use internally, the Application and Screen objects etc. If this worked in the past you have simply been lucky , but your luck appearendly ran out when you switched to the more complex FireDac framework. The only safe way to share object references between modules is to use packages instead of DLLs. Unfortunately, packages are all or nothing, you have to build host program and your own packages (which replace the DLLs you now use) with packages and deploy all the required run-time packages of the RTL, VCL, and whatever else (e.g. 3rd party stuff) to the user's PCs. Not fun. And if you load packages dynamically better not try to unload them in code as well, that has been a source of hard to track errors due to premature RTL etc. unit finalization in the past (may have been fixed but is hard to verify). If you want to stick with DLLs you need a different design that shares interface references instead of object references between the modules. Host and DLLs would use a common unit with the interface declarations; all exported functions would use interface references instead of object references. For the classes you now share you would have to write a layer of TInterfacedObject-derived wrappers that implement the interfaces that allow you to safely call methods and access properties od the wrapped objects.
  10. PeterBelow

    ListView

    A TListview is not designed to host other controls. Take a look at TControllist, it may be more suited to your requirements. Another option would be a TScrollbox with a number of instances of a custom frame (which are created in code as needed, each representing the data of a suitable object or record).
  11. Not easily, no. You would have to draw the text yourself (OnDrawCell event) and also adjust the row height as needed. Perhaps showing the full text in a popup hint would be easier to implement.
  12. PeterBelow

    Edit features

    See the TextHint property of TEdit, that's probably what you are looking for. If not give us more detail.
  13. PeterBelow

    activex word97 word

    The problem is likely the huge jump in the Word versions. Newer Word versions use a different document format and plainly refuse to edit documents in ancient formats like Word97, although you can still view them (my latest version installed is Word 2016, so still much older than Word365). You may have to rebuild your template dot with the new Word version to get it to work as you want.
  14. PeterBelow

    How do I tab into a TDBRadioGroup ?

    A radio group, by its very nature, must have one of the options checked. If you need to indicate that none of your two options applies then that has to be added as a third option that is checked as default. What type of database field have you tied the group to?
  15. PeterBelow

    Integer Overflow - Exception

    This may not do what you expect it to do. ALength may be int64 but the compiler will calculate the right-hand side using a smaller integer type into which both the constant 2 and errorlevel fit and only extend the result to int64 before storing it into Alength. So the calculation may well overflow the range of the type used for the calculation, and since D12 has enabled overflow and range checks by default you get an exception. Change the code to Alength := int64(2) shl errorlevel; to force the use of int64 for the calculation.
  16. The Install packages dialog affects the current project only if you have one open, and the IDE basic configuration if you have no project open. Changing the latter will not affect any projects you created before, though. To get rid of the package for good you have to edit the registry key HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\21.0\Known Packages (for D10) and rename the items for the dclbind*.bpl packages or move them top the "Disabled Packages" key.
  17. PeterBelow

    Preventing and allowing things to happen in a Thread???

    Put all controls on the form on a client-aligned panel and then simply disable the panel while the background thread is active. Also use a handler for the OnCloseQuery event to prevent closing of the app while the thread is running. If you have a main menu bar tie all menu items to actions and disable the actionlist while the thread is running. Just don't forget to enable all the stuff again when the thread is done.
  18. PeterBelow

    debug message

    Delphi forms tend to recreate their window handle at the drop of a hat. So move the DragAcceptFiles calls from the events to overridden CreateWnd and DestroyWnd methods, that makes sure the handle changes are properly handled.
  19. See this blog post. Sometimes part of the pages work, but getit is the least stable. If you have it try to installing from the ISO.
  20. PeterBelow

    Delphi 12 Welcome Page: Open Recent

    You know that this forum is not the right place for feature requests, do you? The replacement for Embarcadero's Quality Portal will hopefully be online soon, post a request there.
  21. PeterBelow

    Delphi 12 Application showing two windows on the taskbar?

    Check the project dpr file. Have you set Application.MainformOnTaskbar to false there? Does the form class override the CreateParams method or CreateWnd?
  22. PeterBelow

    Procedure/Function Commenting..

    It might be worth noting that XML documentation tags also work inside a normal Delphi comment (enclosed in braces {}) instead of these horrible triple slash comments copied from C# along with the XML tags themselves. Much easier to maintain in my opinion if a tag content spans several lines.
  23. PeterBelow

    Delphi 12 Application showing two windows on the taskbar?

    The posted image is not large enough to make out any details unfortunately. Is this a MDI application by any chance? That is an area in the VCL where some major changes were made.
  24. PeterBelow

    Testing a Semaphore??

    You call GetLastError after the Create call. It will return ERROR_ALREADY_EXISTS if the named semaphore already exists. That also applies to other named synchronization objects, like mutexes. See the API function CreateSemaphoreW. TSemaphore inherits a property LastError from THandleObject, it may serve as well.
  25. If you're german you may want to post on the german DelphiPraxis site, link is at the top right of this page. Anyway: in the Tools -> Options dialog you can find the default folders the compiler will put package-related files in (*.bpl, *.dcp). But these can be overridden in the project options for each package project, so check there too. The project options should have "$(BDSCOMMONDIR)\BPL" as package output folder to use the IDE default. The run-time package has to be in a folder that is on the system PATH, since such packages are loaded by the OS following the same search logic used for DLLs (in fact packages are DLLs with some Delphi-specific extensions). The design-time package can be in a folder not on the PATH since the IDE loads design-time packages using the full path. The IDE installer adds the default folder for run-time packages to the PATH, but that may fail if your PATH is already very long, so better check it. As for installing: do you get no context menu if you right-click on the project name in the Project Manager view?
×