I was asked to make some changes to a project another developer did 10+ years ago in Delphi 7. This is a proprietary bit of code, so I'll be extremely specific.
The "Container" is a TScrollBox and the panels inside are TSpkRollPanel's -- a collapsible or expandable TPanel derivative.
I hope the image below explains everything. It's really simple. I'm supposed to make the TSpkRollPanel elements drag/drop vertically ONLY so they can be arranged in the desired order. I've spent a few hours getting up to speed on Delphi (Which I haven't seen in 10+ years)
If anyone could point me in the right direction, I'd appreciate it. I'm Delphi literate, just rusty.
With standard TPanel panels the following works fine, and most probably with your panels as well. The steps are the following:
Select all panels
Set Align property of all panels to AlTop
Set DragMode property of all panels to dmAutomatic
Switch to event view in Object Inspector
Double click in entry field of OnDragDrop to create event handler
Double click in entry field of OnDragOver to create event handler
If the names of the two created event handlers include identifier for a specific panel, you may want to rename the event handlers to reflect that they are common for all panels.
6 Finally, add code to event handlers
procedure TForm1.PanelDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
TPanel(Source).Top := TPanel(Sender).Top - 5;
end;
procedure TForm1.PanelDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := True;
end;
The solution works so that when a panel (A) is dropped on another (B) it (A) will take that panels (B) place in the alignment order, pushing (B) and the other panels down.
Related
I want to be able to use one procedure to center all the components on a form. This is the kind of thing I'm going for:
procedure TForm4.centerComponent(x: Tobject);
begin
x.Left := (Form4.ClientWidth - x.Width) div 2;
end;
I would only be passing built in components (memo, label, edit etc...)
I get the feeling this is either not possible or if it is its probably not best practice
This is easy, but you must be careful about terminology:
A TObject is any Delphi object. It need not be a control. It doesn't even need to be something you can drop on a form.
A TComponent is an object you can drop on a form. It might be a visual control (like a button, a label, or an edit box), or it might be a non-visual component (like a TActionList).
A TControl is a visual control, like a button, a label, an edit box, or an animated analogue clock.
The above classes are ordered by inheritance.
So, you want a procedure that acts on TControls in general:
procedure Centre(AControl: TControl);
var
Parent: TWinControl;
begin
Parent := AControl.Parent;
if Parent = nil then
Exit;
AControl.SetBounds(
(Parent.ClientWidth - AControl.Width) div 2,
(Parent.ClientHeight - AControl.Height) div 2,
AControl.Width,
AControl.Height
);
end;
Every TControl has Top, Left, Width, and Height properties, as well as the SetBounds method, which we use above.
Notice that I centre the control in its parent window. (A control's Top and Left values are always relative to its parent.)
Now, there are two kinds of controls in Delphi:
Controls that are actual Win32 windows (with HWNDs).
Controls that are not actual Win32 windows.
Only the former kind of control can have child controls. These controls derive from TWinControl. That's the reason I declare Parent as a TWinControl. This is also the type of the TControl.Parent property.
Some notes about your code
x.Left := (Form4.ClientWidth - x.Width) div 2;
Here there are two issues (except for x: TObject not having any Width or Left properties):
Form4 is one particular instance of the form class. It is much better to write Self.ClientWidth or simply ClientWidth, so you refer to the current instance of the form class.
But even this is not good enough, because this only works if the form is the parent of x. x might well have a different parent. For instance, x might have a TPanel as its parent (the TPanel's parent being the form).
I'm wondering if it's possible to make something like this:
but without creating a second TForm.
I'm using Delphi 7, but if a newer version make it possible just tell me.
I've always thought the DockEx demo was over-complicated for learning the basics of docking.
The following is the simplest example I know of:
Add a TPanel to a blank form and set its DragKind property to dkDock, DragMode to dmAutomatic and its Align property to alTop.
Drop a TButton on the TPanel
Add the code below to the form:
Run the project and manually drag the panel off the form.
Click Button1.
The above shows how Delphi can undock a Panel (or TEdit, etc) without you
needing to create a second form to host it while undocked, like Remy said in a comment. The Button1 click-handler shows a way (admittedly imperfect) of re-docking the panel. Next:
Undock the panel again, but this time, click the Close button on its auto-created host.
Then, read the OLH and figure out a) how to get the now-hidden panel visible again and b) to re-position & re-align it on the form as it was prior to undocking,
type
TMyClass = TControl;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyClass(Panel1).ManualDock(Self, Nil, alNone);
end;
Parts of my stringgrid are eligible drop targets, and some are not (first row is column headings, first column is a sort of index and subsequent columns may be dropped to). I have that coded and working.
Now I am thinking that it might be nice to gve a visual indiation to the user as he drags the mouse over a cell which is a potential drop target. I woudl like to highlight the first cell in the row and column of the cell over which he is currently hovering (or possibly the entire row and column, forming a sort of crosshair; I am as yet undecided). I reckon I can code that in OnDrawCell.
I had thought to use OnMouseMove and cehck if Dragging then, but ...
My problem is that when I am dragging the OnMouseMove event never gets called.
Is there any other way to know when the cursor is hovering over a strigngrid during a drag operation?
The OnDragOver event is specifically designed for doing this; it's called automatically, and provides the X and Y coordinates where the mouse pointer is located. There's a code sample available at that link location that demonstrates using it as well - it's for a TListBox, but the principle is the same.
procedure TForm1.FormCreate(Sender: TObject);
begin
ListBox1.Items.Add('Not');
ListBox1.Items.Add('In');
ListBox1.Items.Add('Alphabetical');
ListBox1.Items.Add('Order');
end;
// This OnDragOver event handler allows the list box to
// accept a dropped label.
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Source is TLabel;
end;
In Delphi 2010, I need to display a grid that has a horizontal scroll bar with about 15 columns x 5 rows.
I chose to use a StringGrid.
However, while the mouse button is down dragging the horizontal scroll bar, I want the grid to scroll live.
The StringGrid component, it appears, does not scroll live. It waits until the mouse button is released before updating the column and scrolling if necessary.
Also, the horizontal scroll bar button (is that what it's called) is not proportional to the number of columns. And for a down-arrow when on the bottom row to move to the top of the next column to the right...
These seem like common needs, so I was surprised not to find them in TStringGrid.
Any suggestions on a way around these two problems? I can use a DbGrid or other standard component, but my preference is to not use a commercial grid if I can avoid it. And I'm not going to use shareware or freeware...
TIA
For the first question, you can set goThumbTracking in the StringGrid's Options at design-time, or at run-time: StringGrid1.Options := StringGrid1.Options + [goThumbTracking];
For the third question, you can provide the functionality you need by using keyboard event handlers of the control. An example;
procedure TForm1.StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
StringGrid: TStringGrid;
begin
StringGrid := Sender as TStringGrid;
case Key of
VK_DOWN:
if StringGrid.Row = StringGrid.RowCount - 1 then begin
Key := 0;
StringGrid.Row := StringGrid.FixedRows;
if StringGrid.Col = StringGrid.ColCount - 1 then
StringGrid.Col := StringGrid.FixedCols
else
StringGrid.Col := StringGrid.Col + 1;
end;
VK_UP: //...;
VK_RIGHT: //;
VK_LEFT: //;
end;
end;
For the second question, the scrolling code seems to be buried in private methods of TCustomGrid. I have no clue how to achieve that..
If noticed you are not interested in third party components - Freeware, I am not fond of these either, but we all must make sacrifices sometimes if we want to get the problems solved. This is one of these sacrifices! This component is to good to be ignored. You will not create something like it yourself if you don't have a couple of years of free time.
Either write a new component based on TStringGrid (I would not - it is not the best tool in the box to begin with)
But take some time and learn TVirtualStringTree. The component is years ahead of TStrignGrid. The source is available and there are many who uses it.
And there are events already implemented to react on scrollbar changes
OnScroll, OnShowScrollbar
http://www.delphi-gems.com/index.php?option=com_content&task=view&id=12&Itemid=38
Search on stackoverflow and you can read much more about tvirtualstringtree
Second the suggestion to use TVirtualStringTree. Working with the TStringGrid component is like stabbing yourself in the belly with a rusty scissor.
I use Beyond Compare (a great program), and was very impressed when it displayed a "New Version Available" label on its Menu Bar. I clicked on it, up popped an install new version box, it installed, the program restarted itself and there was the new version and no more label.
I thought that was a great feature. The label is there prominently on the menu bar where the user can't miss it. I've already got the update procedure, so all I had to do was add the label. That should be easy.
Here's the label where I want it:
(source: beholdgenealogy.com)
... Wrong. I couldn't figure out how to add a label there. The menu bar and the control area above it appear to be hands-off area for visual components. I couldn't place one there.
But I know it can be done, because Beyond Compare is a Delphi program.
Can anyone tell me what I have to do to put a TLabel in my Menu Bar or at least make it appear to be over the Menu Bar in the correct position?
For reference, I use Delphi 2009.
Conclusion: Christopher seems to have correctly figured out what the Beyond Compare people did. I've decided to implement the menu item, but without the customization of his "owner draw" solution. So I don't get the blue bold underline hyperlink look, but I also don't lose all the automatic things (like the Vista styling) that owner draw skips.
To space the menu item over to the right, I've added an item after the "Help" that has the caption " " and is disabled.
Thanks, Christopher. I was stuck thinking it must be a Label, but you saw around that.
Are you sure it's a label?
I haven't used the program, but it could just be a menu item, set to 'owner draw' and painted to look like a link?
http://sirmonkeys.com/images/updatelink.png
(done in Delphi 7)
procedure TForm1.MYITem1DrawItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
begin
acanvas.Font.Style := [fsUnderline,fsbold];
acanvas.Font.color := clblue;
acanvas.Brush.Style := bsClear;
acanvas.TextOut(arect.left+1,arect.top+1,'Link to Update...');
end;
procedure TForm1.MYITem1MeasureItem(Sender: TObject; ACanvas: TCanvas;
var Width, Height: Integer);
begin
width := 100;
end;
and then either a have an ImageList assigned to MainMenu1.Images
or set MainMenu1.OwnerDraw to true.
Beyond Compare's implementation is actually a TLabel. We use Toolbar 2000 for our menus and toolbars, so embedding a control on the menu directly is supported (with a correct background), and it has the advantage that it supports right-justified menu items.