DataSet filters and title menu filters
I have a form that has a checkbox for the user to toggle a filter on the dataset / grid. The grid also has title menu filters enabled via TitleMenuAttributes.Options (sfoFilter).
When filters are added/removed using the title menu, the dataset's OnFilterRecord and Filtered property are modified. But their previous value/state is ignored. If I already have TDataSet.OnFilterRecord assigned, that event will not be called once a filter is added via the title menu. Further, the prior OnFilterRecord is not restored if the title menu filter is cleared.
To allow combined filtering (title menu and my custom filter), I implemented OnAfterMenuFilter to update the dataset filter as needed. However this looks (and smells) as though there either is, or should be, an easier way to accomplish my goal. Here is a redacted version of my code:
procedure TForm1.cbxMyFilterClick(Sender: TObject);
begin
UpdateFilter(None);
end;
procedure TForm1.DBGrid1AfterMenuFilter(Sender: TObject; AFieldName: string; AFilterAction: TwwGridFilterAction);
begin
UpdateFilter(AFilterAction);
end;
procedure TForm1.UpdateFilter(AFilterAction: TwwGridFilterAction);
begin
var bUseMyFilter := cbxMyFilter.Checked;
var bFiltered := DataSet1.Filtered;
case AFilterAction of
None: begin
if bUseMyFilter then
if Assigned(DataSet1.OnFilterRecord) then
TwwFilterDialog(DBGrid1.FilterDialog).OnAcceptFilterRecord := AcceptFilterRecord
else
DataSet1.OnFilterRecord := DataSet1FilterRecord;
DataSet1.Filtered := False;
DataSet1.Filtered := bFiltered or bUseMyFilter;
end;
ClearAll,
Remove: begin
if bUseMyFilter then
if bFiltered then
TwwFilterDialog(DBGrid1.FilterDialog).OnAcceptFilterRecord := AcceptFilterRecord
else
DataSet1.OnFilterRecord := DataSet1FilterRecord;
DataSet1.Filtered := False;
DataSet1.Filtered := bFiltered or bUseMyFilter;
end;
Add: begin
if bUseMyFilter then
TwwFilterDialog(DBGrid1.FilterDialog).OnAcceptFilterRecord := AcceptFilterRecord;
end;
end;
end;
procedure TForm1.AcceptFilterRecord(Sender: TObject; DataSet: TDataSet; var Accept: boolean; var DefaultFiltering: boolean);
begin
if cbxMyFilter.Checked then
Accept := Accept and (DataSet1Value.AsInteger = 0);
end;
procedure TForm1.dsVendorFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
if cbxMyFilter.Checked then
Accept := DataSet1Value.AsInteger = 0;
end;
-
You can have the filtering not use the OnFilterRecord and instead use the Filter Property of the dataset to avoid any conflicts. To do this, you can initialize your grid as follows.
// By calling FormGridFilterTypesNeeded it will initialize this data structure so it would not be nil afterwards.
wwdbgrid3.FormGridFilterTypesNeeded;
// Use FilterProp instead of callback filter
TwwFilterDialog(wwDBGrid3.FormGridFilterTypes.FilterDialog).FilterPropertyOptions.DatasetFilterType:= fdUseFilterProp; -
I'm posting my current approach in case someone else wants to do something similar.
Using your suggestion, my first approach was to manually modify the dataset's Filter property, adding or removing my filter to the one set by TwwFormGridFilterTypes. Although that worked, I didn't like working around the grid's filtering implementation.
I was able to accomplish what I want with the procedure below, which is called from the OnClick event that toggles my filter and the grid's OnAfterMenuFilter event handler. In my scenario, MyField is not shown in the grid and the user cannot add it via Add Column. If the user could, the procedure below would have to accommodate any filtering that the user set on the field via the title menu filter.
procedure TForm1.UpdateVendorFilter(AFilterAction: TwwGridFilterAction);
const
MYFIELD = 'MyField';
begin
// I pass None for AFilterAction when called from the "UseMyFilter" checkbox OnClick event.
if not (AFilterAction in [None, ClearAll]) then
Exit;
if cbxNoSend1099Claims.Checked then begin
var item := TwwFilterDialog(dbGridVendor.FormGridFilterTypes.FilterDialog).AddFieldInfo();
item.fieldName := MYFIELD;
item.DisplayLabel := item.fieldName;
item.MatchType := fdMatchExact;
item.filterValue := '0';
end
else begin
for var iLoop := 0 to TwwFilterDialog(dbGridVendor.FormGridFilterTypes.FilterDialog).FieldInfo.Count - 1 do begin
var itm := TwwFilterDialog(dbGridVendor.FormGridFilterTypes.FilterDialog).FieldInfo[iLoop];
if TwwFieldInfo(itm).FieldName = MYFIELD then begin
TwwFilterDialog(dbGridVendor.FormGridFilterTypes.FilterDialog).FieldInfo.Delete(iLoop);
TwwFieldInfo(itm).Free();
break;
end;
end;
end;
TwwFilterDialog(dbGridVendor.FormGridFilterTypes.FilterDialog).ApplyFilter();
end;In addition to your code above, I had to set the FilterDialog's DataSource property, for the scenario where a user clicks my filter checkbox before or without filtering via the grid's title buttons.
TwwFilterDialog(dbGridVendor.FormGridFilterTypes.FilterDialog).DataSource := dtsrcBlah;
Please sign in to leave a comment.
Comments
3 comments