Mouse over stringgrid when dragging? - delphi

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;

Related

How to ignore the mouse wheel scrolling for TComboBox controls? [duplicate]

This question already has answers here:
How to suppress mouse wheel in TcxComboBox
(1 answer)
Suppress mouse scroll events on specific controls but NOT their parent?
(1 answer)
Closed last year.
I've found numerous things in this regard, but they're either for third-party controls, different specific situations, or for a completely different language. What I need should be fairly simple.
I have a TDBGrid with a TComboBox placed over a specific cell. As the user scrolls through this grid, the combo box moves along to the corresponding cell. User is further able to change the value of this combo box to update the database.
However, when using the mouse wheel to scroll, if the mouse pointer just happens to be over this combo box, it ends up changing the value of the combo box, rather than scrolling the grid.
How do I suppress the scrolling in the combo box?
I am answering this QA style, because I found such a simple solution on my own which I couldn't find on various forums.
The simplest method is to add an OnKeyDown event handler to the TComboBox, and add the following:
procedure TfrmMain.cboStatusKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key in [VK_UP, VK_DOWN] then
Key:= 0;
end;
Keep in mind that this also blocks keyboard up/down events. If you also wish to block keyboard left/right events, then you can also do this:
procedure TfrmMain.cboStatusKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT] then
Key:= 0;
end;

Drag/Drop to arrange vertically within ScrollBox

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.

Live update of StringGrid when horizontal scroll bar is moving?

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.

How can I select multiple individual cells of a string grid?

I am looking for a string grid that allows me select multiple cells anywhere in the grid without them adjoining each other, e.g pressing CTRL and clicking on various cells over the grid. Or if anyone knows how to do this with the standard Delphi TStringGrid.
Any pointer would be gratefully received.
Although there are a lot of better capable people here, since you haven't gotten any answers, I thought I'd give it a try.
I'm not aware of a way to have the component do this for you. However, when you Control-click a cell, the event OnSelectedCell is called. (I just tested that.) You could put code in an event handler that adds the cell's row and column to a list that you keep of the rows and columns that are selected. Then, in the OnDrawCell event, highlight the cell:
procedure TForm1.StringGrid1DrawCell( Sender: TObject;
ACol: Integer;
ARow: Integer;
Rect: TRect;
State: TGridDrawState);
begin
if CellSelected( ARow, ACol) then // you write CellSelected() to refer to the list you're keeping
begin
StringGrid1.Canvas.Brush.Color := clYellow;
StringGrid1.Canvas.FillRect(Rect);
StringGrid1.Canvas.TextOut(Rect.Left,Rect.Top,StringGrid1.Cells[ACol,ARow]);
end;
end;

How do I use DoMouseWheel to scroll a line at a time?

I've written a grid control and would like to add support for the mouse wheel to it. I thought it would be as simple as overriding the DoMouseWheel virtual method, but there is a bit of a problem with it.
You can set the number of lines to scroll at a time in Control Panel and the default there is three. And this makes perfect sense when scrolling through a document or web page, but on a grid, I think the expectation is rather to scroll a line at a time. But it seems that Delphi's wheel support will call DoMouseWheel three times for every notch that I scroll, meaning that I can only scroll to each third line in the grid (or whatever that global setting is).
How do I go about scrolling a single line at a time for every turn of the mouse wheel?
Update: The short answer here is to simply set Result to True after scrolling - then it doesn't scroll three times, but only once.
Just copy the code from the TCustomGrid class, which overrides both DoMouseWheelDown() and DoMouseWheelUp() to scroll exactly one line at a time.
In general, it is not a very good idea to fight against the system defaults and/or the user preferences. In this case means that you should respect whatever the system or the user has decided to set in the scrolling time.
Having said so, if you really believe that the multiscroll effect is totally wrong and misleading for the kind of component you want to drive, you might envision a way to get rid of this. You could try to set some timer and ignore all but one of the mouseWheel events that happen in a given lapse of time (in the range of milliseconds). One thing you should do is to set a configuration option in your program, to let the user to turn off this behaviour.
In my case, I used the JVDBGrid component, but I think this work for DbGrid too. You may overwrite the following functions: OnMouseWheelDown and OnMouseWheelUp.
E.g.:
Type declaration:
type
TMyGrid = class(TJvExDBGrid);
Implementation
procedure TFExample.JvDBGrid1MouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
Handled := TMyGrid(Sender).DataLink.DataSet.MoveBy(1) <> 0;
end;
procedure TFExample.JvDBGrid1MouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
Handled := TMyGrid(Sender).DataLink.DataSet.MoveBy(-1) <> 0;
end;

Resources