Search code examples
delphistored-proceduresadodbdbgrid

Delphi DBgrid Expand/Collapse


I'm trying to implement an expand/collapse option in the Delphi DBGrid. Sadly it is not a default supported option. The data used for display is comming from an ADOStoredProcedure.

The function has to be made for the DBGrid, using a alternative component lib is not an option. The SMDBGrid is avalible though (also does not support e/c)

Searching Google has not given me anything usefull yet. I'm wondering if anyone here has tackled this problem or an idea of how to go at this.

Thanks in advance!


Solution

  • Took some time but I managed to create te functionality for the DBGrid using a dynamic filter on the dataset. I'll post the code for someone who would need this functionality.

    Class to store expanded items

    type
      //Store ID's used to keep expanded items
      TPlanningFilterItem = class(TObject)
        public
          Sublevel, ProjectID, OnderdeelID, MedewerkerID: integer;
      end;
    

    Capture indicator click

      procedure TFPlanningOverzicht.GridPlanningDblClick(Sender: TObject);
      var
        P: TPoint;
        C: TGridCoord;
      begin
        GetCursorPos(P);
        P := (Sender as TCustomGrid).ScreenToClient(P);
        C := (Sender as TCustomGrid).MouseCoord(P.X, P.Y);
    
        //Only capture indicator row X = 0
        //Ignore title indicator Y > 1
        if (C.X = 0) AND (C.Y > 0) then
          DatasetFilterToevoegenVerwijderen;
          FilterDataSet;
        begin
        end;
      end;
    

    Add or remove filter

      procedure TFPlanningOverzicht.DatasetFilterToevoegenVerwijderen;
      var
        newFilterItem: TPlanningFilterItem;
        tmp: TPlanningFilterItem;
        I: Integer;
      begin
          newFilterItem := TPlanningFilterItem.Create;
          newFilterItem.Sublevel := DPlanning.PlanningOverzicht.FieldByName('SUBLEVEL').AsInteger;
          newFilterItem.ProjectID := DPlanning.PlanningOverzicht.FieldByName('ProjectID').AsInteger;
          newFilterItem.OnderdeelID := DPlanning.PlanningOverzicht.FieldByName('OnderdeelID').AsInteger;
          newFilterItem.MedewerkerID := DPlanning.PlanningOverzicht.FieldByName('MedewerkerID').AsInteger;
    
          //Ignore expand when deepest lvl reached
          if newFilterItem.Sublevel > 2 then
            Exit;
    
          for I := 0 to GridFilterItems.Count - 1 do
          begin
            //Compare to existing
            tmp := GridFilterItems.Items[I];
            if (tmp.Sublevel = newFilterItem.Sublevel) AND
                (tmp.ProjectID = newFilterItem.ProjectID) AND
                (tmp.OnderdeelID = newFilterItem.OnderdeelID) AND
                (tmp.MedewerkerID = newFilterItem.MedewerkerID)
            then
            begin
              //If item currently expanded collapse and exit
              GridFilterItems.Delete(I);
              Exit;
            end;
          end;
    
          //Item not yet expanded, so expand
          GridFilterItems.Add(newFilterItem);
      end;
    

    Applying the filter

      procedure TFPlanningOverzicht.FilterDataSet;
      var
        I: integer;
        tmp: TPlanningFilterItem;
        Filter: string;
        C: Integer;
      begin
        //Always show top level items
        Filter := '(SUBLEVEL = ''' + IntToStr(1) + ''' ) OR ';
    
        for I := 0 to GridFilterItems.Count - 1 do
        begin
          tmp := GridFilterItems[I];
    
          //Expand when 1st row selected (shoud be written to your case)
          if (tmp.Sublevel= 1) then
          begin
            Filter := Filter +
            '(MedewerkerID = ''' + IntToStr(tmp.MedewerkerID) + ''' AND ' +
            'SUBLEVEL = ''' + IntToStr(2) + ''' ) OR ';
          end
          else
          begin
            //Expands for the second level (shoud be written to your case)
            Filter := Filter +
            '(MedewerkerID = ''' + IntToStr(tmp.MedewerkerID) + ''' AND ' +
            'OnderdeelID = ''' + IntToStr(tmp.OnderdeelID) + ''' AND ' +
            'SUBLEVEL = ''' + IntToStr(3) + ''' ) OR ';
          end;
        end;
    
        //Remove last or
        Delete(Filter, Filter.Length - 2, 3);
    
        GridPlanning.DataSource.DataSet.Filter := Filter;
        GridPlanning.DataSource.DataSet.Filtered := True;
      end;
    

    Hope this would be usefull to someone.