Using integer as if subject - delphi

Trying to have some main template to change visibility of groupboxes based on the number in main editbox.
EditDay is the edit box, where only numbers are in it
day:=DayOfTheYear(Now);
EditDay.Text:=day;
so it's basicaly the day of the year.
Anyway, I need a groupbox (with a few memos) for everyday of the year. Since this is a file with records, which another program will read off for everyday different infos, I need that file writer first, so I can even make one. That's what this one is
Since I'm doing a record file, there has to be all boxes firstly filled up before I'll write to a file, so I need to have Groupboxes to be visible one at a time, each one for a day I specify in a main TEdit.
Right now I'm stuck with setting the visibility of the groupboxes; The code below gives me Access violation error.
x is the number specified in TEdit named EditDay. I wanted to make an y every other day but the day in EditDay box so all but x;
x : Integer;
y : Integer;
procedure TWriteForm.DayCheckTimer(Sender: TObject);
begin
x:=StrToInt(EditDay.Text);
y:=Not x;
(FindComponent('GroupBox'+IntToStr(x)) as TGroupBox).Visible := True;
(FindComponent('GroupBox'+IntToStr(y)) as TGroupBox).Visible := False;
Tried to set y:=[1..365] and not x; [1..365] - x; and several others, but none of them worked.
Where am I wrong? .. Any help will be appreciated. :))
[I'm kinda beginner, yes..]

view y:=Not x; in the debugger x=1 will be y=-2, you won't find a Compoentr with this name.
You will have to iterate over your componets
For i := 1 to mCount
and set visibilty by condtition
(FindComponent('GroupBox'+IntToStr(i)) as TGroupBox).Visible := y = i;

Here a small sample project to deal with a lot (365) of records.
unit RecordEdit_ViewU;
interface
uses
SysUtils,
Controls, Forms, Dialogs, StdCtrls, System.Classes;
type
TPerson = record
Firstname : string[50]; // shortstring !!
Lastname : string[50]; // shortstring !!
end;
TRecordEdit_View = class( TForm )
Current_Edit : TEdit;
Data_Firstname_Edit : TEdit;
Data_Lastname_Edit : TEdit;
Data_Prev_Button : TButton;
Data_Next_Button : TButton;
Data_Save_Button : TButton;
procedure FormCreate( Sender : TObject );
procedure Current_EditChange( Sender : TObject );
procedure Data_Prev_ButtonClick( Sender : TObject );
procedure Data_Next_ButtonClick( Sender : TObject );
procedure Data_Save_ButtonClick( Sender : TObject );
private
FData : array [1 .. 365] of TPerson;
FCurrent : Integer;
procedure SetCurrent( const Value : Integer );
procedure InitData;
procedure StoreCurrent;
procedure LoadCurrent;
procedure SaveData;
public
property Current : Integer read FCurrent write SetCurrent;
end;
var
RecordEdit_View : TRecordEdit_View;
implementation
{$R *.dfm}
procedure TRecordEdit_View.Current_EditChange( Sender : TObject );
begin
Current := StrToIntDef( Current_Edit.Text, 0 ); // convert text to integer
end;
procedure TRecordEdit_View.Data_Next_ButtonClick( Sender : TObject );
begin
Current := Current + 1; // next record
end;
procedure TRecordEdit_View.Data_Prev_ButtonClick( Sender : TObject );
begin
Current := Current - 1; // prev record
end;
procedure TRecordEdit_View.Data_Save_ButtonClick( Sender : TObject );
begin
SaveData;
end;
procedure TRecordEdit_View.FormCreate( Sender : TObject );
begin
InitData;
end;
procedure TRecordEdit_View.InitData;
begin
FCurrent := Low( FData ); // first record
LoadCurrent; // load data from record
end;
procedure TRecordEdit_View.LoadCurrent;
begin
// Data from record to controls
Data_Firstname_Edit.Text := FData[Current].Firstname;
Data_Lastname_Edit.Text := FData[Current].Lastname;
// Update the Current-Edit
Current_Edit.Text := IntToStr( Current );
end;
procedure TRecordEdit_View.SaveData;
begin
ShowMessage( 'Needs to be implemented!' );
end;
procedure TRecordEdit_View.SetCurrent( const Value : Integer );
begin
// check, if we have a change and if we can change to the new index
if ( Value <> Current ) and ( Value >= Low( FData ) ) and ( Value <= High( FData ) )
then
begin
StoreCurrent; // store data from controls
FCurrent := Value; // change current index
LoadCurrent; // load data from record
end;
end;
procedure TRecordEdit_View.StoreCurrent;
begin
// Data from controls to record
FData[Current].Firstname := Data_Firstname_Edit.Text;
FData[Current].Lastname := Data_Lastname_Edit.Text;
end;
end.
And the form
object RecordEdit_View: TRecordEdit_View
Left = 0
Top = 0
Caption = 'RecordEdit_View'
ClientHeight = 337
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Current_Edit: TEdit
Left = 107
Top = 16
Width = 75
Height = 21
TabOrder = 0
Text = 'Current_Edit'
OnChange = Current_EditChange
end
object Data_Firstname_Edit: TEdit
Left = 80
Top = 56
Width = 129
Height = 21
MaxLength = 50
TabOrder = 1
Text = 'Data_Firstname_Edit'
end
object Data_Lastname_Edit: TEdit
Left = 80
Top = 83
Width = 129
Height = 21
MaxLength = 50
TabOrder = 2
Text = 'Data_Lastname_Edit'
end
object Data_Prev_Button: TButton
Left = 80
Top = 16
Width = 21
Height = 21
Caption = '<'
TabOrder = 3
OnClick = Data_Prev_ButtonClick
end
object Data_Next_Button: TButton
Left = 188
Top = 16
Width = 21
Height = 21
Caption = '>'
TabOrder = 4
OnClick = Data_Next_ButtonClick
end
object Data_Save_Button: TButton
Left = 80
Top = 118
Width = 129
Height = 25
Caption = 'Save Data'
TabOrder = 5
OnClick = Data_Save_ButtonClick
end
end
You can get the complete Source and Executable here

Related

How to get fmx listbox.ScrollToItem to work on form creation or activation

I have a form with at TListBox that I populate in the onCreate event, where I also set the selected item. I want the List Box to have the selected item in view when the form shows, so I tried firing the ScrollToItem method. This does not work. I also tried putting it in OnShow and OnActivate events, but it still does not work. Is there a way to get this to work?
Here is a sample program that illustrates the problem:
`type
TForm5 = class(TForm)
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form5: TForm5;
implementation
{$R *.fmx}
procedure TForm5.FormCreate(Sender: TObject);
var
i: Integer;
lbi: TListBoxItem;
begin
for i := 1 to 50 do
begin
lbi := TListBoxItem.Create(ListBox1);
lbi.Text := 'item ' + inttostr(i);
ListBox1.AddObject( lbi );
end;
ListBox1.itemindex := ListBox1.items.indexof('item 48');
ListBox1.ScrollToItem(ListBox1.Selected);
end;
end.`
and the FMX file:
`object Form5: TForm5
Left = 0
Top = 0
Caption = 'Form5'
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnCreate = FormCreate
DesignerMasterStyle = 0
object ListBox1: TListBox
Position.X = 224.000000000000000000
Position.Y = 144.000000000000000000
TabOrder = 1
DisableFocusEffect = True
DefaultItemStyles.ItemStyle = ''
DefaultItemStyles.GroupHeaderStyle = ''
DefaultItemStyles.GroupFooterStyle = ''
Viewport.Width = 196.000000000000000000
Viewport.Height = 196.000000000000000000
end
end`
TListBox has a property ViewportPosition: TPointF that sets the scrollbars. Add the following line after you set ListBox1.ItemIndex:
ListBox1.ViewportPosition := PointF(0.0, ListBox1.itemindex * ListBox1.ItemHeight);
The previous assumes that all items have the same height (TListBox1.ItemHeight set in Object Inspector or in code earlier). Your FMX file doesn't reflect this, so you may want to add it, otherwise the scrolling will not take place.
You might want to set individual height for the items. In that case you must traverse all items up to the one you want to be selected and sum their heights to get the Y term for the ViewportPosition.

How to change TDBGrld row colour when a field value is changed

(Minimal RepEx at the end)
I have a DBGrid (actually a TSMDBgrid) populated by a query.
One field, payment_made, is Boolean and displays in the grid as a checkbox either ticked or unticked. depending upon the field value. This is the only field that can be edited. Initially all rows are unticked
I'm using the often published DrawColumnCell() code below to change the whole row colour to blue if the Boolean field on that row gets changed to checked (true) and back to white if it subsequently gets unchecked (false).
I want all rows that are checked to remain blue.
The code I use is
procedure TFrmBulkSubdPaymentRecord.SMDBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect;
DataCol: Integer;
Column: TColumn;
State: TGridDrawState);
//see https://www.thoughtco.com/change-coloring-in-tdbgrid-component-4077252
begin
if MyQuery1.FieldByName('payment_made').AsBoolean = true then
SMDBGrid1.Canvas.Brush.Color := $00ffff99//pale blue
else
SMDBGrid1.Canvas.Brush.Color := clwhite;
SMDBGrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;
When starting with a white grid, if I check the payment_made box in ,say, row 3 the whole row goes blue.
If I check another row, say row 6 then that whole row row goes blue.
If I then uncheck row 3 the whole of that row goes white again
But, if I then check row 3 again, this time just the payment_made cell goes blue, not the whole row
It looks like could be something to do with the OnDrawColumnCell() not being called for every cell.
Am I doing this the right way or have I missed something?
I've looked at this post and several other related posts but it appears I am doing it right (although obviously not)
No other processing is done when the field is clicked as it's all done afterwards in a loop that deals with each checked row in turn.
Incidentally, I use
if MyQuery1.FieldByName('payment_made').AsBoolean = true then
simply to make the code more readable. I realise that the = true is unnecessary and I can live with the extra few microseconds this might add.
Minimal Reproducible example (written in Delphi 2009 and using TSMDBgrid from SM components (although problem seem to exist with the TDGGrid as well)
The .dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 553
ClientWidth = 640
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 344
Top = 56
Width = 61
Height = 13
Caption = '<--- TDBGrid'
end
object Label2: TLabel
Left = 344
Top = 165
Width = 75
Height = 13
Caption = '<--- TSMDBGrid'
end
object DBGrid1: TDBGrid
Left = 8
Top = 16
Width = 321
Height = 97
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
object SMDBGrid1: TSMDBGrid
Left = 8
Top = 137
Width = 321
Height = 104
Options = [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit]
TabOrder = 1
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
Flat = False
BandsFont.Charset = DEFAULT_CHARSET
BandsFont.Color = clWindowText
BandsFont.Height = -11
BandsFont.Name = 'Tahoma'
BandsFont.Style = []
Groupings = <>
GridStyle.Style = gsCustom
GridStyle.OddColor = clWindow
GridStyle.EvenColor = clWindow
TitleHeight.PixelCount = 24
FooterColor = clBtnFace
ExOptions = [eoBooleanAsCheckBox, eoENTERlikeTAB, eoKeepSelection, eoStandardPopup, eoBLOBEditor, eoTitleWordWrap, eoFilterAutoApply]
RegistryKey = 'Software\Scalabium'
RegistrySection = 'SMDBGrid'
WidthOfIndicator = 11
DefaultRowHeight = 24
ScrollBars = ssHorizontal
end
object btnSetTrue: TButton
Left = 480
Top = 111
Width = 75
Height = 25
Caption = 'btnSetTrue'
TabOrder = 2
OnClick = btnSetTrueClick
end
object btnSetFalse: TButton
Left = 480
Top = 160
Width = 75
Height = 25
Caption = 'btnSetFalse'
TabOrder = 3
OnClick = btnSetFalseClick
end
object Memo1: TMemo
Left = 8
Top = 304
Width = 609
Height = 241
Lines.Strings = (
'Top grid is TDBGrid, botton grid is TSMDBGrid'
'Both use DataSource1 as their datasource'
'Both have the same code for their OnDBGrid1DrawColumnCell'
'When the field '#39'payment_made'#39' is TRUE the whole row should be bl' +
'ue, when it is false the whole rowq should be white'
''
'Correct operation'
'=========='
''
'Change the value of the boolean field using the buttons. '
'This operates correctly, the whole row in each grid changes to b' +
'lue or whire correctly. '
'problem evident.'
''
'To reproduce the problem'
'==============='
''
'Change the boolean field from true to false or vice versa using ' +
'either the check box in SMDBGrid or by typing true '
'or false in the DBGrid.'
'Sometimes only one cell on the row changes to the correct colour' +
', leaving other cells on the row the wrong colour.')
TabOrder = 4
end
object ClientDataSet1: TClientDataSet
Aggregates = <>
Params = <>
Left = 376
Top = 240
end
object DataSource1: TDataSource
Left = 448
Top = 240
end
end
The .pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SMDBGrid, Grids, DBGrids, DB, DBClient;
type
TForm1 = class(TForm)
ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
SMDBGrid1: TSMDBGrid;
btnSetTrue: TButton;
btnSetFalse: TButton;
Memo1: TMemo;
Label1: TLabel;
Label2: TLabel;
procedure FormShow(Sender: TObject);
procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure SMDBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure btnSetTrueClick(Sender: TObject);
procedure btnSetFalseClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnSetFalseClick(Sender: TObject);
begin
ClientDataSet1.FieldByName('payment_made').AsBoolean := false;
SMDBGrid1.Refresh ;
DBGrid1.refresh
end;
procedure TForm1.btnSetTrueClick(Sender: TObject);
begin
ClientDataSet1.FieldByName('payment_made').AsBoolean := true;
SMDBGrid1.Refresh ;
DBGrid1.refresh
end;
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if ClientDataSet1.FieldByName('payment_made').AsBoolean = true then
DBGrid1.Canvas.Brush.Color := $00ffff99//pale blue
else
DBGrid1.Canvas.Brush.Color := clwhite;
DBGrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
//define dataset fields
ClientDataSet1.FieldDefs.Add('payment_made', ftBoolean);
ClientDataSet1.FieldDefs.Add('second_column', ftString, 10);
ClientDataSet1.CreateDataSet;
ClientDataSet1.LogChanges := False;
//put in one row of data
ClientDataSet1.Append; // insertfirst row
ClientDataSet1.FieldByName('payment_made').AsBoolean := false;
ClientDataSet1.FieldByName('second_column').AsString := 'row one';
ClientDataSet1.Post;
//leave it in editing mode (although this doesn't seem to make any difference)
ClientDataSet1.Edit;
//set option for SMDBgrid to display booleans as a checkbox
SMDBGrid1.ExOptions := SMDBGrid1.ExOptions + [eoBooleanAsCheckBox] ;
//link components together
DataSource1.DataSet := ClientDataSet1;
SMDBGrid1.DataSource := DataSource1;
DBGrid1.DataSource := DataSource1;
//point the grids to their respective OnDrawColumnCell routines
DBGrid1.OnDrawColumnCell := DBGrid1DrawColumnCell ;
SMDBGrid1.OnDrawColumnCell := SMDBGrid1DrawColumnCell ;
end;
procedure TForm1.SMDBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if ClientDataSet1.FieldByName('payment_made').AsBoolean = true then
SMDBGrid1.Canvas.Brush.Color := $00ffff99//pale blue
else
SMDBGrid1.Canvas.Brush.Color := clwhite;
SMDBGrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;
end.

Tchart change labels [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I need a little help on changing the labels shown on a tchart
I use this code to populate the tchart:
With Series1 do
begin
clear;
Add (v[1], 'abcdef', clRed);
Add (v[2], 'aaaaaaaaaaaaaaaaaaaaaaaa', clBlue);
Add (v[3], 'bbbbbbbbbbbbbb', clGreen);
end;
I get this chart:
image of chart
Please help me on changing the labels above the rectangles from the text shown in the image to some other variables via code; also please tell me what can i do to show long texts on the x axis and how to break it on multiple lines
First, I'll assume that your "v" variable was an array of integer; My example it's named "BarValue". The form has a TChart with a 2D Bar Series added.
When you construct the labels, you'll want to replace the spaces and word-breaks with the "TeeLineSeparator" and then maybe rescan them and insert in a "TeeLineSeparator" into much longer words to force them to line-break. (In my example I just broke the long "aaaaaaaaa..." label into words randomly.)
This example worked in the Delphi 10.2 Tokyo and Delphi 2007.
type
TForm1 = class(TForm)
Chart1: TChart;
Series1: TBarSeries;
procedure FormCreate(Sender: TObject);
procedure Series1GetMarkText(Sender: TChartSeries; ValueIndex: Integer; var MarkText: string);
private
BarMark : array[1..3] of string;
BarValue : array[1..3] of integer;
BarLabel : array[1..3] of string;
BarColor : array[1..3] of TColor;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i : integer;
begin
Chart1.Legend.Visible := false;
// This maybe needed on earlier versions of TChart (e.g. Delphi 2007-)
// to make room for multi-line labels
//Chart1.Axes.Bottom.LabelSize := 32;
Series1.OnGetMarkText := Series1GetMarkText;
BarValue[1] := 100;
BarValue[2] := 200;
BarValue[3] := 300;
BarLabel[1] := 'abcdefg';
BarLabel[2] := 'aaaa'+TeeLineSeparator+'aaaaaaaaa'+TeeLineSeparator+'aaaaaaaaaaa';
BarLabel[3] := 'bbbbbbbbbbbbbb';
BarColor[1] := clRed;
BarColor[2] := clBlue;
BarColor[3] := clGreen;
BarMark[1] := 'ok1';
BarMark[2] := 'ok2';
BarMark[3] := 'ok3';
Series1.Clear;
for i := 1 to 3 do
Series1.Add (BarValue[i], BarLabel[i], BarColor[i]);
end;
procedure TForm1.Series1GetMarkText(Sender: TChartSeries; ValueIndex: Integer; var MarkText: string);
begin
MarkText := BarMark[ValueIndex+1];
end;
The DFM:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 374
ClientWidth = 702
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Chart1: TChart
Left = 204
Top = 40
Width = 400
Height = 250
Title.Text.Strings = (
'TChart')
View3D = False
TabOrder = 0
DefaultCanvas = 'TGDIPlusCanvas'
ColorPaletteIndex = 13
object Series1: TBarSeries
OnGetMarkText = Series1GetMarkText
XValues.Name = 'X'
XValues.Order = loAscending
YValues.Name = 'Bar'
YValues.Order = loNone
end
end
end

How to loop through records on a cxgrid - Delphi xe2

How do you loop through a cxgrids records? i.e. make delphi program go through/check each record in the cxgrid from top to bottom.
I have a cxgrid which is displaying records from a tadquery which is talking to a database table if this helps in anyway.
Sample code to do this for a TcxGridDBTableView in a TcxGrid, and also to iterate an DataSet below. Both of these samples will work regardless of whether the DataSet is filtered or not.
The sample for the TcxGrid assumes you've pulled the grid from the palette, added a TcxDBTableView to it, added the dataset's columns to it and left all grid properties in the Object Inspector at their defaults, except the KeyFieldNames of the TableView, which needs setting to the DataSet's primary key, in my case "FilesID". This is so as to be able to identify the dataset record for a given row in the TableView - you obtain the key value, ID, for the row like so:
ID := cxGrid1DBTableView1.DataController.GetRecordId(TopRowIndex + Row);
The ID value is then used by the call to CDS1.Locate() to retrieve the record.
The TBookmark is used to note the current CDS record before the operation and to return to it afterwards. The calls to DisableControls and EnableControls are to prevent the cxGrid (and any other DB-aware controls connected to the CDS) from being changed while the operation is in progress.
procedure TForm1.IterateVisibleGridRows;
var
BM : TBookmark;
TopRowIndex : Integer;
VisibleCount : Integer;
Row : Integer;
ID : Integer;
Controller : TcxGridTableController;
ViewInfo : TcxGridTableViewInfo;
begin
BM := CDS1.GetBookmark;
try
Controller := cxGrid1DBTableView1.Controller;
ViewInfo := TcxGridTableViewInfo(Controller.ViewInfo);
TopRowIndex := Controller.TopRowIndex;
VisibleCount := ViewInfo.RecordsViewInfo.VisibleCount;
CDS1.DisableControls;
Row := 0;
while Row < VisibleCount do begin
ID := cxGrid1DBTableView1.DataController.GetRecordId(TopRowIndex + Row);
if CDS1.Locate('FilesID', ID, []) then begin
// Do what you want here
end;
Inc(Row);
end;
finally
CDS1.GotoBookmark(BM);
CDS1.FreeBookmark(BM);
CDS1.EnableControls;
end;
end;
Btw, I know this is not what you asked, but if you want to iterate a dataset without doing it using a TcxGrid, it's actually much simpler:
procedure IterateDataSetRows(DataSet : TDataSet);
var
BM : TBookmark;
begin
BM := CDS1.GetBookmark;
try
// delete the following 2 lines and the one in the finally block if you don't want a "Wait" cursor
Screen.Cursor := crSqlWait;
Screen.ActiveForm.Update;
DataSet.DisableControls;
DataSet.First;
while not DataSet.Eof do begin
// Do what you want here
DataSet.Next;
end;
finally
DataSet.GotoBookmark(BM);
DataSet.FreeBookmark(BM);
DataSet.EnableControls;
Screen.Cursor := crDefault;
end;
end;
You didn't say where it was all records in your grid or just the visible ones:
Any how heres is an example for doing both:
This example uses a form with a cxGrid, cxButton and a cxMemo. Plus a dxMemdataset
Here is the DFM code:
object Form20: TForm20
Left = 0
Top = 0
Caption = 'Form20'
ClientHeight = 299
ClientWidth = 462
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
DesignSize = (
462
299)
PixelsPerInch = 96
TextHeight = 13
object cxGrid1: TcxGrid
Left = 0
Top = 0
Width = 299
Height = 299
Align = alLeft
TabOrder = 0
ExplicitHeight = 635
object cxGrid1DBTableView1: TcxGridDBTableView
Navigator.Buttons.CustomButtons = <>
DataController.DataSource = DataSource1
DataController.Summary.DefaultGroupSummaryItems = <>
DataController.Summary.FooterSummaryItems = <>
DataController.Summary.SummaryGroups = <>
object cxGrid1DBTableView1RecId: TcxGridDBColumn
DataBinding.FieldName = 'RecId'
Visible = False
end
object cxGrid1DBTableView1Field1: TcxGridDBColumn
DataBinding.FieldName = 'Field1'
end
object cxGrid1DBTableView1Field2: TcxGridDBColumn
DataBinding.FieldName = 'Field2'
end
end
object cxGrid1Level1: TcxGridLevel
GridView = cxGrid1DBTableView1
end
end
object cxButton1: TcxButton
Left = 305
Top = 8
Width = 154
Height = 25
Caption = 'Do the trick'
TabOrder = 1
OnClick = cxButton1Click
end
object cxMemo1: TcxMemo
Left = 305
Top = 39
Anchors = [akLeft, akTop, akRight, akBottom]
Lines.Strings = (
'cxMemo1')
TabOrder = 2
Height = 260
Width = 154
end
object dxMemData1: TdxMemData
Indexes = <>
SortOptions = []
Left = 160
Top = 144
object dxMemData1Field1: TIntegerField
FieldName = 'Field1'
end
object dxMemData1Field2: TIntegerField
FieldName = 'Field2'
end
end
object DataSource1: TDataSource
DataSet = dxMemData1
Left = 168
Top = 152
end
end
First thing first at form create I generate some random Data:
procedure TForm20.FormCreate(Sender: TObject);
var
i: Integer;
begin
randomize;
dxMemData1.DisableControls;
try
dxMemData1.Open;
for i := 0 to 999 do
dxMemData1.AppendRecord([i, Random(500), Random(500)]);
finally
dxMemData1.EnableControls;
end;
end;
Since my grid is bound to the dataset data will show up on screen.
Here is my form definition:
type
TForm20 = class(TForm)
dxMemData1: TdxMemData;
dxMemData1Field1: TIntegerField;
dxMemData1Field2: TIntegerField;
cxGrid1DBTableView1: TcxGridDBTableView;
cxGrid1Level1: TcxGridLevel;
cxGrid1: TcxGrid;
DataSource1: TDataSource;
cxGrid1DBTableView1RecId: TcxGridDBColumn;
cxGrid1DBTableView1Field1: TcxGridDBColumn;
cxGrid1DBTableView1Field2: TcxGridDBColumn;
cxButton1: TcxButton;
cxMemo1: TcxMemo;
procedure FormCreate(Sender: TObject);
procedure cxButton1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Then you just have to push the button:
procedure TForm20.cxButton1Click(Sender: TObject);
var
RecNo, i: Integer;
cxCustomGridRecordViewInfo: TcxCustomGridRecordViewInfo;
s: string;
begin
s := Format(
'You have' + sLineBreak +
' %d records in your Dataset' + sLineBreak +
' %d records in your grid' + sLineBreak +
' %d VISIBLE records in your grid'
, [
cxGrid1DBTableView1.DataController.RecordCount,
cxGrid1DBTableView1.DataController.FilteredRecordCount,
cxGrid1DBTableView1.ViewInfo.VisibleRecordCount
]
);
MessageDlg(s, mtInformation, [mbOK], 0);
cxMemo1.Lines.BeginUpdate;
cxMemo1.Lines.Clear;
cxMemo1.Lines.Add(' *** Filtered Records ***');
for i := 0 to cxGrid1DBTableView1.DataController.FilteredRecordCount - 1 do
begin
RecNo := cxGrid1DBTableView1.DataController.FilteredRecordIndex[i];
cxMemo1.Lines.Add(cxGrid1DBTableView1.DataController.Values[RecNo, 1]);
end;
cxMemo1.Lines.Add(' *** Visible Records ***');
for i := 0 to cxGrid1DBTableView1.ViewInfo.VisibleRecordCount - 1 do
begin
cxCustomGridRecordViewInfo := cxGrid1DBTableView1.ViewInfo.RecordsViewInfo[i];
cxMemo1.Lines.Add(cxCustomGridRecordViewInfo.GridRecord.Values[1]);
end;
cxMemo1.Lines.EndUpdate;
end;
So there you see Filtered records are the one you have in your grid after possibly after a filter had been applyed. Visible Record are the onec actually visible on the screen.

Delphi stringlist finding negative keyword in list

I have two string lists that I'm working with. One that has a list of keywords, and then another that has a list of negative keywords. I want to be able to search through the list and pick out the list items that do not contain the negative keyword and output to a third keyword list. I was using the AnsiPos function but that found the negative keywords if they were part of a word, vs full word.
Any suggestions on a relatively easy way to do this? Speed is not that important, but would be nice.
Example of what I'm looking to do:
Keyword List:
Cat
Catfish
Fish Sticks
Dog Food
Negative Keyword List:
Fish
Returned Values Wanted:
Cat
Catfish
Dog Food
This is what I've got so far.. which does not work. I used information from: Is There An Efficient Whole Word Search Function in Delphi?
function ExistWordInString(aString: PAnsichar; aSearchString: string;
aSearchOptions: TStringSearchOptions): Boolean;
var
Size : Integer;
begin
Size := StrLen(aString);
result := SearchBuf(aString, Size, 0, 0, aSearchString, aSearchOptions) <> nil;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
i, j, index: integer;
s: string;
stl: tstringlist;
begin
stl := TStringList.Create;
stl.Text := listbox1.Items.Text;
for I := 0 to stl.Count - 1 do
begin
for j := 0 to listbox2.Count - 1 do
begin
if not ExistWordInString(PAnsiChar(listbox2.Items.Strings[j]),
listbox1.Items.Strings[i], [soWholeWord, soDown])
then
listbox3.Items.Append(stl.Strings[i]);
end;
end;
end;
If spaces are the only word delimiter you need to worry about, then you can do a whole word match using AnsiPos by adding a space before and after both the keyword and the negative keyword, ie
AnsiPos(' '+SubStr+' ', ' '+Str+' ')
You'd need a loop to check every entry from the negative keyword list.
this sample code works like a charm (using Delphi 7):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, StrUtils;
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
ListBox3: TListBox;
procedure Button1Click(Sender: TObject);
private
function ExistWordInString(aString, aSearchString:string;aSearchOptions: TStringSearchOptions): Boolean;
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
i,k: integer;
begin
for k:= 0 to ListBox2.Count -1 do
for i:= 0 to ListBox1.Count - 1 do
begin
if not ExistWordInString(ListBox1.Items[i], ListBox2.Items[k],[soWholeWord,soDown]) then
ListBox3.Items.Append(ListBox1.Items[i]);
end;
end;
function TForm1.ExistWordInString(aString, aSearchString: string; aSearchOptions: TStringSearchOptions): Boolean;
var
Size : Integer;
begin
Size:=Length(aString);
Result := SearchBuf(PChar(aString), Size, 0, 0, aSearchString, aSearchOptions)<>nil;
end;
end.
and here's the form:
object Form1: TForm1
Left = 1008
Top = 398
Width = 411
Height = 294
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 320
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object ListBox1: TListBox
Left = 8
Top = 8
Width = 177
Height = 97
ItemHeight = 13
Items.Strings = (
'Cat '
'Catfish'
'Fish Sticks'
'Dog Food')
TabOrder = 1
end
object ListBox2: TListBox
Left = 192
Top = 8
Width = 121
Height = 97
ItemHeight = 13
Items.Strings = (
'Fish')
TabOrder = 2
end
object ListBox3: TListBox
Left = 8
Top = 112
Width = 305
Height = 137
ItemHeight = 13
TabOrder = 3
end
end
hope this helps.
Reinhard :-)
I think I figured it out. Use stringlist.find('fish',index);
I didn't figure it out. .find did not work.
-Brad
You can use the SearchBuf function (see the pastacool's answer) IF you are not interested in other characters except A..Z / Unicode.
If you have an Unicode Delphi (D2009 or D2010) then you must use TCharacter.IsLetterOrDigit(aString: string; aIndex: integer): boolean; from the Character unit. A simple example for you to get the idea:
procedure TForm7.btn1Click(Sender: TObject);
var
bMatches: boolean;
begin
with rgx1 do //custom component - disregard it
begin
RegEx:=edtTextToFind.Text; //text to find
Subject:=mmoResult.Text; //text in which to search
if Match then //aha! found it!
begin
bMatches:=True;
if chkWholeWord.Checked then //be attentive from here!! - I think that's self explaining...
begin
if MatchedExpressionOffset>1 then
bMatches:=not TCharacter.IsLetterOrDigit(Subject, MatchedExpressionOffset-1);
if bMatches and (MatchedExpressionOffset+MatchedExpressionLength<=Length(Subject)) then
bMatches:=not TCharacter.IsLetterOrDigit(Subject, MatchedExpressionOffset+MatchedExpressionLength);
end;
if bMatches then //select it in the memo
begin
mmoResult.SelStart:=MatchedExpressionOffset-1;
mmoResult.SelLength:=MatchedExpressionLength;
mmoResult.SetFocus;
end
else
ShowMessage('Text not found!');
end
else
ShowMessage('Text not found!');
end;
end;
Change your function to read:
function ExistWordInString(aString:PAnsichar;
aSearchString:string;
aSearchOptions: TStringSearchOptions): Boolean;
var
b : boolean;
begin
if soWholeWord in aSearchOptions then
b := Pos(' '+Uppercase(aSearchString)+' ',' '+UpperCase(aString)+' ') > 0;
else
b := Pos(UpperCase(aSearchString),UpperCase(aString)) > 0;
Result := b;
end;
If your using Delphi 2009/2010 then change it from Pos to AnsiPos. My assumption here is that soWholeWord means that the match "Fish" would match "Fish Sticks" but not "catfish".

Resources