Refresh Query / cxGrid - delphi

Using Delphi XE2.
I am writing a software package which uses cxGrids and are linked to Querys/Datasources.
At a click of a button how do you refresh a Query to make sure the records are up to date in the cxGrids.
Also if a record is highlighted on a cxGrid it must remember that record and doesn't reset back to the top of the grid.

Close and open the dataset behind the cxgrid to make sure you have the latest data.
dataset.close;
dataset.open;
If you need to remember the current record and put the cursor back on it - use a bookmark as shown in the link below.
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/DB_TDataSet_GetBookmark.html
If you prefer not to use a bookmark, you can utilise the dataset.locate method.
Store the primary key of the record, and after the refresh, use dataset.locate(dataset.fieldbyname('PK').AsDataType) to take you back to that record.
Using the locate method is probably a more readable/elegant way of working.

for cxgrid of devexpress the bookmaker is a bad solution for restore the selection , you can use the cxStatusKeeper ( it public unit that you can download from support center of devexpress )
{Init the component for restore selection}
FGridStatus := TcxGridDBTableKeeper.Create(self);
FGridStatus.LoadExpanding := False;
FGridStatus.LoadSelection := True;
FGridStatus.LoadFocus := True;
FGridStatus.LoadTopRecord := False;
FGridStatus.LoadWithDetails := False;
FGridStatus.LoadFocusedView := True;
FGridStatus.LoadFocusedItem := True;
FGridStatus.View := gvTableElementi;
{save the current items}
FGridStatus.Store;
{restore the selection}
if FGridStatus.GridStored then
FGridStatus.Restore;

cxGridTableView.Navigator has a Refresh button that will do what you want.
If you want to Refresh using your own button, you can call cxGridTableView.DataController.RefreshExternalData

Related

Listbox does not show the changes made to the Dataset

I have a FMX ComboBox connected through LiveBindings to a ClientDataset. Everything works fine, until I needed to filter the data from the ClientDataset. After applying the filter, ComboBox.Items and ComboBox.ListItems are ok, i.e., it is the data contained in the filtered ClientDataset. But the data that the ComboBox listbox shows is always the same: the data that the ClientDataset contained the first time the listbox was displayed.
procedure TForm14.Button1Click(Sender: TObject);
begin
LinkFillControlToField1.Active := False;
ClientDataset1.Filter := 'TYPE = ''N''';
Clientdataset1.Filtered := not ClientDataset1.Filtered;
// Deactivate and Reactivate the link is necessary to refresh the data
LinkFillControlToField1.Active := True;
end;
In that code I change the filter to the ClientDataset and I deactivate the LinkFillControlToField and after applying the filter I activate it again to refresh the data. It works without problems with TListBox and other controls, but not with a TComboBox, although ComboBox.Items and ComboBox.ListItems do contain the correct data, but it is not the one shown in the combobox listbox.
Is there a way to solve this, using LiveBindings ? I have looked for properties and methods of the ComboBox (Repaint for example), the LinkFillControlToField and the BindSourceDB, but nothing has worked for me.
I use Delphi 10.4 Update 2, Firemonkey Windows application (32 or 64) running on Windows 10.
TIA.,
Ricardo
I had the same problem. My data source was TFireDAC TQuery. I submitted a change to a parma contained in the query.
My solution to my problem was to insert the line to explicitly clear the list of items.
if (SCM != NULL && SCM->qrySession->Active) {
SCM->qrySession->DisableControls();
// remove all the strings held in the combobox
// note cmbSessionList->Clear() doesn't work here.
cmbSessionList->Items->Clear();
SCM->qrySession->Close();
// ASSIGN PARAM to display or hide CLOSED sessions
SCM->qrySession->ParamByName("HIDECLOSED")->AsBoolean = fHideClosedSessions;
SCM->qrySession->Prepare();
SCM->qrySession->Open();
SCM->qrySession->EnableControls();
}

TStringGrid show (bcd) in delphi live binding

I connect TFDQuery with TStringGrid in live binding in delphi firemonkey apps.
I tried to use filter in TFDQuery based on Editbox for searching purpose, and it's work just fine.
but, whenever I clear the Editbox, one of my row in TStringGrid would show "(bcd)" as it's value like the pict below.
what am I doing wrong ? how can I fix it ?
Edit :
Im using mySql database with firedac tfdconnection + tfdquery
the datatype of the column is AnsiString & FmtBCS(32,0)
Im using live binding feature in delphi.
my filter code
with MainQuery do begin
Filtered := False;
OnFilterRecord := nil;
Filter := FilterValue;
Filtered := True;
end;
I Insert to the table with TFDConnection.execSQL
the "(BCD)" part always change on the selected Row as the pict below.
EDIT 2:
To Reproduce my error, you can :
add TStringGrid.
Add Editbox.
add tfdconnection
add tfdquery
use live binding from tfdquery to tstringgrid.
add query to tfdquery.sql.text that using SUM() in mysql. Example : "select id, sum(stock) as total_stock from stocks"
activate that tfdquery
add onkeyup event on editbox.
add this code :
FilterValue:= 'garmines_id LIKE ''/' +Edit1.Text+'%'' ESCAPE ''/'' ';
with FDQuery1 do begin
Filtered:= false;
OnFilterRecord := nil;
Filter := FilterValue;
Filtered := True;
end;
run
try to type something on editbox to make sure filter works fine.
clear editbox, then the "(BCD)" is show on the selected row.
I reproduce this error. this is the SS :
Well, I still don't know what exactly causing this problem but I found the work around solution that avoid this problem appears.
you need to set TStringGrid.selected value to -1 before refreshing the TFDQuery. so the code become :
FilterValue:= 'garmines_id LIKE ''/' +Edit1.Text+'%'' ESCAPE ''/'' ';
StringGrid1.selected := -1;
with FDQuery1 do begin
Filtered:= false;
OnFilterRecord := nil;
Filter := FilterValue;
Filtered := True;
end;
I suspect that the cause of this problem is data type that come from mysql sum() method namely FmtBCD(32)
Go to DataMapping Rules (firedac connection)
Mark ignore inherited rules
create 2 new rules
rule1: source: dtBCD / target datatype: dtDouble / all min: 0 / all max: 100
rule2: source: dtfmtbcd / target datatype: dtDouble / all min: 0 / all max: 100
click ok. now the fields will be dtDouble, and are compatible with tgrid

How to avoid of hourglass mouse cursor when executing SQL query with TFDQuery in Delphi XE5

I have grid control on form and when current record change i need to load RTF stored in DB. It works fine in general, but when i switch records i can see changes of mouse cursor to hourglass and back to regular:
function TComments.GetDocument(AID: integer; ADst: TStream):Boolean;
begin
try
SelectQuery.Close;
SelectQuery.Params.Clear;
SelectQuery.SQL.Text :=
'SELECT Dokument from Kommentarer ' +
'WHERE ID = :ID';
SelectQuery.ParamByName('ID').AsInteger := AID;
SelectQuery.Open;
Result := SelectQuery.RecordCount > 0;
if Result then
(SelectQuery.Fields[0] as TBLOBField).SaveToStream(ADst);
finally
SelectQuery.Close;
end;
end;
If i comment "SelectQuery.Open;" then cursor doesn't switch. I supposed there should be option in TFDQuery (or connection), but i can't find anything. Any help?
UPDATE. As TLama suggested, i placed WaitCursor:TFDGUIxWaitCursor at my form (one place for app) and use it this way:
StoredCursor := WaitCursor.ScreenCursor;
WaitCursor.ScreenCursor := gcrNone;
try
// access DB with queries
finally
WaitCursor.ScreenCursor := StoredCursor;
end;
UPDATE2: Two more ways to do it.
Set TFDQuery.ResourceOptions.SilentMode=True (simplest way to disable hourglass cursor for specific queries, property name is bad, but according to doc it doesn't block any dialogs, only changes of cursor).
Set global variable FADGUIxSilentMode=True from unit FireDAC.UI.Intf (not best, but probably simplest way to disable changes of cursor globally for FireDAC).
This is covered in this FAQ set:
How can I turn off the SQL hourglass completely ?
a) To disable the wait cursor completely for an application, use
TFDGUIxWaitCursor with Provider = 'Console'. The 'Console'
provider contains an empty wait cursor implementation and the wait
cursor will no longer be shown by FireDAC. If a mouse cursor is still
changing, then check that only FireDAC.ConsoleUI.Wait unit is included
into your application and FireDAC.VCLUI.Wait and FireDAC.FMXUI.Wait
are not included. Note that you will not have the ability to turn the
wait cursor on again.
b) To disable the wait cursor, but to have the ability to enable it
again later, use code like the following:
FDWaitCursor1.ScreenCursor := gcrNone;
or
FDManager.ResourceOptions.SilentMode := True;
c) To disable the wait cursor and FireDAC dialogs, but to have the
ability to enable them again later, set the FDManager.SilentMode
property to True. This will disable all wait cursors and FireDAC
dialogs, including:
The error dialog
Async execution dialog
Login dialog
Script progress dialog
Setting ResourceOptions.SilentMode to True disables only wait
cursors.
Since you want to turn off that cursor only for that query execution, use the option b and wrap your code with something like this:
var
OldCursor: TFDGUIxScreenCursor;
begin
OldCursor := FDWaitCursor1.ScreenCursor;
FDWaitCursor1.ScreenCursor := gcrNone;
try
// your code goes here
finally
FDWaitCursor1.ScreenCursor := OldCursor;
end;
end;

Update existing Google calendar entry using TMS Cloud component

I use the following code to add a new entry to my Google calendar using TMS Cloud component
I have the new entries in a table in a database and they are added OK
But I would like to be able to update an entry also, but that I cant find any help about.
The demo that is supplied with the controls has the option, but I don't want to load all entries from the calendar and select the one to edit there. I want to save some sort of entryID that it is given and use that to update.
DevExpress has an option that lets you sync a table to and from Outlook calendar (works very nice) so I think it should be possible to do with TMS
The question is: how do I get an ID of a calendar entry when creating it so I can store it with the record in table and use it afterwards for updating?
var
ci: TGCalendarItem;
begin
AdvGCalendar.App.Key := Settings.Google.Key;
AdvGCalendar.App.Secret := Settings.Google.Secret;
AdvGCalendar.Logging := true;
if not AdvGCalendar.TestTokens then
AdvGCalendar.RefreshAccess;
if not AdvGCalendar.TestTokens then
AdvGCalendar.DoAuth
else
Connected := True;
ci := AdvGCalendar.Items.Add;
AdvGCalendar.GetCalendars();
ci.CalendarID := Settings.Google.Calendar;
if ci.CalendarID <> '' then
begin
ci.Location := CiLocation;
ci.Description := CiDescription;
ci.Summary := CiSummary;
ci.StartTime := EncodeDateTime(YearOf(StartDate), MonthOf(StartDate), DayOf(StartDate), HourOf(StartTime), MinuteOf(StartTime), 0, 0);
ci.EndTime := EncodeDateTime(YearOf(StopDate), MonthOf(StopDate), DayOf(StopDate), HourOf(StopTime), MinuteOf(StopTime), 0, 0);
ci.IsAllDay := False;
ci.Visibility := viPrivate;
AdvGCalendar.Add(ci);
end;
end;
The problem was related to the fact that there was an error in the version I was using of the component so that the ID was not returned OK.
Upgrading to a newer version fixed this so now it is working.

Delphi. How to Disable/Enable controls without triggering controls events

I have a DataSet (TZQuery), which has several boolean fields, that have TDBCheckBoxes assigned to them.
These CheckBoxes have "OnClick" events assigned to them and they are triggered whenever I change field values (which are assigned to checkboxes).
The problem is that I do not need these events triggerred, during many operations i do with the dataset.
I've tried calling DataSet.DisableControls, but then events are called right after i call DataSet.EnableControls.
So my question is - is there a way to disable triggering Data-aware controls events.
Edit (bigger picture):
If an exception happens while let's say saving data, i have to load the default values (or the values i've had before saving it). Now while loading that data, all these events (TDBCheckBoxes and other data-aware controls) are triggered, which do all sorts of operations which create lag and sometimes even unwanted changes of data, i'm looking for an universal solution of disabling them all for a short period of time.
Building on Guillem's post:
Turn off everything:
Traverse each component on the form with the for-loop, shown below, changing the properties to the desired value.
If you want to later revert back to the original property values, then you must save the original value (as OldEvent is used below.)
Edit: The code below shows the key concept being discussed. If components are being added or deleted at run-time, or if you'd like to use the absolutely least amount of memory, then use a dynamic array, and as Pieter suggests, store pointers to the components rather than indexing to them.
const
MAX_COMPONENTS_ON_PAGE = 100; // arbitrarily larger than what you'd expect. (Use a dynamic array if this worries you.
var
OldEvent: Array[0.. MAX_COMPONENTS_ON_PAGE - 1] of TNotifyEvent; // save original values here
i: Integer;
begin
for i := 0 to ComponentCount - 1 do
begin
if (Components[i] is TCheckBox) then
begin
OldEvent[i] := TCheckBox(Components[i]).OnClick; // remember old state
TCheckBox(Components[i]).OnClick := nil;
end
else if (Components[i] is TEdit) then
begin
OldEvent[i] := TEdit(Components[i]).OnClick; // remember old state
TEdit(Components[i]).OnClick := nil;
end;
end;
Revert to former values
for i := 0 to ComponentCount - 1 do
begin
if (Components[i] is TCheckBox) then
TCheckBox(Components[i]).OnClick := OldEvent[i]
else if (Components[i] is TEdit) then
TEdit(Components[i]).OnClick := OldEvent[i];
end;
There may be a way to fold all of the if-statements into one generic test that answers "Does this component have an OnClickEvent" -- but I don't know what it is.
Hopefully someone will constructively criticize my answer (rather than just down voting it.) But, hopefully what I've shown above will be workable.
One way to do this is following:
var
Event : TNotifyEvent;
begin
Event := myCheckbox.OnClick;
try
myCheckbox.OnClick := nil;
//your code here
finally
myCheckbox.OnClick := Event;
end;
end;
HTH
The internal design of the TCustomCheckBox is that it triggers the Click method every time the Checked property if changed. Be it by actually clicking it or setting it in code. And this is happening here when you call EnableControls because the control gets updated to display the value of the linked field in your dataset.
TButtonControl (which is what TCustomCheckBox inherits from) has the property ClicksDisabled. Use this instead of (or in addition to) the DisableControls/EnableControls call. Unfortunately it is protected and not made public by TCustomCheckBox but you can use a small hack to access it:
type
TButtonControlAccess = class(TButtonControl)
public
property ClicksDisabled;
end;
...
TButtonControlAccess(MyCheckBox1).ClicksDisabled := True;
// do some dataset stuff
TButtonControlAccess(MyCheckBox1).ClicksDisabled := False;
Of course you can put this into a method that checks all components and sets this property if the control inherits from TCustomCheckBox or some other criteria.

Resources