Design-time drag and drop in Delphi? - delphi

Before Delphi 2006 (I think) introduced the TFlowPanel and TGridPanel, I did a control that was similar in concept. It still does a couple of things those controls do not do, and when upgrading my code to Delphi 2009, I decided to add a couple of enhancements to that as well.
Right now, the order of the child controls is determined by their creation order. The FlowPanel and GridPanel show a better way with ControlIndex and other filtered properties, but I was wondering if there is a way to handle drag and drop reordering in design-time? As far as I can tell, dragging an edit control and dropping it onto my panel doesn't call anything that I can access at design-time.
I was half-fantasising about a way to either detect the drop operation directly, or to perhaps detect when a control is moved so I can determine where it should go.
Any ideas?
Update:
OK, got it working. The container control was already overriding AlignControls to manage the placement of the controls. When you drag the nested control and drop it, AlignControls is again called. I then compared the new coordinates of the control with the other controls in the list and moved it to the appropriate position.
There were a couple of problems that I had to work through (mostly related to the many calls to AlignControls) but the basic concept is simple enough. Thanks to all the commenters for all the help.

You can't drag a control that's already on the form and drop it onto your panel. Dragging is only for moving a control, not for changing its parent. To change the parent, cut and paste.
If the control is already on your panel, and you want to move it to another position on your panel, then the panel can control the layout by overriding the TWinControl.AlignControls method. When a control is moved, its SetBounds method is called, and among the things tha happens is that it calls AlignControl(Self) on its parent window. That calls AlignControls. Look in Controls.pas, and you'll see that that's a complicated method, but it's what is responsible for the layout of the children on a control, and that's exactly what you're planning to change.

Perhaps some of these suggestions might help.
You can re-parent a control in the designer without having to do cut-and-paste. View the structure pane, and simply drag the visual control to the node of another parent in the structure pane. If you have things in a flowpanel, drag everything out of the flow panel and drag them back in the order that you want them to be.
(You can re-parent ANY visual control this way, without changing anything other than its parent. I highly recommend doing it this way.)
You can view the form as text, and move the declaration order around in there -- but obviously you'll need to be careful when editing the "resource" file directly.
You can set tab order in the designer, so you could make a different control based on tab order that works as you want. You can right click on the form and change the creation order of the non-visual controls, but that doesn't work with visual controls.

Have you tried to write an "OnDragDrop" event for your grid component, where you check if your component is in design mode?
I haven't written such a component yet, but I don't see why the event shouldn't trigger.

Related

Creating a forms editor in Delphi

My goal is to create a simple forms editor like the one that we find on Delphi IDE.
Right now the user can select and add the components making it parent of a TPanel that is the holder of the form. For simplicity, please consider also TPanel as the visual components added to the form.
I have 2 missing parts I want to find out ideas/code to help complete:
1 - how to move the created visual component? The same effect that in IDE for moving the visual component, for example Tpanel, around, chaning its top and left position
2 - how to draw that hooks for the component with focus on the form editor
3 - how to resize using the hooks
I only want the part related to handle the visual part. I am not generating DFM or anything like that.
Simply put your moving code needs to do this:
When the mouse goes down, check if the mouse position is over a control that can be dragged. If so, then set a variable named FDragControl to refer to that control. This code lives in an OnMouseDown event handler.
When the mouse moves, if FDragControl is not nil, move the control. This code lives in an OnMouseMove event handler.
When the mouse goes up, set FDragControl to nil.
That's pretty much all there is to it. The main nuance is that you must also remember the X, Y values of the mouse when the drag commenced. So in your OnMouseDown handler you write:
FStartMousePos := Point(X, Y);
FStartDragControlPos := Point(FDragControl.Left, FDragControl.Top);
And then in the OnMouseMove your position code reads:
FDragControl.Left := FStartDragControlPos.X + (X-FStartX);
FDragControl.Top := FStartDragControlPos.Y + (Y-FStartY);
You will also need to capture the mouse when you start dragging.
The resizing code is similar. Again, you need to decide in the OnMouseDown that you are resizing rather than dragging, but the code still involves handling mouse down, move and up events.
As for painting, you need to force a repaint whenever one of your event handlers changes a property that will influence the visual appearance of your form. You can use the value of FDragControl to decide whether or not to use special drawing of your control and indicate that it is being dragged. And likewise for resizing.
I've not coded up a full working implementation since your question is high level and conceptual. The implementation is down to you.
// I have made this an answer as I have just read your latest update which really should have been made as an edit to your original question but, anyway.
You can download the Cindy Components Pack and use the cyResizer Component which will do pretty much everything you need and is very customisable as well.
You can download it from here: http://sourceforge.net/projects/tcycomponents/
Searching more for an answer I could find these articles:
How to Move and Resize Controls at Run Time
http://delphi.about.com/library/weekly/aa102505a.htm
How to Add Size Handles to Controls being Resized at Run-Time
http://delphi.about.com/library/weekly/aa110105a.htm
Pretty much with all the information to complete this task with source code example.
These articles show how to implement and use a TMover class. I have done it and work correctly.
I have also downloaded the TcyComponents Pack and used the TcyResizer. It is a full featured form editor with pretty much everything that is required for a Delphi like forms editor. I recommend. It comes with source code and works fine with XE2 version.

How to revert a frame to inherited when it contains a control aligned to client?

I sometimes design a frame where there are controls all over the place. It could be a grid with align=alClient, or maybe some panels and splitters. Or it could be something else entirely. Anyway, when I want to "revert to inherited" I have to make space for right-clicking by setting align=alNone and move a control, or edit the DFM directly. This is somewhat cumbersome. Granted, this happens rarely enough not to be a big deal, but still, it bugs me. It would help if I could right-click in the structure view, but alas, this doesn't work.
Has anyone else noticed this, and if so; How do you deal with it? Like me, with silent (well, so far at least) annoyance, or have you found a brilliant solution?
Select the control in the Form Designer. If the control is obscured by a child control, then select the child control, and then press Esc to select the parent. Press Shift+F10 to activate the context menu.

Add shortcut to a TForm or Panel.Transparent?

To thwart the nit-pickers, let me start with, I searched here with this and could not find an answer, and yes, also I did scroll through the "Similar questions."...
Adding shortcuts to a TForm
I want to drag and drop some shortcuts from the Desktop to a TForm in my application. I am using Anders Melander's brilliant Drag Drop Suite (DDS).
I tried putting a TImage on the form but the DDS does not drop to an Image so I added a TPanel with a TImage on it. I could then drop on the panel and assign the image to the TImage.Picture. Problem was the Panel has no Transparent Property so the shortcut on the form looks clunky with the visible Panel behind it.
I need to be able to drop to the TImage or make the underlying TPanel transparent.
Can anyone help with code-snippets for either of those options, or better yet, a method of dropping a Shortcut directly on to my Form.
Thanks
Coincidentally I needed to make a TWinControl (the base for every visible control with a window handle, including TPanel) transparent. I found numerous results and applied them to this answer.
It's been a while since I implemented drag and drop, but I assume you call some API and pass it the handle of the panel? That answers the question why you can't use TImage. TImage is a graphic control, a control without a handle, that relies on its parent for recieving messages and drawing itself.
It should be possible to use the form, though, since that has a handle too.
If the TImage is directly on the TForm, then let the TForm handle the drop, no TPanel needed. OLE Drag&Drop operations (which Ander's components implement) provide coordinates where dragging and dropping occurs. The TForm should be able to detect when a drag is over the area occupied by the TImage and what type of data is being dragged, and only allow dropping of supported types within that area, extracting the dropped data and updating the TImageas needed, and denying anything else that does not match that criteria.

Fluid Form Layout in Delphi

We have developed a software. In this software we are show and hiding a few controls on various input screens depending on various situations.
When we hid a control what happens is that the space occupied by that control is left as it is and layout looks very bad at times esp. in screens that have larger numbers of controls. Our client does not like this and has asked us to do something about this.
My question:
Is there some way by which we can create Fluid Layouts so that when a control is hidden the rest of the controls automatically adjusts themselves to fill the empty space left by the control hidden and when the control is show they should automatically make way for the control and adjust themselves accordingly.
I know we can achieve this by coding but that will require a lot of code in each screen for adjusting the layout. I am looking something which will reduce coding in each screen as there are 80+ screens.
Please suggest some way which is less error pron and can get rid of unnecessary coding in each input screen.
I think your best option is to use a component that handles the layout of your vcl controls on your form in runtime (depending on the conditions that you define). I recommend you try the Devexpress ExpressLayout Control
you can find two great demo videos here
ExpressLayout Control - How to Customize Layout Views
ExpressLayout Control - Create and Customize a Simple Layout
(source: devexpress.com)
You can check these features
Auto-Management - Control groups and individual control elements are automatically managed by the Layout Control. You never worry about pixel-by-pixel positioning.
Form auto-sizing - The form can be automatically resized to fit its contents best.
Bye.
Now, I'm not sure how complex layout you have, but I guess you can use TFlowPanel and/or TGridPanel for this. Flowpanel has a nice handling of components that change visiblity. I'm not sure how well gridpanel handles the same...
What kind of controls are you dynamically hiding, and what do you mean with auto fill space?
I do not know if it is as this simple: place controls on panels, and use align alTop/alClient/alBottom. When you hide a panel, all other panels will move automatically up.
One problem though: if you want to show a panel again, the order of panels can sometimes be screwed up... Can be fixed by manually setting .Top property, or "hide" by setting .Height := 1;
What I would do with a complex layout is actually split it up into several tabs. This has two advantages. It simplifies the form layout, and allows you to show and hide whole tabs depending on choices made in other tabs.
Raize Components have a TRzFlowPanel UI component. Does exactly what you're after.
Use TRzFlowPanel to put an empty flow panel on a form. The major difference between a traditional panel and a flow panel is the way in which controls are placed. With a traditional panel, you place a control (such as a button) in a specific location. You can freely move that control to any location within the panel using the mouse. In a flow panel, each control is placed in a specific location, regardless of where you place it with the mouse. The automatic location is controlled by the FlowStyle property. For example, using the default FlowStyle property of LeftRightTopBottom, the first control you add to the flow panel snaps to the top left corner. The second control that you add snaps next to the first control, and so on.

How to select and move multiple controls at runtime

I have created some custom controls (TCustomControl) in Delphi that I can move them at runtime but only one by one. How I can select two or more of these controls, at runtime again, and move them around all together with the mouse?
Thank you.
Another comercial solution would be from DevExpress: LayoutControl. It allows for drag and drop, grouping, full rearrange, hiding and adding of components at runtime.
If you can't find any simpler way, you can always do it manually. Keep a list of all the selected controls. When the drag operation begins, make another list, this one containing TPoint values indicating how far on both axes each control's Top and Left properties are from the mouse's position. Then, as the user drags the control, continually update the selected controls to keep them at the proper relative positions to the mouse pointer.
I once used a component named handles, that if I remember correctly wasn't too difficult to update to the later versions of Delphi and supported multi-select.
How about a commercial solution? The screen shot shows alignment tools, which would suggest that it supports multi-select.

Resources