Как запретить перенос кросс-таба на next page?

отредактировано 10:17 Раздел: FastReport 3.0
Как запретить перенос кросс-таба на следющую страницу без обрезания!(это делает DownThenAcross=True) не влазящих колонок? Т.е. чтобы впихивало все колонки на ширину страницы с автоподбором их ширины и высоты также как при превышении колонкой ширины MaxWidth. Или сие невозможно?

Комментарии

  • отредактировано 10:17
    Доброго времени суток всем.
    Меня то же интересует как сделать чтоб группа не разрывалась
    и каждая новая страница начиналась с новой группы?
    И еще, как определить высоту заголовка колонок, от чего она зависит?
  • отредактировано 10:17
    Irina написал:
    Меня то же интересует как сделать чтоб группа не разрывалась
    UserManual стр. 59
    Есть еще способ,позволяющий избежать разрыва группы.Для этого надо
    включить свойство заголовка группы "Не разрывать группу"(или KeepTogether в
    инспекторе объектов).При этом,если вся группа не помещается на странице,ее
    вывод переносится на новую страницу.
    Irina написал:
    и каждая новая страница начиналась с новой группы?
    StartNewPage там же
  • отредактировано 10:17
    Markus написал:
    Markus написал:
    Есть еще способ,позволяющий избежать разрыва группы.Для этого надо
    включить свойство заголовка группы "Не разрывать группу"(или KeepTogether в
    инспекторе объектов).При этом,если вся группа не помещается на странице,ее
    вывод переносится на новую страницу.
    В теме же речь идет о Cross? ;)
    У этого компонента я лично данного свойства не нашда ;)
  • отредактировано 10:17
    Ну и о cross тоже, если использовать Oldstyled cross-tab.
  • отредактировано January 2005
    Похоже, что стандартными средствами эта трабла не решается. ;)

    Если кому интересно, сейчас заканчиваю тестирование решения этой проблемы, c небольшими издержками во внешнем коде, на базе демки PrintTable (в которой почти пол-кросса перекидывается на следующую страницу, т.к. все колонки не вмещаются по ширине), могу закинуть сюда.

    Т.е. сделал грамотное (на мой взгляд ;) ) масштабирование кросса целиком по ширине страницы (бэнда если угодно) - если не влазит по ширине или меньше ширины Parent'а, то пересчитываются и изменяются ширины колонок таким образом, чтобы все колонки были полностью распределены по ширине того на ком лежит кросс. Если ширины колонок увеличиваются, то сохраняются пропорции ширин рассчитаных изначально кроссом, если уменьшаются то уменьшаются ширины самых "жадных" колонок без изменения ширин "не жадных" ;) Все форматирования текста (переносы и т.п.) и изменение высот строк кросс делает сам. Кароче это то, что мне было изначально нужно. А то таблицу/набор данных невозможно нормально распечатать. ;)
  • отредактировано 10:17
    По поступившей на мыло просьбе скидываю демку того, что было описано в предыдущем посте, работает на ура ща собираюсь использовать этот подход в проекте, единственное что не мешало бы проверить на больших выборках данных насколько быстро это будет работать ...
    Просьба не пинать за корявые английские комменты ;) , т.к. я в ангельском не очень волоку, но думаю это лучше чем вообще без комментариев.
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      frxClass, StdCtrls, frxCross, Db, DBTables;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        frxCrossObject1: TfrxCrossObject;
        Table1: TTable;
        frxReport1: TfrxReport;
        procedure Button1Click(Sender: TObject);
        procedure frxReport1BeforePrint(c: TfrxReportComponent);
      private
        { Private declarations }
        wcs: array of string;  //widest cells strings by columns
        cws: array of integer; //columns widths
    
        bCrossCalc: boolean;   //Sign of calculation
    
        FOldOcw: TfrxOnCalcWidthEvent; //For original handler savå and call
    
        procedure DoReport;
        procedure FindWidestCells;
    
        procedure OnPrintHeader(Memo: TfrxCustomMemoView; const HeaderIndexes,
          HeaderValues, Value: Variant);
    
        procedure OnCalcWidth(ColumnIndex: Integer; const ColumnValues: Variant;
          var Width: Extended);
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses Printers;
    
    {$R *.DFM}
    
    //##############################################################################
    const
      DEC_FIELDS: integer = 0; //Decreasing number of printed fields (for examination)
        //!!!! Font must be as font of cross object cells !!! (at least Name)
      CELL_FONT_NAME = 'Arial';
      CELL_FONT_SIZE = 10;
    //##############################################################################
    
    function Squeeze(var arw: array of integer; pw: integer): integer; //arw - array of widths, pw - place width
    var //True squeezing widths (arw) elements into available width (pw). Copyright (c) 2005 GlooM :)
      i, n: integer;
      avr: integer;           //average
      bPass: boolean;         //Sign of passage
      unp: array of ^integer; //unprocessed (not passed) elements pointers
    begin
        //Initializations
      avr := 0;
      bPass := True;
      n := Length(arw);
      SetLength(unp, n);
      for i := 0 to n - 1 do unp[i] := @arw[i];
    
        //Processing
      while n > 0 do //unpassed elements exists
      begin
        if bPass then avr := pw div n            //pass exist, get new average
        else
        begin                                    //not once not be passed
          for i := 0 to n - 1 do
          begin                                  //all unpassed set to previous average
            unp[i]^ := avr;
            Dec(pw, avr);                        //with decrease spase rest
          end;
          n := 0;                                //and processing is complete!
          SetLength(unp, n);
          Break;
        end;
    
        bPass := False;
        i := 0;
        while i < n do
        begin       //Checking
          if unp[i]^ <= avr then  //îê!
          begin     //Pass this element:
            Dec(pw, unp[i]^);         //decrease spase rest
              //throw out current (i) element:
            n := Length(unp) - 1;     //current last index of elemnt, and new length
            unp[i] := unp[n];         //move last to current (i)
            SetLength(unp, n);        //free last
            bPass := True;
            Continue;                 //without i increase!!!
          end;
          inc(i);
        end;
    
      end; //while ... //Processing End
    
      Result := avr;  //!!! Max average !!! is used as MaxWidth for Squeezed CrossView!!!
    
      // Delete or set next blok as a comment, if fine tunning is not required
      if (avr > 0) and (pw > 0) then  //Still exist free space. Fine tunning:
        for i := 0 to Length(arw) - 1 do
          if arw[i] = avr then
          begin
            Inc(arw[i]);
            Dec(pw);
            if not (pw > 0) then Break;   //no more free space
          end;
    end; //Squeeze ... End
    
    procedure Stretch(var arw: array of integer; pw: integer);
    var i, w: integer;
      {------------------------------------------------}
      procedure CalcSumWidth;
      var k: integer;
      begin
        w := 0;
        for k := 0 to Length(arw) - 1 do Inc(w, arw[k]); //Sum of elements widths
      end;
      {------------------------------------------------}
    begin         //Proportional stretch widths (arw) on a space width (pw)
      CalcSumWidth;                        //Sum of elements widths before stretching
    
      for i := 0 to Length(arw) - 1 do     //New elements widths
        arw[i] := Round(arw[i] * (pw / w));
    
      CalcSumWidth;                        //Sum of elements widths after stretching
    
      if w <> pw then Squeeze(arw, pw);    //Fine tunning if need
    end;
    
    procedure TForm1.FindWidestCells;
    var
      fc: integer;      //fields count
      prn: TPrinter;
      tmpStr: string;
      i, tmpW: integer;
    begin
      fc := Table1.Fields.Count - DEC_FIELDS;
      if not (fc > 0) then Exit;
    
      SetLength(wcs, fc);
      SetLength(cws, fc);
    
      ZeroMemory(@cws[Low(cws)], SizeOf(cws[Low(cws)]) * fc);
      for i := 0 to fc - 1 do wcs[i] := '';
    
      prn := Printer;
      with prn.Canvas.Font do
      begin         //!!!! Font must be as font of cross object cells !!!
        Name := CELL_FONT_NAME;
        Size := CELL_FONT_SIZE;
      end;
    
      Table1.First;
      while not Table1.Eof do
      begin
        for i := 0 to fc - 1 do with Table1 do
        begin
          tmpStr := Trim(Fields[i].AsString); //!!!! Added real data as well must be trimmed !!!
          tmpW := prn.Canvas.TextWidth(tmpStr);
    
          if tmpW > cws[i] then
          begin
            cws[i] := tmpW;
            wcs[i] := tmpStr;
          end;
    
        end;
        Table1.Next;
      end;
    
    end; //FindWidestCells ... End
    
    procedure TForm1.OnPrintHeader (Memo: TfrxCustomMemoView;
      const HeaderIndexes, HeaderValues, Value: Variant);
    begin
      cws[integer(HeaderIndexes[0])] := Trunc(Memo.Width); //Get column width from cross-object
    end;
    
    procedure TForm1.OnCalcWidth(ColumnIndex: Integer; const ColumnValues: Variant;
      var Width: Extended);
    begin
      try
        Width := cws[ColumnIndex]; //Setup my calculated column width
      finally
        if Assigned(FOldOcw) then FOldOcw(ColumnIndex, ColumnValues, Width);
      end;
    end;
    
    procedure TForm1.DoReport;
    var
      cross: TfrxCrossView;
      OldOph: TfrxOnPrintHeaderEvent;
      ParW: integer;  //Parent Width
      RowW: integer;  //Row Width
      {----------------------------------------------------------}
      function GetCross: boolean;
      begin
        cross := frxReport1.FindObject('Cross1') as TfrxCrossView;
        Result := Assigned(cross);
      end;
      {----------------------------------------------------------}
      procedure CalcRowWidth;
      var i: integer;
      begin
        RowW := 0;
        for i := 0 to Length(cws) - 1 do Inc(RowW, cws[i]);
      end;
      {----------------------------------------------------------}
      procedure RunReport;
      begin
        frxReport1.PrepareReport;
        frxReport1.ShowPreparedReport;
      end;
      {----------------------------------------------------------}
    begin
      if not GetCross then Exit;
    
      FindWidestCells;   //Get widest cells strings by conlumns into wcs
    
      cross.MaxWidth := MaxWord;         //Unrestricted!!!
      cross.MinWidth := 0;               //Unrestricted!!!
    
      ParW := Trunc(cross.Parent.Width); //Get cross parent width (max allowable!!!)
    
      //Get prepared columns widths to cws array
      OldOph := cross.OnBeforePrintColumnHeader;          //Save handler
      try
        cross.OnBeforePrintColumnHeader := OnPrintHeader; //Set my new handler
        bCrossCalc := True;                               //Set calculation sign
        frxReport1.PrepareReport;
      finally
        cross.OnBeforePrintColumnHeader := OldOph;        //Restore handler
        bCrossCalc := False;                              //Unset calculation sign
      end;
    
      CalcRowWidth; //Get row width
    
      if not GetCross then Exit; //Get cross again
    
      if RowW > ParW then                     //larger than allowable
      begin                                   //squeezing need
        Cross.MaxWidth := Squeeze(cws, ParW); //Max allowable column width setup
        RunReport;
      end
      else                                    // = or <
        if RowW < ParW then                   //smaller than parent width
        begin                                 //Stretching need
          Stretch(cws, ParW);                 //New widths setup into cws array
          FOldOcw := cross.OnBeforeCalcWidth; //Save original handler
          try
            cross.OnBeforeCalcWidth := OnCalcWidth; //New my handler with columns width management
            RunReport;
          finally
            cross.OnBeforeCalcWidth := FOldOcw;     //Restore original handler
          end;
        end;
    end; //DoReport ... End
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      DoReport;
    end;
    
    procedure TForm1.frxReport1BeforePrint(c: TfrxReportComponent);
    var
      Cross: TfrxCrossView;
      i, j: Integer;
    begin
      if c is TfrxCrossView then
      begin
        Cross := TfrxCrossView(c);
    
        if not bCrossCalc then //Real print
        begin
          Table1.First;
          i := 0;
          while not Table1.Eof do
          begin
            for j := 0 to Table1.Fields.Count - 1 - DEC_FIELDS do
              Cross.AddValue([i], [Trim(Table1.Fields[j].DisplayLabel)],
                [Trim(Table1.Fields[j].AsString)]);
            Table1.Next;
            Inc(i);
          end;
        end
        else    //Prepare calculate
        begin   //Add only one most wide row
          for j := 0 to Length(wcs) - 1 do
            Cross.AddValue([0], [Trim(Table1.Fields[j].DisplayLabel)], [wcs[j]]);
        end;
      end;
    end;
    
    end.
    

    Для того чтобы не пихало на страницу такое кол-во полей (что в реале навряд ли может быть, или поля будут гораздо меньшей ширины, т.к. страница то не резиновая ;) ) можно немного уменьшить количество выводимых полей указав уменьшение в константе DEC_FIELDS и посмотреть как это будет выглядеть в более реальных условиях. Хотя всё работает для любых условий, разве что если кол-во полей будет таким, что при расчёте ширины полей окажутся не > 0.
    Спасибо за внимание. Удачи. ;)

Оставить комментарий

Многофункциональный текстовый редактор. Чтобы отредактировать стиль параграфа, нажмите TAB, чтобы перейти к меню абзаца. Там вы можете выбрать стиль. По умолчанию не выбран ни один стиль. Когда вы выберете текст, появится встроенное меню форматирования. Нажмите TAB, чтобы войти в него. Некоторые элементы, такие как многофункциональные вставки ссылок, картинок, индикаторов загрузки и сообщений об ошибок могут быть вставлены в редактор. Вы можете перемещаться по ним, используя стрелки внутри редактора и удалять с помощью клавиш delete или backspace.