Rearange TBindNavigator Buttons - delphi

I am using FireDac controls to connect with my db.
To navigate through the result sets i then use TBindNavigator.
This is a very handy component. However it lacks support for rearanging the buttons.
Here is an image of the control:
What i try to achieve is:
I downloaded the trial Version of TMS components and even their control (https://www.tmssoftware.com/site/dban.asp) isn't able to rearanche the buttons.
I am wondering if this is possible?

In order to achieve this, you need to create class inherited from dbnavigator, like e.g. :
type TNewDBNavigator = class(TDBNavigator);
It's needed because Buttons in TDBNavigator are protected.
Buttons are declared as:
Buttons: array[TNavigateBtn] of TNavButton;
And their order:
TNavigateBtn =
(nbFirst, nbPrior, nbNext, nbLast, nbInsert,
nbDelete, nbEdit, nbPost, nbCancel, nbRefresh);
If You had trouble with "hacking" VCL components, there is
nice article at delphi.about.com:
At this link
The other approach would be creating a set of custom buttons and modify their behaviur the way You want them to.
Best regards

Related

How to refence a DBGrid from another form

I have a mainForm with a DBGrid and I have a second form with a CheckListBox that shows all of the DBGrid columns for the user to choose. I need to reference in Form2 the DBGrid that I have in MainForm.
I would like this second form to handle all of the procedures connected to the dbdgrid columns , so that I can reuse it easily.
That was the idea, but I dod'nt find the way to pass the DBGrid reference.
Is it possible ?
Answering the question you asked, on your Form2, define a property
TForm2
[...]
private
FGrid : TDBGrid
public
property Grid : TDBGrid read FGrid write FGrid;
Then, after you've created an instance of TForm2, just do
Form2.Grid := MainForm.DBGrid1;
Then, on Form2, you can do anything valid you like to change Grid and the changes will be made to MainForm.DBGrid1.
Is it possible?
The question should rather be Is there a better way to achieve what I want?
Would it be maintainable if Form2 worked basically with a control from a different form? What if other forms would also need to hold references to components on other forms?
How hard would it be in a year to find a bug if controls are used over different forms?
Would such a solution match to the SOLID principles?
Answering these questions should help you to look for a different approach.
You should consider to separate UI and business logic. A TDBGrid seems to be a convenient way to get data from a database into your application but it violates the Single Responsibility Principle since it loads and displays data at the same time. Don't use it as a basic data provider inside your application. Perform the SQL queries from a deeper UI independant layer of your software. Store the results in containers and display them in all the ways you want in your different forms.

Delphi Object Pascal corresponding run-time calls of TBindNavigator

My question is about Delphi Object Pascal corresponding run-time calls of TBindNavigator component buttons.
I'm interesting of the following buttons:
"Post";
"Cancel";
"Refresh".
How can I implement it in the code without TBindNavigator?
Would you please clarify this issue?
Thanks.
If you wish to implement the necessary actions with simultaneous check if they are enabled etc. then you do not need TBindNavigator at all. The easiest way is the following:
The sample for an FMX Form:
Add a TActionList to your form.
Add a New Standard Action form LiveBindings node of necessary TFMXBindNavigatePost, TFMXBindNavigateCancel etc. class.
Select this action and set Datasource property.
Assign the action to the necessary TButton or other control.
Steps 2-4 can be done in the other way as well.
Selet the control (i.e. TButton), select property Action and click the arrow on the right.
Select New standard action then LiveBindings. Select the necessary class TFMXBindNavigate*. The action will be assigned to the TButton.
Select TActionList and choose the added action and set DataSource property.
The action will be kept up-to-date by LiveBindings. The necessary controls will become Enabled-Disabled and will act as buttons of TBindNavigator.

dbctrlgrid delete and populate entries at runtime

I want to load the content of of dbctrlgrid at runtime (from database). So I encountered several challenges:
How to detect if the dbctrlgrid is empty and/or how to clear it.
How to put Tlabel and Tdbtext on the panel. The main problem seems to be to find the right parent. dbctrlgrid doesn't work. There is an object called Tdbctrlpanel which should work, but I don't know how to access it. I could not find it in properties or methods of Tdbctrlgrid.
Any code snipplet is welcome
To answer 1)
You don't query the TDBCtrlGrid, you query the underlying dataset; if it .IsEmpty the grid is empty.
When people start using data aware (grid) components they have the tendency to see that as the 'data container' that you can query and modify, but that is not the case. See it as a view on your underlying data with some built-in editors that modify that data. Then the 'same rules' apply to you as to these editors: update the underlying dataset.
To add controls to a TDBCtrlGrid you have to set the controls parent to the Panel property of the TDBCtrlGrid. The problem is that this property is protected. There are several ways to overcome this limitation. One is shown at Delphi About: Accessing protected members of a component
This is a common technique known to Delphi programmers as the 'protected hack'.

Delphi TComboBox Dropdown fields filtering [duplicate]

Everyone probably knows what I mean, but to clarify the control would need to:
Fire an event when user edits the text. The event would provide a SuggestionList: TStrings which you could fill with matches/suggestions.
if the SuggestionList is not empty a drop down should appear.
Unlike combo, the control should not attempt to automatically select/auto complete or otherwise affect the editing.
So, is there a Delphi edit/combo control that works like that ?
Use the autocompletion feature built in to all Windows edit controls.
First, fill your TStrings object however you want. Then use GetOleStrings to create a TStringsAdapter to wrap it. (The adapter does not claim ownership of the TStrings object, so you must make sure you don't destroy it while the adapter is still live.) The adapter gives you an IStrings interface, which you'll need because the autocompletion feature requires an IEnumString interface to provide the completion matches. Call _NewEnum for that.
Next, call CoCreateInstance to create an IAutoComplete object. Call its Init method to associate it with the window handle of your edit control. If you're using a combo box, then send it a cbem_GetEditControl message to find the underlying edit window.
You can stop at that point and autocompletion should work automatically. You can disable autocompletion if you want, or you can set any number of autocompletion options.
You say you don't want autocompletion, but in the OS terminology, I think what you really don't want is called auto append, where the remainder of the string is entered into the edit box automatically as the user types, but selected so that further typing will overwrite it, and the user needs to delete the excess text if the desired value is shorter than one of the matches.
There is also auto suggest, which displays a drop-down list of suggestions.
You can enable either or both options. You don't need to filter the list of suggestions yourself; the autocomplete object filters the IEnumString list by itself.
You can use standard TComboBox and faststrings library (for stringMatches() function).
procedure TForm1.cbChange(Sender: TObject);
var
s:Integer;
tmpstr:string;
begin
//suggestions: tstringlist
cb.AutoComplete:=false;
tmpstr:=cb.Text;
cb.Items.Clear;
for s:=0 to suggestions.Count - 1 do
if StringMatches(suggestions[s],cb.Text+'*') then
cb.Items.Add(suggestions[s]);
cb.DroppedDown:=(cb.Items.Count<>0) and (Length(cb.Text)<>0);
cb.Text:=tmpstr;
cb.SelStart:=Length(cb.Text)
end;
If you just want to show a file or url list:
SHAutoComplete(GetWindow(eb_MyComboBox->Handle, GW_CHILD), SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_DIRS);
I first implemented this feature like Rob described it in his answer. Later I saw that TComboBoxEx has the property AutoCompleteOptions where I set acoAutoSuggest to True and acoAutoAppend to False. The ComboBox now filters its item list when doing some entry and shows the matching items.
I'm using RAD Studio 10 Seattle and XE2 but don't know if this feature is available in older versions.
To the last bit of your question: "So, is there a Delphi edit/combo control that works like that ?":
A bit late to the party but yes, I have written a free and open source component that implements the Google Place Autocomplete and Google Place Details API's:
It does inherit from the standard TComboBox but you can modify the code to work with any TEdit
https://carbonsoft.co.za/components/
or
https://github.com/RynoCoetzee/TRCGPlaceAutoCompleteCombo

Cloning a TStringGrid Component

I am starting to learn Delphi. So I decided to write an application like MS Excel from scratch. In a new Form1, I did put a TPageControl component containing only 1 page. In that page, I did put a TAdvStringGrid and a TPanel with some buttons (button1, button2) and a Popup1 menu for defining some actions on the grid, like copy cell, copy row, delete row, etc. For that StringGrid, also, I have defined some properties, like colors, fonts, etc. I added a toolbar with a button to the main form, in order to add more pages to the PageControl. The OnClick method of that button defines two actions:
1) to add a new Page2 to the PageControl, and
2) to add a new StringGrid In the new created Page2 .
That new (runtime defined) StringGrid created in a new Page of a Tpagecontrol should inherite (get, copy, clone, duplicate) the properties and methods of the StringGrid parent already defined in the first page at design time, and should be able to call the PopUp1 menu just like the StringGrid parent. How we do this?
At the beginning, I thought I just should copy the StringGrid properties using assing(), but when using this approach, the popup menu does not pop up when right mouse clicking on the new StringGrid... and the buttons (button1 and button2) of Form1 only work with the first StringGrid but not with the new added one. I did read somewhere that in order to solve this problem, I could duplicate the StringGrid component by using the write and read TMemoryStream (save the parent stringgrid into a memorystream, create a new stringgrid and then read that memorystream into the new created stringgrid), so I did, but when the program executes this component cloning method, I get a runtime error. :-(
I did carefully chech the Help. Nothing found on that topic. Seems there is not any Delphi component library or third party components that cope with this kind of task. Can anybody help, please? :o)
I would use an tabcontrol instead of an pagecontrol. That way, you would end up with multiple tabs but only one page and grid. I would then make some kind of data structure to keep all my cell information in, and render this structure to the grid. This way, I can have multiple structures, and let the active tab decide which structure to render. You will also end up with a looser coupling between your gui and your logic, making it easier change things later. E.g. if you need to bring in some values form a different spreadsheet into a cell in the current spreadsheet, you can load up a structure and pick out the wanted values. No need to make any gui for the second spreadsheet at all.
For a 3.party component, I will recommend TMS FlexCell and TAdvSpreadGrid. That will give you most of what you need.
A tricky choice for a learner :) however you do not need to start streaming things.
Look up the assign() procedure for TPersistent this is the routine you need to copy parts of the grid easily. For example
for i := 0 to StringGrid1.RowCount - 1 do
StringGrid2.Rows[i].Assign(StringGrid1.Rows[i]);
for an easy start differentiate your grids with the Tag property(StringGrid1.Tag := 1, StringGrid2.Tag := 2 Etc.
The popup menu is pretty simple too:
StringGrid2.popupmenu := stringGrid1.popupMenu But then then you must decide in the Popup Routine Which Grid is "Active" some thing l like
Tform1.popupMenuItem1Click(Sender: TObject)
if Sender is TStringGrid then
Case TStrigngGrid(Sender).Tag of
1: // Grid 1
2: // Grid 2
You can use the same simple logic with the buttons.
As neftali mentioned the best thing would be to put the created grids in an ObjectList.
You would then end up with the slightly more complex but expandabe:
Tform1.popupMenuItem1Click(Sender: TObject)
var AGrid: TStringGrid;
if Sender is TStringGrid then
AGrid := MyListOfStringGrids[MyListOfStringGrids.IndexOf(Sender)];
DoMenuItem1Stuff(AGrid);
Have fun
"...and the buttons (button1 and button2) of Form1 only work with the first StringGrid but not with the new added one. I did read somewhere that in order to solve this problem..."
There is no generic method for solving this. Delphi offers different tools to solve it.
You can create a list of Objects (TObjectList) that containts all the StringGrid; At Button1 Click event you must search what is the grid that you are using at this moment. For example (BIS for the other buttons):
var
index:integer;
sg:TStringGrid;
begin
...
// search the active page
index := pageControl.ActivePageIndex; //0, 1, 2,...
// USe this for search the StringGrid
sg := TStringGrid(OList.Objects[index]);
// the code that you have at the event bus woking with sg
// not stringgrid1, stringgrid2,...
...
sg.Color :=
...
If you don't want use ObjectList, there are alternatives. You can use Tag property for all StringGrids. Assign 0, 1, 2, 3,...
Extract the index (active page) and search the TStringGrid that have the property Tag with the same value. You can do this with FindComponent. The first methos is better. ;-)
Regards.
P.D: Excuse for my bad english.
Dear all, I am trying to learn Delphi
The Delphi style is to find/create/buy a component that does the job and use them in the design-time. You could try making a custom component based on a grid or use TFrame. See links from Custom Component Development and help files that comes with Delphi.
If you really need to clone the control dynamically, here's an example I found that uses stream.ReadComponent.

Resources