VCL, избавляемся от мерцания, раз и навсегда

  • Tutorial

Delphi и C++Builder разработчики, использующие VCL не по наслышке знают о вездесущей проблеме мерцания контролов. Мерцание происходит при перерисовке, вследствие того, что сначала отрисовываеться фон компонента, и только потом сам компонент.


И если в случае с наследниками от TWinControl частичным решением проблемы является установка свойства DoubleBuffered в True, что заставляет контрол отрисовываться в буфере (однако DoubleBuffered работает тоже не идеально, к прим.: контрол перестает быть прозрачным), то в случае с TGraphicControl решение с DoubleBuffered просто невозможно, из-за отсутствия у TGraphicControl окна, установка же DoubleBuffered в True у родителя не помогает, из-за того что отрисовка вложенных TGraphicControl-ов происходит уже после прорисовки родителя в буфере.


Обычно остается только одно — смириться с мерцанием, и максимально упростить отрисовку для минимизации эффекта, или использовать по возможности исключительно TWinControl-ы, что не всегда возможно и удобно.


Однажды намучившись с мерцанием, я не выдержал и решил решить эту проблему, раз и навсегда!


Как мне удалось решить проблему?


Заранее извиняюсь за некоторую сумбурность подачи, и недосказанность, описывать подобные вещи довольно сложно, однако поделиться с сообществом хочется.


Был разработан класс TEsCustomControl = class(TWinControl), который осуществляет альтернативную буферизацию (при DoubleBuffered = False, иначе используется родная буферизация VCL).


Класс имеет свойство BufferedChildren, при активации которого отрисовка вложенных TGraphicControl-ов происходит в буфере, что полностью избавляет от мерцания.


К счастью в VCL нужные методы отрисовки объявлены не как private, что и позволило реализовать полную буферизацию.


Для того чтобы компонент выглядел прозрачным, необходимо отрисовать на нем фон нижележащего компонента, что осуществляется с помощью процедуры DrawParentImage.


procedure DrawParentImage(Control: TControl; DC: HDC; InvalidateParent: Boolean = False);
var
  ClientRect: TRect;
  P: TPoint;
  SaveIndex: Integer;
begin
  if Control.Parent = nil then
    Exit;
  SaveIndex := SaveDC(DC);
  GetViewportOrgEx(DC, P);

  // if control has non client border then need additional offset viewport
  ClientRect := Control.ClientRect;
  if (ClientRect.Right <> Control.Width) or (ClientRect.Bottom <> Control.Height) then
  begin
    ClientRect := CalcClientRect(Control);
    SetViewportOrgEx(DC, P.X - Control.Left - ClientRect.Left, P.Y - Control.Top - ClientRect.Top, nil);
  end else
    SetViewportOrgEx(DC, P.X - Control.Left, P.Y - Control.Top, nil);

  IntersectClipRect(DC, 0, 0, Control.Parent.ClientWidth, Control.Parent.ClientHeight);

  Control.Parent.Perform(WM_ERASEBKGND, DC, 0);
  Control.Parent.Perform(WM_PRINTCLIENT, DC, PRF_CLIENT);

  RestoreDC(DC, SaveIndex);

  if InvalidateParent then
    if not (Control.Parent is TCustomControl) and not (Control.Parent is TCustomForm) and
       not (csDesigning in Control.ComponentState)and not (Control.Parent is TEsCustomControl) then
    begin
      Control.Parent.Invalidate;
    end;

  SetViewportOrgEx(DC, P.X, P.Y, nil);
end;

Буферизация происходит за счет того что компонент в переопределенном методе PaintWindow отрисовываеться не непосредственно на предоставленный хендл, а на временный (или нет в зависимости от свойства IsCachedBuffer) HBITMAP, и уже после полной отрисовки копируется функцией BitBlt.


(Довольно много кода, из-за многих частных случаев)


TEsCustomControl.PaintWindow
procedure TEsCustomControl.PaintWindow(DC: HDC);
var
  TempDC: HDC;
  UpdateRect: TRect;
  //---
  BufferDC: HDC;
  BufferBitMap: HBITMAP;
  Region: HRGN;
  SaveViewport: TPoint;
  BufferedThis: Boolean;
begin
  BufferBitMap := 0;
  Region := 0;
  BufferDC := 0;

  if GetClipBox(DC, UpdateRect) = ERROR then
    UpdateRect := ClientRect;

  BufferedThis := not BufferedChildren;

  // fix for designer selection
  BufferedThis := BufferedThis or (csDesigning in ComponentState);

  try
    if BufferedThis then
    begin
    //------------------------------------------------------------------------------------------------
    // Duplicate code, see PaintHandler, Please sync this code!!!
    //------------------------------------------------------------------------------------------------
      // if control not double buffered then create or assign buffer
      if not DoubleBuffered then
      begin
        BufferDC := CreateCompatibleDC(DC);
        // CreateCompatibleDC(DC) return 0 if Drawing takes place to MemDC(buffer):
        // return <> 0 => need to double buffer || return = 0 => no need to double buffer
        if BufferDC <> 0 then
        begin
          // Using the cache if possible
          if FIsCachedBuffer or FIsFullSizeBuffer then
          begin
            // Create cache if need
            if CacheBitmap = 0 then
            begin
              BufferBitMap := CreateCompatibleBitmap(DC, ClientWidth, ClientHeight);
              // Assign to cache if need
              if FIsCachedBuffer then
                CacheBitmap := BufferBitMap;
            end
            else
              BufferBitMap := CacheBitmap;

            // Assign region for minimal overdraw
            Region := CreateRectRgnIndirect(UpdateRect);//0, 0, UpdateRect.Width, UpdateRect.Height);
            SelectClipRgn(BufferDC, Region);
          end
          else
            // Create buffer
            BufferBitMap := CreateCompatibleBitmap(DC, RectWidth(UpdateRect), RectHeight(UpdateRect));
          // Select buffer bitmap
          SelectObject(BufferDC, BufferBitMap);
          // [change coord], if need
          // Moving update region to the (0,0) point
          if not(FIsCachedBuffer or FIsFullSizeBuffer) then
          begin
            GetViewportOrgEx(BufferDC, SaveViewport);
            SetViewportOrgEx(BufferDC, -UpdateRect.Left + SaveViewport.X, -UpdateRect.Top + SaveViewport.Y, nil);
          end;
        end
        else
          BufferDC := DC;
      end
      else
        BufferDC := DC;
    //------------------------------------------------------------------------------------------------
    end else
      BufferDC := DC;

    if not(csOpaque in ControlStyle) then
      if ParentBackground then
      begin
        if FIsCachedBackground then
        begin
          if CacheBackground = 0 then
          begin
            TempDC := CreateCompatibleDC(DC);
            CacheBackground := CreateCompatibleBitmap(DC, ClientWidth, ClientHeight);
            SelectObject(TempDC, CacheBackground);
            DrawBackground(TempDC); //DrawParentImage(Self, TempDC, False);
            DeleteDC(TempDC);
          end;
          TempDC := CreateCompatibleDC(BufferDC);
          SelectObject(TempDC, CacheBackground);
          if not FIsCachedBuffer then
            BitBlt(BufferDC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), TempDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY)
          else
            BitBlt(BufferDC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), TempDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY);
          DeleteDC(TempDC);
        end
        else
          DrawBackground(BufferDC); //DrawParentImage(Self, BufferDC, False);
      end else
        if (not DoubleBuffered or (DC <> 0)) then
          if not IsStyledClientControl(Self) then
            FillRect(BufferDC, ClientRect, Brush.Handle)
          else
          begin
            SetDCBrushColor(BufferDC,
              ColorToRGB({$ifdef VER230UP}StyleServices.GetSystemColor(Color){$else}Color{$endif}));
            FillRect(BufferDC, ClientRect, GetStockObject(DC_BRUSH));
          end;

    FCanvas.Lock;
    try
      Canvas.Handle := BufferDC;
      TControlCanvas(Canvas).UpdateTextFlags;

      if Assigned(FOnPainting) then
        FOnPainting(Self, Canvas, ClientRect);
      Paint;
      if Assigned(FOnPaint) then
        FOnPaint(Self, Canvas, ClientRect);
    finally
      FCanvas.Handle := 0;
      FCanvas.Unlock;
    end;

  finally
    if BufferedThis then
    begin
      //------------------------------------------------------------------------------------------------
      // Duplicate code, see PaintHandler, Please sync this code!!!
      //------------------------------------------------------------------------------------------------
      try
        // draw to window
        if not DoubleBuffered then
        begin
          if not(FIsCachedBuffer or FIsFullSizeBuffer) then
          begin
            // [restore coord], if need
            SetViewportOrgEx(BufferDC, SaveViewport.X, SaveViewport.Y, nil);
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC, 0, 0, SRCCOPY);
          end
          else
          begin
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY);
          end;
        end;
      finally
        if BufferDC <> DC then
          DeleteObject(BufferDC);
        if Region <> 0 then
          DeleteObject(Region);
        // delete buffer, if need
        if not FIsCachedBuffer and (BufferBitMap <> 0) then
          DeleteObject(BufferBitMap);
      end;
      //------------------------------------------------------------------------------------------------
    end;
  end;
end;

Буферизация вложенных TGraphicControl-ов реализована альтернативным методом PaintHandler, в котором происходит буферизация всех этапов прорисовки компонента, в том числе и отрисовки TGraphicControl-ов.


TEsCustomControl.PaintHandler
procedure TEsCustomControl.WMPaint(var Message: TWMPaint);
begin
  ControlState := ControlState + [csCustomPaint];

  // buffered childen aviable only for not DoubleBuffered controls
  if BufferedChildren and (not FDoubleBuffered) and
     not (csDesigning in ComponentState) then // fix for designer selection
  begin
    PaintHandler(Message)// My new PaintHandler
  end else
    inherited;

  ControlState := ControlState - [csCustomPaint];
end;

procedure TEsCustomControl.PaintHandler(var Message: TWMPaint);
var
  PS: TPaintStruct;
  BufferDC: HDC;
  BufferBitMap: HBITMAP;
  UpdateRect: TRect;
  SaveViewport: TPoint;
  Region: HRGN;
  DC: HDC;
  IsBeginPaint: Boolean;
begin
  BufferBitMap := 0;
  BufferDC := 0;
  DC := 0;
  Region := 0;
  IsBeginPaint := Message.DC = 0;

  try
    if IsBeginPaint then
    begin
      DC := BeginPaint(Handle, PS);
      {$IFDEF VER230UP}
      if TStyleManager.IsCustomStyleActive and not FIsCachedBuffer then
        UpdateRect := ClientRect
        // I had to use a crutch to ClientRect, due to the fact that
        // VCL.Styles.TCustomStyle.DoDrawParentBackground NOT use relative coordinates,
        // ie ignores SetViewportOrgEx!
        // This function uses ClientToScreen and ScreenToClient for coordinates calculation!
      else
      {$endif}
        UpdateRect := PS.rcPaint;
    end
    else
    begin
      DC := Message.DC;
      {$IFDEF VER230UP}
      if TStyleManager.IsCustomStyleActive and not FIsCachedBuffer then
        UpdateRect := ClientRect
      else
      {$endif}
        if GetClipBox(DC, UpdateRect) = ERROR then
          UpdateRect := ClientRect;
    end;

    //------------------------------------------------------------------------------------------------
    // Duplicate code, see PaintWindow, Please sync this code!!!
    //------------------------------------------------------------------------------------------------
    // if control not double buffered then create or assign buffer
    if not DoubleBuffered then
    begin
      BufferDC := CreateCompatibleDC(DC);
      // CreateCompatibleDC(DC) return 0 if Drawing takes place to MemDC(buffer):
      // return <> 0 => need to double buffer || return = 0 => no need to double buffer
      if BufferDC <> 0 then
      begin
        // Using the cache if possible
        if FIsCachedBuffer or FIsFullSizeBuffer then
        begin
          // Create cache if need
          if CacheBitmap = 0 then
          begin
            BufferBitMap := CreateCompatibleBitmap(DC, ClientWidth, ClientHeight);
            // Assign to cache if need
            if FIsCachedBuffer then
              CacheBitmap := BufferBitMap;
          end
          else
            BufferBitMap := CacheBitmap;

          // Assign region for minimal overdraw
          Region := CreateRectRgnIndirect(UpdateRect);//0, 0, UpdateRect.Width, UpdateRect.Height);
          SelectClipRgn(BufferDC, Region);
        end
        else
          // Create buffer
          BufferBitMap := CreateCompatibleBitmap(DC,
            UpdateRect.Right - UpdateRect.Left, UpdateRect.Bottom - UpdateRect.Top);
        // Select buffer bitmap
        SelectObject(BufferDC, BufferBitMap);
        // [change coord], if need
        // Moving update region to the (0,0) point
        if not(FIsCachedBuffer or FIsFullSizeBuffer) then
        begin
          GetViewportOrgEx(BufferDC, SaveViewport);
          SetViewportOrgEx(BufferDC, -UpdateRect.Left + SaveViewport.X, -UpdateRect.Top + SaveViewport.Y, nil);
        end;
      end
      else
        BufferDC := DC;
    end
    else
      BufferDC := DC;
    //------------------------------------------------------------------------------------------------

    // DEFAULT HANDLER:
    Message.DC := BufferDC;
    inherited PaintHandler(Message);

  finally
    try
      //------------------------------------------------------------------------------------------------
      // Duplicate code, see PaintWindow, Please sync this code!!!
      //------------------------------------------------------------------------------------------------
      try
        // draw to window
        if not DoubleBuffered then
        begin
          if not(FIsCachedBuffer or FIsFullSizeBuffer) then
          begin
            // [restore coord], if need
            SetViewportOrgEx(BufferDC, SaveViewport.X, SaveViewport.Y, nil);
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC, 0, 0, SRCCOPY);
          end
          else
          begin
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY);
          end;
        end;
      finally
        if BufferDC <> DC then
          DeleteObject(BufferDC);
        if Region <> 0 then
          DeleteObject(Region);
        // delete buffer, if need
        if not FIsCachedBuffer and (BufferBitMap <> 0) then
          DeleteObject(BufferBitMap);
      end;
      //------------------------------------------------------------------------------------------------
    finally
      // end paint, if need
      if IsBeginPaint then
        EndPaint(Handle, PS);
    end;
  end;
end;

Класс TEsCustomControl имеет несколько полезных свойств и событий:


  TPaintEvent = procedure(Sender: TObject; Canvas: TCanvas; Rect: TRect) of object;

  /// <summary> The best replacement for TCustomControl, supports transparency and without flicker </summary>
  TEsCustomControl = class(TWinControl)
  ...
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure UpdateBackground(Repaint: Boolean); overload;
    procedure UpdateBackground; overload;
    // ------------------ Properties for published -------------------------------------------------
    property DoubleBuffered default False;
    {$IFDEF VER210UP}
    property ParentDoubleBuffered default False;
    {$ENDIF}
    // Painting for chidrens classes
    property OnPaint: TPaintEvent read FOnPaint write FOnPaint;
    property OnPainting: TPaintEvent read FOnPainting write FOnPainting;
    // BufferedChildrens
    property ParentBufferedChildren: Boolean read FParentBufferedChildren write SetParentBufferedChildren default True;
    property BufferedChildren: Boolean read FBufferedChildren write SetBufferedChildren stored IsBufferedChildrenStored;
    // External prop
    property IsCachedBuffer: Boolean read FIsCachedBuffer write SetIsCachedBuffer default False;
    property IsCachedBackground: Boolean read FIsCachedBackground write SetIsCachedBackground default False;
    property IsDrawHelper: Boolean read FIsDrawHelper write SetIsDrawHelper default False;
    property IsOpaque: Boolean read GetIsOpaque write SetIsOpaque default False;
    property IsFullSizeBuffer: Boolean read FIsFullSizeBuffer write SetIsFullSizeBuffer default False;
  end;

Интересным может оказаться свойство IsDrawHelper рисующее удобную рамку в DesignTime.


image


Для создания своего не мерцающего компонента вам достаточно унаследоваться от TEsCustomControl, как если бы вы делали наследника от TCustomControl, и объявить нужные вам свойства как published.


TEsCustomControl дает полное управление процессом буферизации и отрисовки, и доказал свою надежность во многих проектах и компонентах.


image


Для примера можно рассмотреть компонент TEsLayout — прозрачный Layout с возможностью буферизации вложенных в него TGraphicControl-ов:
https://github.com/errorcalc/FreeEsVclComponents/blob/master/Source/ES.Layouts.pas


И под спойлером
{******************************************************************************}
{                            EsVclComponents v2.0                              }
{                           ErrorSoft(c) 2009-2016                             }
{                                                                              }
{                     More beautiful things: errorsoft.org                     }
{                                                                              }
{           errorsoft@mail.ru | vk.com/errorsoft | github.com/errorcalc        }
{              errorsoft@protonmail.ch | habrahabr.ru/user/error1024           }
{                                                                              }
{         Open this on github: github.com/errorcalc/FreeEsVclComponents        }
{                                                                              }
{ You can order developing vcl/fmx components, please submit requests to mail. }
{ Вы можете заказать разработку VCL/FMX компонента на заказ.                   }
{******************************************************************************}
unit ES.Layouts;

interface

uses
  Winapi.Messages, Vcl.Controls, System.Classes, System.Types, Vcl.Graphics, ES.BaseControls,
  ES.CfxClasses;

type
  TEsCustomLayout = class(TEsBaseLayout)
  private
    FLocked: Boolean;
    procedure CMIsToolControl(var Message: TMessage); message CM_ISTOOLCONTROL;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    property UseDockManager default True;
  public
    constructor Create(AOwner: TComponent); override;
    property Color default clBtnFace;
    property DockManager;
    property Locked: Boolean read FLocked write FLocked default False;
  end;

  TEsLayout = class(TEsCustomLayout)
  published
    property Align;
    property Anchors;
    property AutoSize;
    property BiDiMode;
    property BorderWidth;
    property BufferedChildren;// TEsCustomControl
    property Color;
    property Constraints;
    property Ctl3D;
    property UseDockManager;
    property DockSite;
    property DoubleBuffered;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property IsCachedBuffer;// TEsCustomControl
    property IsCachedBackground;// TEsCustomControl
    property IsDrawHelper;// TEsCustomControl
    property IsOpaque;// TEsCustomControl
    property IsFullSizeBuffer;// TEsCustomControl
    property Locked;
    property Padding;
    property ParentBiDiMode;
    property ParentBackground;
    property ParentBufferedChildren;// TEsCustomControl
    property ParentColor;
    property ParentCtl3D;
    property ParentDoubleBuffered;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Touch;
    property Visible;
    {$if CompilerVersion > 23}
    property StyleElements;
    {$ifend}
    property OnAlignInsertBefore;
    property OnAlignPosition;
    property OnCanResize;
    property OnClick;
    property OnConstrainedResize;
    property OnContextPopup;
    property OnDockDrop;
    property OnDockOver;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGesture;
    property OnGetSiteInfo;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnPaint;// TEsCustomControl
    property OnPainting;// TEsCustomControl
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;

  TEsPanel = class(TEsLayout)
  private
    FFrameWidth: TFrameWidth;
    FFrameColor: TColor;
    FFrameStyle: TFrameStyle;
    procedure SetFrameColor(const Value: TColor);
    procedure SetFrameStyle(const Value: TFrameStyle);
    procedure SetFrameWidth(const Value: TFrameWidth);
  protected
    procedure Paint; override;
    procedure AdjustClientRect(var Rect: TRect); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property BevelKind;
    property BevelInner;
    property BevelOuter;
    property FrameStyle: TFrameStyle read FFrameStyle write SetFrameStyle default TExFrameStyle.Raised;
    property FrameColor: TColor read FFrameColor write SetFrameColor default clBtnShadow;
    property FrameWidth: TFrameWidth read FFrameWidth write SetFrameWidth default 1;
  end;

implementation

uses
  ES.ExGraphics, ES.Utils, Vcl.Themes;

procedure TEsCustomLayout.CMIsToolControl(var Message: TMessage);
begin
  if not FLocked then Message.Result := 1;
end;

constructor TEsCustomLayout.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
    csSetCaption, csParentBackground, csDoubleClicks, csReplicatable, csPannable, csGestures];
  Width := 185;
  Height := 41;
  UseDockManager := True;
end;

procedure TEsCustomLayout.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  // nope now
end;

{ TEsPanel }

procedure TEsPanel.AdjustClientRect(var Rect: TRect);
begin
  inherited;
  if FrameStyle <> TExFrameStyle.None then
  begin
    Rect.Inflate(-GetFrameWidth(FrameStyle, FrameWidth), -GetFrameWidth(FrameStyle, FrameWidth));
  end;
end;

constructor TEsPanel.Create(AOwner: TComponent);
begin
  inherited;

  FFrameColor := clBtnShadow;
  FFrameWidth := 1;
  FFrameStyle := TExFrameStyle.Raised;
end;

procedure TEsPanel.Paint;
begin
  if (csDesigning in ComponentState) and IsDrawHelper then
    DrawControlHelper(Self, [hoPadding, hoClientRect], GetFrameWidth(FrameStyle, FrameWidth));

  if FrameStyle <> TExFrameStyle.None then
    if IsStyledBorderControl(Self) then
      DrawFrame(Canvas, ClientRect, FrameStyle, FrameWidth, StyleServices.GetSystemColor(FrameColor),
        StyleServices.GetSystemColor(clBtnHighlight), StyleServices.GetSystemColor(clBtnShadow))
    else
      DrawFrame(Canvas, ClientRect, FrameStyle, FrameWidth, FrameColor, clBtnHighlight, clBtnShadow);

end;

procedure TEsPanel.SetFrameColor(const Value: TColor);
begin
  if FFrameColor <> Value then
  begin
    FFrameColor := Value;
    Invalidate;
  end;
end;

procedure TEsPanel.SetFrameStyle(const Value: TFrameStyle);
begin
  if FFrameStyle <> Value then
  begin
    FFrameStyle := Value;
    Realign;
    Invalidate;
  end;
end;

procedure TEsPanel.SetFrameWidth(const Value: TFrameWidth);
begin
  if FFrameWidth <> Value then
  begin
    FFrameWidth := Value;
    Realign;
    Invalidate;
  end;
end;

end.

Исходный же код модуля содержащего TEsCustomControl и его версии-LayoutTEsBaseLayout доступен по ссылке:
https://github.com/errorcalc/FreeEsVclComponents/blob/master/Source/ES.BaseControls.pas


И под спойлером
{******************************************************************************}
{                       EsVclComponents/EsVclCore v3.0                         }
{                           errorsoft(c) 2009-2018                             }
{                                                                              }
{                     More beautiful things: errorsoft.org                     }
{                                                                              }
{           errorsoft@mail.ru | vk.com/errorsoft | github.com/errorcalc        }
{              errorsoft@protonmail.ch | habrahabr.ru/user/error1024           }
{                                                                              }
{         Open this on github: github.com/errorcalc/FreeEsVclComponents        }
{                                                                              }
{ You can order developing vcl/fmx components, please submit requests to mail. }
{ �� ������ �������� ���������� VCL/FMX ���������� �� �����.                   }
{******************************************************************************}

{
  This is the base unit, which must remain Delphi 7 support, and it should not
  be dependent on any other units!
}

unit ES.BaseControls;

{$IF CompilerVersion >= 18} {$DEFINE VER180UP} {$IFEND}
{$IF CompilerVersion >= 21} {$DEFINE VER210UP} {$IFEND}
{$IF CompilerVersion >= 23} {$DEFINE VER230UP} {$IFEND}
{$IF CompilerVersion >= 24} {$DEFINE VER240UP} {$IFEND}

// see function CalcClientRect
{$define FAST_CALC_CLIENTRECT}

// see TEsBaseLayout.ContentRect
{$define TEST_CONTROL_CONTENT_RECT}

interface

uses
  WinApi.Windows, System.Types, System.Classes, Vcl.Controls,
  Vcl.Graphics, {$IFDEF VER230UP}Vcl.Themes,{$ENDIF} WinApi.Messages, WinApi.Uxtheme, Vcl.Forms;

const
  CM_ESBASE = CM_BASE + $0800;
  CM_PARENT_BUFFEREDCHILDRENS_CHANGED = CM_ESBASE + 1;

  EsVclCoreVersion = 3.0;

type
  THelperOption = (hoPadding, hoBorder, hoClientRect);
  THelperOptions = set of THelperOption;

  TPaintEvent = procedure(Sender: TObject; Canvas: TCanvas; Rect: TRect) of object;

  /// <summary> The best replacement for TCustomControl, supports transparency and without flicker </summary>
  TEsCustomControl = class(TWinControl)
  private
    // anti flicker and transparent magic
    FCanvas: TCanvas;
    CacheBitmap: HBITMAP;// Cache for buffer BitMap
    CacheBackground: HBITMAP;// Cache for background BitMap
    FIsCachedBuffer: Boolean;
    FIsCachedBackground: Boolean;
    FBufferedChildren: Boolean;
    FParentBufferedChildren: Boolean;
    FIsFullSizeBuffer: Boolean;
    // paint events
    FOnPaint: TPaintEvent;
    FOnPainting: TPaintEvent;
    // draw helper
    FIsDrawHelper: Boolean;
    // paint
    procedure SetIsCachedBuffer(Value: Boolean);
    procedure SetIsCachedBackground(Value: Boolean);
    procedure SetIsDrawHelper(const Value: Boolean);
    procedure SetIsOpaque(const Value: Boolean);
    function GetIsOpaque: Boolean;
    procedure SetBufferedChildren(const Value: Boolean);
    procedure SetParentBufferedChildren(const Value: Boolean);
    function GetTransparent: Boolean;
    procedure SetTransparent(const Value: Boolean);
    function IsBufferedChildrenStored: Boolean;
    // handle messages
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
    procedure WMWindowPosChanged(var Message: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
    procedure CMParentBufferedChildrensChanged(var Message: TMessage); message CM_PARENT_BUFFEREDCHILDRENS_CHANGED;
    procedure DrawBackgroundForOpaqueControls(DC: HDC);
    // intercept mouse
    // procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
    // other
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
    procedure WMTextChanges(var Message: TMessage); message WM_SETTEXT;
    // fix
    procedure FixBufferedChildren(Reader: TReader);
    procedure FixParentBufferedChildren(Reader: TReader);
    procedure SetIsFullSizeBuffer(const Value: Boolean);
  protected
    // fix
    procedure DefineProperties(Filer: TFiler); override;
    // paint
    property Canvas: TCanvas read FCanvas;
    procedure DeleteCache;{$IFDEF VER210UP}inline;{$ENDIF}
    procedure Paint; virtual;
    procedure PaintWindow(DC: HDC); override;
    procedure PaintHandler(var Message: TWMPaint);
    procedure DrawBackground(DC: HDC); virtual;
    // other
    procedure UpdateText; dynamic;
    //
    property ParentBackground default True;
    property Transparent: Boolean read GetTransparent write SetTransparent default True;// analog of ParentBackground
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure UpdateBackground(Repaint: Boolean); overload;
    procedure UpdateBackground; overload;
    // ------------------ Properties for published -------------------------------------------------
    property DoubleBuffered default False;
    {$IFDEF VER210UP}
    property ParentDoubleBuffered default False;
    {$ENDIF}
    // Painting for chidrens classes
    property OnPaint: TPaintEvent read FOnPaint write FOnPaint;
    property OnPainting: TPaintEvent read FOnPainting write FOnPainting;
    // BufferedChildrens
    property ParentBufferedChildren: Boolean read FParentBufferedChildren write SetParentBufferedChildren default True;
    property BufferedChildren: Boolean read FBufferedChildren write SetBufferedChildren stored IsBufferedChildrenStored;
    // External prop
    property IsCachedBuffer: Boolean read FIsCachedBuffer write SetIsCachedBuffer default False;
    property IsCachedBackground: Boolean read FIsCachedBackground write SetIsCachedBackground default False;
    property IsDrawHelper: Boolean read FIsDrawHelper write SetIsDrawHelper default False;
    property IsOpaque: Boolean read GetIsOpaque write SetIsOpaque default False;
    property IsFullSizeBuffer: Boolean read FIsFullSizeBuffer write SetIsFullSizeBuffer default False;
  end;

  {$IFDEF VER180UP}
  TContentMargins = record
  type
    TMarginSize = 0..MaxInt;
  private
    Left: TMarginSize;
    Top: TMarginSize;
    Right: TMarginSize;
    Bottom: TMarginSize;
  public
    function Width: TMarginSize;
    function Height: TMarginSize;
    procedure Inflate(DX, DY: Integer); overload;
    procedure Inflate(DLeft, DTop, DRight, DBottom: Integer); overload;
    procedure Reset;
    constructor Create(Left, Top, Right, Bottom: TMarginSize); overload;
  end;

  /// <summary> ONLY INTERNAL USE! THIS CLASS CAN BE DELETED! (USE TEsCustomControl OR TEsCustomLayot) </summary>
  TEsBaseLayout = class(TEsCustomControl)
  private
    FBorderWidth: TBorderWidth;
    procedure SetBorderWidth(const Value: TBorderWidth);
  protected
    procedure AlignControls(AControl: TControl; var Rect: TRect); override;
    procedure AdjustClientRect(var Rect: TRect); override;
    procedure Paint; override;
    // new
    procedure CalcContentMargins(var Margins: TContentMargins); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    function ContentRect: TRect; virtual;
    function ContentMargins: TContentMargins; inline;
    property BorderWidth: TBorderWidth read FBorderWidth write SetBorderWidth default 0;
    property BufferedChildren default True;
  end;

  /// <summary> The GraphicControl, supports Padding and IsDrawHelper property </summary>
  TEsGraphicControl = class(TGraphicControl)
  private
    FPadding: TPadding;
    FIsDrawHelper: Boolean;
    function GetPadding: TPadding;
    procedure SetPadding(const Value: TPadding);
    procedure PaddingChange(Sender: TObject);
    procedure SetIsDrawHelper(const Value: Boolean);
  protected
    procedure Paint; override;
    function HasPadding: Boolean;
    // new
    procedure CalcContentMargins(var Margins: TContentMargins); virtual;
  public
    destructor Destroy; override;
    property Padding: TPadding read GetPadding write SetPadding;
    function ContentRect: TRect; virtual;
    function ContentMargins: TContentMargins; inline;
    property IsDrawHelper: Boolean read FIsDrawHelper write SetIsDrawHelper default False;
  end;

  procedure DrawControlHelper(Control: TControl; Options: THelperOptions; FrameWidth: Integer = 0); overload;
  procedure DrawControlHelper(Canvas: TCanvas; Rect: TRect; BorderWidth: TBorderWidth;
    Padding: TPadding; Options: THelperOptions); overload;
  {$ENDIF}

  function CalcClientRect(Control: TControl): TRect;

  procedure DrawParentImage(Control: TControl; DC: HDC; InvalidateParent: Boolean = False);

implementation

uses
  System.SysUtils, System.TypInfo;

type
  TOpenCtrl = class(TWinControl)
  public
    property BorderWidth;
  end;

// Old delphi support
{$IFNDEF VER210UP}
function RectWidth(const Rect: TRect): Integer;
begin
  Result := Rect.Right - Rect.Left;
end;

function RectHeight(const Rect: TRect): Integer;
begin
  Result := Rect.Bottom - Rect.Top;
end;
{$ENDIF}

{$IFDEF VER210UP} {$REGION 'DrawControlHelper'}
procedure DrawControlHelper(Canvas: TCanvas; Rect: TRect; BorderWidth: TBorderWidth;
  Padding: TPadding; Options: THelperOptions);
  procedure Line(Canvas: TCanvas; x1, y1, x2, y2: Integer);
  begin
    Canvas.MoveTo(x1, y1);
    Canvas.LineTo(x2, y2);
  end;
var
  SaveBk: TColor;
  SavePen, SaveBrush: TPersistent;
begin
  SavePen := nil;
  SaveBrush := nil;

  try
    if Canvas.Handle = 0 then
      Exit;

    // save canvas state
    SavePen := TPen.Create;
    SavePen.Assign(Canvas.Pen);
    SaveBrush := TBrush.Create;
    SaveBrush.Assign(Canvas.Brush);

    Canvas.Pen.Mode := pmNot;
    Canvas.Pen.Style := psDash;
    Canvas.Brush.Style := bsClear;

    // ClientRect Helper
    if THelperOption.hoClientRect in Options then
    begin
      SaveBk := SetBkColor(Canvas.Handle, RGB(127,255,255));
      DrawFocusRect(Canvas.Handle, Rect);
      SetBkColor(Canvas.Handle, SaveBk);
    end;

    // Border Helper
    if THelperOption.hoBorder in Options then
    begin
      if (BorderWidth <> 0) and (BorderWidth * 2 <= RectWidth(Rect)) and (BorderWidth * 2 <= RectHeight(Rect)) then
        Canvas.Rectangle(Rect.Left + BorderWidth, Rect.Top + BorderWidth,
          Rect.Right - BorderWidth, Rect.Bottom - BorderWidth);
    end;

    // Padding Helper
    if THelperOption.hoPadding in Options then
    begin
      if (BorderWidth + Padding.Top < RectHeight(Rect) - BorderWidth - Padding.Bottom) and
         (BorderWidth + Padding.Left < RectWidth(Rect) - BorderWidth - Padding.Right) then
      begin
        Canvas.Pen.Style := psDot;

        if Padding.Left <> 0 then
          Line(Canvas, Rect.Left + Padding.Left + BorderWidth, Rect.Top + Padding.Top + BorderWidth,
            Rect.Left + Padding.Left + BorderWidth, Rect.Bottom - Padding.Bottom - BorderWidth - 1);
        if Padding.Top <> 0 then
          Line(Canvas, Rect.Left + Padding.Left + BorderWidth, Rect.Top + Padding.Top + BorderWidth,
            Rect.Right - Padding.Right - BorderWidth - 1, Rect.Top + Padding.Top + BorderWidth);
        if Padding.Right <> 0 then
          Line(Canvas, Rect.Right - Padding.Right - BorderWidth - 1, Rect.Top + Padding.Top + BorderWidth,
            Rect.Right - Padding.Right - BorderWidth - 1, Rect.Bottom - Padding.Bottom - BorderWidth - 1);
        if Padding.Bottom <> 0 then
          Line(Canvas, Rect.Left + Padding.Left + BorderWidth, Rect.Bottom - Padding.Bottom - BorderWidth - 1,
            Rect.Right - Padding.Right - BorderWidth - 1, Rect.Bottom - Padding.Bottom - BorderWidth - 1);
      end;
    end;

    Canvas.Pen.Assign(SavePen);
    Canvas.Brush.Assign(SaveBrush);
  finally
    SavePen.Free;
    SaveBrush.Free;
  end;
end;

procedure DrawControlHelper(Control: TControl; Options: THelperOptions; FrameWidth: Integer = 0);
var
  Canvas: TCanvas;
  Padding: TPadding;
  BorderWidth: Integer;
  MyCanvas: Boolean;
  R: TRect;
begin
  MyCanvas := False;
  Canvas := nil;
  Padding := nil;
  BorderWidth := 0;

  // if win control
  if Control is TWinControl then
  begin
    TOpenCtrl(Control).AdjustClientRect(R);

    // get padding
    Padding := TWinControl(Control).Padding;
    // get canvas
    if Control is TEsCustomControl then
      Canvas := TEsCustomControl(Control).Canvas
    else
    begin
      MyCanvas := True;
      Canvas := TControlCanvas.Create;
      TControlCanvas(Canvas).Control := Control;
    end;
    // get border width
    if Control is TEsBaseLayout then
      BorderWidth := TEsBaseLayout(Control).BorderWidth
    else
      BorderWidth := TOpenCtrl(Control).BorderWidth;
  end else
  if Control is TGraphicControl then
  begin
    // get canvas
    Canvas := TEsGraphicControl(Control).Canvas;
    if Control is TEsGraphicControl then
      Padding := TEsGraphicControl(Control).Padding;
  end;

  try
    R := Control.ClientRect;
    R.Inflate(-FrameWidth, -FrameWidth);
    DrawControlHelper(Canvas, R, BorderWidth, Padding, Options);
  finally
    if MyCanvas then
      Canvas.Free;
  end;
end;
{$ENDREGION} {$ENDIF}

function IsStyledClientControl(Control: TControl): Boolean;
begin
  Result := False;

  {$IFDEF VER230UP}
  if Control = nil then
    Exit;

  if StyleServices.Enabled then
  begin
    Result := {$ifdef VER240UP}(seClient in Control.StyleElements) and{$endif}
      TStyleManager.IsCustomStyleActive;
  end;
  {$ENDIF}
end;

function CalcClientRect(Control: TControl): TRect;
var
  {$ifdef FAST_CALC_CLIENTRECT}
  Info: TWindowInfo;
  {$endif}
  IsFast: Boolean;
begin
  {$ifdef FAST_CALC_CLIENTRECT}
  IsFast := True;
  {$else}
  IsFast := False;
  {$endif}

  Result := Rect(0, 0, Control.Width, Control.Height);

  // Only TWinControl's has non client area
  if not (Control is TWinControl) then
    Exit;

  // Fast method not work for controls not having Handle
  if not TWinControl(Control).Handle <> 0 then
    IsFast := False;

  if IsFast then
  begin
    ZeroMemory(@Info, SizeOf(TWindowInfo));
    Info.cbSize := SizeOf(TWindowInfo);
    GetWindowInfo(TWinControl(Control).Handle, info);
    Result.Left := Info.rcClient.Left - Info.rcWindow.Left;
    Result.Top := Info.rcClient.Top - Info.rcWindow.Top;
    Result.Right := -Info.rcWindow.Left + Info.rcClient.Right;
    Result.Top := -Info.rcWindow.Top + Info.rcClient.Bottom;
  end else
  begin
    Control.Perform(WM_NCCALCSIZE, 0, LParam(@Result));
  end;
end;

procedure DrawParentImage(Control: TControl; DC: HDC; InvalidateParent: Boolean = False);
var
  ClientRect: TRect;
  P: TPoint;
  SaveIndex: Integer;
begin
  if Control.Parent = nil then
    Exit;
  SaveIndex := SaveDC(DC);
  GetViewportOrgEx(DC, P);

  // if control has non client border then need additional offset viewport
  ClientRect := Control.ClientRect;
  if (ClientRect.Right <> Control.Width) or (ClientRect.Bottom <> Control.Height) then
  begin
    ClientRect := CalcClientRect(Control);
    SetViewportOrgEx(DC, P.X - Control.Left - ClientRect.Left, P.Y - Control.Top - ClientRect.Top, nil);
  end else
    SetViewportOrgEx(DC, P.X - Control.Left, P.Y - Control.Top, nil);

  IntersectClipRect(DC, 0, 0, Control.Parent.ClientWidth, Control.Parent.ClientHeight);

  Control.Parent.Perform(WM_ERASEBKGND, DC, 0);
  Control.Parent.Perform(WM_PRINTCLIENT, DC, PRF_CLIENT);

  RestoreDC(DC, SaveIndex);

  if InvalidateParent then
    if not (Control.Parent is TCustomControl) and not (Control.Parent is TCustomForm) and
       not (csDesigning in Control.ComponentState)and not (Control.Parent is TEsCustomControl) then
    begin
      Control.Parent.Invalidate;
    end;

  SetViewportOrgEx(DC, P.X, P.Y, nil);
end;

procedure BitmapDeleteAndNil(var Bitmap: HBITMAP);{$IFDEF VER210UP}inline;{$ENDIF}
begin
  if Bitmap <> 0 then
  begin
    DeleteObject(Bitmap);
    Bitmap := 0;
  end;
end;

procedure TEsCustomControl.CMParentBufferedChildrensChanged(var Message: TMessage);
begin
  if FParentBufferedChildren then
  begin
    if Parent <> nil then
    begin
      if Parent is TEsCustomControl then
        BufferedChildren := TEsCustomControl(Parent).BufferedChildren
      else
        BufferedChildren := False;
    end;
    FParentBufferedChildren := True;
  end;
end;

procedure TEsCustomControl.CMTextChanged(var Message: TMessage);
begin
  inherited;
  UpdateText;
end;

procedure TEsCustomControl.WMTextChanges(var Message: TMessage);
begin
  Inherited;
  UpdateText;
end;

constructor TEsCustomControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  // init
  ControlStyle := ControlStyle - [csOpaque] + [csParentBackground];
  {$IFDEF VER210UP}
  ParentDoubleBuffered := False;
  {$ENDIF}

  CacheBitmap := 0;
  CacheBackground := 0;

  // canvas
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;

  // new props
  FParentBufferedChildren := True;
  FBufferedChildren := False;
  FIsCachedBuffer := False;
  FIsCachedBackground := False;
  FIsFullSizeBuffer := False;
  FIsDrawHelper := False;
end;

// temp fix
procedure TEsCustomControl.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('BufferedChildrens', FixBufferedChildren, nil, False);
  Filer.DefineProperty('ParentBufferedChildrens', FixParentBufferedChildren, nil, False);
end;

// ok
procedure TEsCustomControl.DeleteCache;
begin
  BitmapDeleteAndNil(CacheBitmap);
  BitmapDeleteAndNil(CacheBackground);
end;

destructor TEsCustomControl.Destroy;
begin
  FCanvas.Free;
  DeleteCache;
  inherited;
end;

procedure TEsCustomControl.DrawBackground(DC: HDC);
begin
  DrawParentImage(Self, DC, False);
end;

// hack for bad graphic controls
procedure TEsCustomControl.DrawBackgroundForOpaqueControls(DC: HDC);
var
  i: integer;
  Control: TControl;
  Prop: Pointer;
begin
  for i := 0 to ControlCount - 1 do
  begin
    Control := Controls[i];
    if (Control is TGraphicControl) and (csOpaque in Control.ControlStyle) and Control.Visible and
       (not (csDesigning in ComponentState) or not (csNoDesignVisible in ControlStyle)
       {$IFDEF VER210UP}or not (csDesignerHide in Control.ControlState){$ENDIF})
    then
    begin
      // Necessary to draw a background if the control has a Property 'Transparent' and hasn't a Property 'Color'
      Prop := GetPropInfo(Control.ClassInfo, 'Transparent');
      if Prop <> nil then
      begin
        Prop := GetPropInfo(Control.ClassInfo, 'Color');
        if Prop = nil then
          FillRect(DC, Rect(Control.Left, Control.Top, Control.Left + Control.Width, Control.Top + Control.Height), Brush.Handle);
      end;
    end;
  end;
end;

(*procedure TEsCustomControl.EndCachedBackground;
begin
  FIsCachedBackground := StoredCachedBackground;
end;

procedure TEsCustomControl.EndCachedBuffer;
begin
  FIsCachedBuffer := StoredCachedBuffer;
end;*)

// temp fix
procedure TEsCustomControl.FixBufferedChildren(Reader: TReader);
begin
  BufferedChildren := Reader.ReadBoolean;
end;

// temp fix
procedure TEsCustomControl.FixParentBufferedChildren(Reader: TReader);
begin
  ParentBufferedChildren := Reader.ReadBoolean;
end;

function TEsCustomControl.GetIsOpaque: Boolean;
begin
  Result := csOpaque in ControlStyle;
end;

function TEsCustomControl.GetTransparent: Boolean;
begin
  Result := ParentBackground;
end;

procedure TEsCustomControl.Paint;
var
  SaveBk: TColor;
begin
  // for Design time
  if IsDrawHelper and(csDesigning in ComponentState) then
  begin
    SaveBk := SetBkColor(Canvas.Handle, RGB(127,255,255));
    DrawFocusRect(Canvas.Handle, Self.ClientRect);
    SetBkColor(Canvas.Handle, SaveBk);
  end;
end;

{ TODO -cCRITICAL : 22.02.2013:
  eliminate duplication of code! }
procedure TEsCustomControl.PaintHandler(var Message: TWMPaint);
var
  PS: TPaintStruct;
  BufferDC: HDC;
  BufferBitMap: HBITMAP;
  UpdateRect: TRect;
  SaveViewport: TPoint;
  Region: HRGN;
  DC: HDC;
  IsBeginPaint: Boolean;
begin
  BufferBitMap := 0;
  BufferDC := 0;
  DC := 0;
  Region := 0;
  IsBeginPaint := Message.DC = 0;

  try
    if IsBeginPaint then
    begin
      DC := BeginPaint(Handle, PS);
      {$IFDEF VER230UP}
      if TStyleManager.IsCustomStyleActive and not FIsCachedBuffer then
        UpdateRect := ClientRect
        // I had to use a crutch to ClientRect, due to the fact that
        // VCL.Styles.TCustomStyle.DoDrawParentBackground NOT use relative coordinates,
        // ie ignores SetViewportOrgEx!
        // This function uses ClientToScreen and ScreenToClient for coordinates calculation!
      else
      {$endif}
        UpdateRect := PS.rcPaint;
    end
    else
    begin
      DC := Message.DC;
      {$IFDEF VER230UP}
      if TStyleManager.IsCustomStyleActive and not FIsCachedBuffer then
        UpdateRect := ClientRect
      else
      {$endif}
        if GetClipBox(DC, UpdateRect) = ERROR then
          UpdateRect := ClientRect;
    end;

    //------------------------------------------------------------------------------------------------
    // Duplicate code, see PaintWindow, Please sync this code!!!
    //------------------------------------------------------------------------------------------------
    // if control not double buffered then create or assign buffer
    if not DoubleBuffered then
    begin
      BufferDC := CreateCompatibleDC(DC);
      // CreateCompatibleDC(DC) return 0 if Drawing takes place to MemDC(buffer):
      // return <> 0 => need to double buffer || return = 0 => no need to double buffer
      if BufferDC <> 0 then
      begin
        // Using the cache if possible
        if FIsCachedBuffer or FIsFullSizeBuffer then
        begin
          // Create cache if need
          if CacheBitmap = 0 then
          begin
            BufferBitMap := CreateCompatibleBitmap(DC, ClientWidth, ClientHeight);
            // Assign to cache if need
            if FIsCachedBuffer then
              CacheBitmap := BufferBitMap;
          end
          else
            BufferBitMap := CacheBitmap;

          // Assign region for minimal overdraw
          Region := CreateRectRgnIndirect(UpdateRect);//0, 0, UpdateRect.Width, UpdateRect.Height);
          SelectClipRgn(BufferDC, Region);
        end
        else
          // Create buffer
          BufferBitMap := CreateCompatibleBitmap(DC,
            UpdateRect.Right - UpdateRect.Left, UpdateRect.Bottom - UpdateRect.Top);
        // Select buffer bitmap
        SelectObject(BufferDC, BufferBitMap);
        // [change coord], if need
        // Moving update region to the (0,0) point
        if not(FIsCachedBuffer or FIsFullSizeBuffer) then
        begin
          GetViewportOrgEx(BufferDC, SaveViewport);
          SetViewportOrgEx(BufferDC, -UpdateRect.Left + SaveViewport.X, -UpdateRect.Top + SaveViewport.Y, nil);
        end;
      end
      else
        BufferDC := DC;
    end
    else
      BufferDC := DC;
    //------------------------------------------------------------------------------------------------

    // DEFAULT HANDLER:
    Message.DC := BufferDC;
    inherited PaintHandler(Message);

  finally
    try
      //------------------------------------------------------------------------------------------------
      // Duplicate code, see PaintWindow, Please sync this code!!!
      //------------------------------------------------------------------------------------------------
      try
        // draw to window
        if not DoubleBuffered then
        begin
          if not(FIsCachedBuffer or FIsFullSizeBuffer) then
          begin
            // [restore coord], if need
            SetViewportOrgEx(BufferDC, SaveViewport.X, SaveViewport.Y, nil);
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC, 0, 0, SRCCOPY);
          end
          else
          begin
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY);
          end;
        end;
      finally
        if BufferDC <> DC then
          DeleteObject(BufferDC);
        if Region <> 0 then
          DeleteObject(Region);
        // delete buffer, if need
        if not FIsCachedBuffer and (BufferBitMap <> 0) then
          DeleteObject(BufferBitMap);
      end;
      //------------------------------------------------------------------------------------------------
    finally
      // end paint, if need
      if IsBeginPaint then
        EndPaint(Handle, PS);
    end;
  end;
end;

{ TODO -cMAJOR : 22.02.2013:
 See: PaintHandler,
 need eliminate duplication of code! }
procedure TEsCustomControl.PaintWindow(DC: HDC);
var
  TempDC: HDC;
  UpdateRect: TRect;
  //---
  BufferDC: HDC;
  BufferBitMap: HBITMAP;
  Region: HRGN;
  SaveViewport: TPoint;
  BufferedThis: Boolean;
begin
  BufferBitMap := 0;
  Region := 0;
  BufferDC := 0;

  if GetClipBox(DC, UpdateRect) = ERROR then
    UpdateRect := ClientRect;

  BufferedThis := not BufferedChildren;

  // fix for designer selection
  BufferedThis := BufferedThis or (csDesigning in ComponentState);

  try
    if BufferedThis then
    begin
    //------------------------------------------------------------------------------------------------
    // Duplicate code, see PaintHandler, Please sync this code!!!
    //------------------------------------------------------------------------------------------------
      // if control not double buffered then create or assign buffer
      if not DoubleBuffered then
      begin
        BufferDC := CreateCompatibleDC(DC);
        // CreateCompatibleDC(DC) return 0 if Drawing takes place to MemDC(buffer):
        // return <> 0 => need to double buffer || return = 0 => no need to double buffer
        if BufferDC <> 0 then
        begin
          // Using the cache if possible
          if FIsCachedBuffer or FIsFullSizeBuffer then
          begin
            // Create cache if need
            if CacheBitmap = 0 then
            begin
              BufferBitMap := CreateCompatibleBitmap(DC, ClientWidth, ClientHeight);
              // Assign to cache if need
              if FIsCachedBuffer then
                CacheBitmap := BufferBitMap;
            end
            else
              BufferBitMap := CacheBitmap;

            // Assign region for minimal overdraw
            Region := CreateRectRgnIndirect(UpdateRect);//0, 0, UpdateRect.Width, UpdateRect.Height);
            SelectClipRgn(BufferDC, Region);
          end
          else
            // Create buffer
            BufferBitMap := CreateCompatibleBitmap(DC, RectWidth(UpdateRect), RectHeight(UpdateRect));
          // Select buffer bitmap
          SelectObject(BufferDC, BufferBitMap);
          // [change coord], if need
          // Moving update region to the (0,0) point
          if not(FIsCachedBuffer or FIsFullSizeBuffer) then
          begin
            GetViewportOrgEx(BufferDC, SaveViewport);
            SetViewportOrgEx(BufferDC, -UpdateRect.Left + SaveViewport.X, -UpdateRect.Top + SaveViewport.Y, nil);
          end;
        end
        else
          BufferDC := DC;
      end
      else
        BufferDC := DC;
    //------------------------------------------------------------------------------------------------
    end else
      BufferDC := DC;

    if not(csOpaque in ControlStyle) then
      if ParentBackground then
      begin
        if FIsCachedBackground then
        begin
          if CacheBackground = 0 then
          begin
            TempDC := CreateCompatibleDC(DC);
            CacheBackground := CreateCompatibleBitmap(DC, ClientWidth, ClientHeight);
            SelectObject(TempDC, CacheBackground);
            DrawBackground(TempDC); //DrawParentImage(Self, TempDC, False);
            DeleteDC(TempDC);
          end;
          TempDC := CreateCompatibleDC(BufferDC);
          SelectObject(TempDC, CacheBackground);
          if not FIsCachedBuffer then
            BitBlt(BufferDC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), TempDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY)
          else
            BitBlt(BufferDC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), TempDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY);
          DeleteDC(TempDC);
        end
        else
          DrawBackground(BufferDC); //DrawParentImage(Self, BufferDC, False);
      end else
        if (not DoubleBuffered or (DC <> 0)) then
          if not IsStyledClientControl(Self) then
            FillRect(BufferDC, ClientRect, Brush.Handle)
          else
          begin
            SetDCBrushColor(BufferDC,
              ColorToRGB({$ifdef VER230UP}StyleServices.GetSystemColor(Color){$else}Color{$endif}));
            FillRect(BufferDC, ClientRect, GetStockObject(DC_BRUSH));
          end;

    FCanvas.Lock;
    try
      Canvas.Handle := BufferDC;
      TControlCanvas(Canvas).UpdateTextFlags;

      if Assigned(FOnPainting) then
        FOnPainting(Self, Canvas, ClientRect);
      Paint;
      if Assigned(FOnPaint) then
        FOnPaint(Self, Canvas, ClientRect);
    finally
      FCanvas.Handle := 0;
      FCanvas.Unlock;
    end;

  finally
    if BufferedThis then
    begin
      //------------------------------------------------------------------------------------------------
      // Duplicate code, see PaintHandler, Please sync this code!!!
      //------------------------------------------------------------------------------------------------
      try
        // draw to window
        if not DoubleBuffered then
        begin
          if not(FIsCachedBuffer or FIsFullSizeBuffer) then
          begin
            // [restore coord], if need
            SetViewportOrgEx(BufferDC, SaveViewport.X, SaveViewport.Y, nil);
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC, 0, 0, SRCCOPY);
          end
          else
          begin
            BitBlt(DC, UpdateRect.Left, UpdateRect.Top, RectWidth(UpdateRect), RectHeight(UpdateRect), BufferDC,
              UpdateRect.Left, UpdateRect.Top, SRCCOPY);
          end;
        end;
      finally
        if BufferDC <> DC then
          DeleteObject(BufferDC);
        if Region <> 0 then
          DeleteObject(Region);
        // delete buffer, if need
        if not FIsCachedBuffer and (BufferBitMap <> 0) then
          DeleteObject(BufferBitMap);
      end;
      //------------------------------------------------------------------------------------------------
    end;
  end;
end;

// ok
function TEsCustomControl.IsBufferedChildrenStored: Boolean;
begin
  Result := not ParentBufferedChildren;
end;

// ok
procedure TEsCustomControl.SetBufferedChildren(const Value: Boolean);
begin
  if Value <> FBufferedChildren then
  begin
    FBufferedChildren := Value;
    FParentBufferedChildren := False;
    NotifyControls(CM_PARENT_BUFFEREDCHILDRENS_CHANGED);
  end;
end;

procedure TEsCustomControl.SetIsCachedBackground(Value: Boolean);
begin
  if Value <> FIsCachedBackground then
  begin
    FIsCachedBackground := Value;
    if not FIsCachedBackground then BitmapDeleteAndNil(CacheBackground);
  end;
end;

procedure TEsCustomControl.SetIsCachedBuffer(Value: Boolean);
begin
  if Value <> FIsCachedBuffer then
  begin
    FIsCachedBuffer := Value;
    if not FIsCachedBuffer then BitmapDeleteAndNil(CacheBitmap);
  end;
end;

procedure TEsCustomControl.SetIsDrawHelper(const Value: Boolean);
begin
  if Value <> FIsDrawHelper then
  begin
    FIsDrawHelper := Value;
    if csDesigning in ComponentState then
      Invalidate;
  end;
end;

procedure TEsCustomControl.SetIsFullSizeBuffer(const Value: Boolean);
begin
  DeleteCache;
end;

// ok
procedure TEsCustomControl.SetIsOpaque(const Value: Boolean);
begin
  if Value <> (csOpaque in ControlStyle) then
  begin
    if Value then
      ControlStyle := ControlStyle + [csOpaque]
    else
      ControlStyle := ControlStyle - [csOpaque];

    Invalidate;
  end;
end;

// ok
procedure TEsCustomControl.SetParentBufferedChildren(const Value: Boolean);
begin
  if Value <> FParentBufferedChildren then
  begin
    FParentBufferedChildren := Value;

    if (Parent <> nil) and not (csReading in ComponentState) then
      Perform(CM_PARENT_BUFFEREDCHILDRENS_CHANGED, 0, 0);
  end;
end;

procedure TEsCustomControl.SetTransparent(const Value: Boolean);
begin
  ParentBackground := Value;
end;

procedure TEsCustomControl.UpdateBackground;
begin
  UpdateBackground(True);
end;

procedure TEsCustomControl.UpdateText;
begin
end;

procedure TEsCustomControl.UpdateBackground(Repaint: Boolean);
begin
  // Delete cache background
  BitmapDeleteAndNil(CacheBackground);

  if Repaint then Invalidate;
end;

procedure TEsCustomControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if DoubleBuffered then
  begin
    inherited;
    // Message.Result := 1;
  end else
  begin
    if ControlCount <> 0 then
      DrawBackgroundForOpaqueControls(Message.DC);
    Message.Result := 1;
  end;
end;

//procedure TEsCustomControl.WMNCHitTest(var Message: TWMNCHitTest);
//begin
//  if (FIsTransparentMouse) and not(csDesigning in ComponentState) then
//    Message.Result := HTTRANSPARENT
//  else
//    inherited;
//end;

procedure TEsCustomControl.WMPaint(var Message: TWMPaint);
begin
  ControlState := ControlState + [csCustomPaint];

  // buffered childen aviable only for not DoubleBuffered controls
  if BufferedChildren and (not FDoubleBuffered) and
     not (csDesigning in ComponentState) then // fix for designer selection
  begin
    PaintHandler(Message)// My new PaintHandler
  end else
    inherited;

  ControlState := ControlState - [csCustomPaint];
end;

procedure TEsCustomControl.WMSize(var Message: TWMSize);
begin
  DeleteCache;
  inherited;
end;

procedure TEsCustomControl.WMWindowPosChanged(var Message: TWMWindowPosChanged);
begin
  if not (csOpaque in ControlStyle) and ParentBackground{ and not CachedBackground }then
    Invalidate;
  Inherited;
end;

{$IFDEF VER180UP}
{ TEsBaseLayout }

constructor TEsBaseLayout.Create(AOwner: TComponent);
begin
  inherited;

  FBufferedChildren := True;
end;

procedure TEsBaseLayout.AdjustClientRect(var Rect: TRect);
begin
  inherited AdjustClientRect(Rect);
  if BorderWidth <> 0 then
  begin
    InflateRect(Rect, -Integer(BorderWidth), -Integer(BorderWidth));
  end;
end;

procedure TEsBaseLayout.AlignControls(AControl: TControl; var Rect: TRect);
begin
  inherited AlignControls(AControl, Rect);
  if (csDesigning in ComponentState) and IsDrawHelper then
    Invalidate;
end;

procedure TEsBaseLayout.CalcContentMargins(var Margins: TContentMargins);
begin
  Margins.Create(Padding.Left, Padding.Top, Padding.Right, Padding.Bottom);
  if BorderWidth <> 0 then
    Margins.Inflate(Integer(BorderWidth), Integer(BorderWidth));
end;

function TEsBaseLayout.ContentMargins: TContentMargins;
begin
  Result.Reset;
  CalcContentMargins(Result);
end;

function TEsBaseLayout.ContentRect: TRect;
var
  ContentMargins: TContentMargins;
begin
  Result := ClientRect;

  ContentMargins.Reset;
  CalcContentMargins(ContentMargins);

  Inc(Result.Left, ContentMargins.Left);
  Inc(Result.Top, ContentMargins.Top);
  Dec(Result.Right, ContentMargins.Right);
  Dec(Result.Bottom, ContentMargins.Bottom);

  {$ifdef TEST_CONTROL_CONTENT_RECT}
  if Result.Left > Result.Right then
    Result.Right := Result.Left;
  if Result.Top > Result.Bottom then
    Result.Bottom := Result.Top;
  {$endif}
end;

procedure TEsBaseLayout.Paint;
begin
  if (csDesigning in ComponentState) and IsDrawHelper then
    DrawControlHelper(Self, [hoBorder, hoPadding, hoClientRect]);
end;

procedure TEsBaseLayout.SetBorderWidth(const Value: TBorderWidth);
begin
  if Value <> FBorderWidth then
  begin
    FBorderWidth := Value;
    Realign;
    Invalidate;
  end;
end;

{ TEsGraphicControl }

procedure TEsGraphicControl.CalcContentMargins(var Margins: TContentMargins);
begin
  if FPadding <> nil then
    Margins.Create(Padding.Left, Padding.Top, Padding.Right, Padding.Bottom)
  else
    Margins.Reset;
end;

function TEsGraphicControl.ContentMargins: TContentMargins;
begin
  Result.Reset;
  CalcContentMargins(Result);
end;

function TEsGraphicControl.ContentRect: TRect;
var
  ContentMargins: TContentMargins;
begin
  Result := ClientRect;

  ContentMargins.Reset;
  CalcContentMargins(ContentMargins);

  Inc(Result.Left, ContentMargins.Left);
  Inc(Result.Top, ContentMargins.Top);
  Dec(Result.Right, ContentMargins.Right);
  Dec(Result.Bottom, ContentMargins.Bottom);

  {$ifdef TEST_CONTROL_CONTENT_RECT}
  if Result.Left > Result.Right then
    Result.Right := Result.Left;
  if Result.Top > Result.Bottom then
    Result.Bottom := Result.Top;
  {$endif}
end;

destructor TEsGraphicControl.Destroy;
begin
  FPadding.Free;
  inherited;
end;

function TEsGraphicControl.GetPadding: TPadding;
begin
  if FPadding = nil then
  begin
    FPadding := TPadding.Create(nil);
    FPadding.OnChange := PaddingChange;
  end;
  Result := FPadding;
end;

function TEsGraphicControl.HasPadding: Boolean;
begin
  Result := FPadding <> nil;
end;

procedure TEsGraphicControl.PaddingChange(Sender: TObject);
begin
  AdjustSize;
  Invalidate;
  if (FPadding.Left = 0) and (FPadding.Top = 0) and (FPadding.Right = 0) and (FPadding.Bottom = 0) then
    FreeAndNil(FPadding);
end;

procedure TEsGraphicControl.Paint;
begin
  if (csDesigning in ComponentState) and IsDrawHelper then
    DrawControlHelper(Self, [hoPadding, hoClientRect]);
end;

procedure TEsGraphicControl.SetIsDrawHelper(const Value: Boolean);
begin
  if FIsDrawHelper <> Value then
  begin
      FIsDrawHelper := Value;
      if csDesigning in ComponentState then
        Invalidate;
  end;
end;

procedure TEsGraphicControl.SetPadding(const Value: TPadding);
begin
  Padding.Assign(Value);
end;

{ TContentMargins }

constructor TContentMargins.Create(Left, Top, Right, Bottom: TMarginSize);
begin
  Self.Left := Left;
  Self.Top := Top;
  Self.Right := Right;
  Self.Bottom := Bottom;
end;

procedure TContentMargins.Reset;
begin
  Left := 0;
  Top := 0;
  Right := 0;
  Bottom := 0;
end;

function TContentMargins.Height: TMarginSize;
begin
  Result := Top + Bottom;
end;

procedure TContentMargins.Inflate(DX, DY: Integer);
begin
  Inc(Left, DX);
  Inc(Right, DX);
  Inc(Top, DY);
  Inc(Bottom, DY);
end;

procedure TContentMargins.Inflate(DLeft, DTop, DRight, DBottom: Integer);
begin
  Inc(Left, DLeft);
  Inc(Right, DRight);
  Inc(Top, DTop);
  Inc(Bottom, DBottom);
end;

function TContentMargins.Width: TMarginSize;
begin
  Result := Left + Right;
end;
{$ENDIF}

end.

Но лучше использовать бесплатную библиотеку VCL компонентов EsVclComponents, которая содержит в себе данные модули и еще много полезных компонентов и классов:
https://github.com/errorcalc/FreeEsVclComponents (библиотека также доступна в пакетном менеджере GetIt для Delphi Berlin, правда не самая свежая версия).


Посмотрите примеры, особенно "\Samples\BufferedChildrens", где видно "магию" подавления мерцания.


Возможно стоит написать отдельную обзорную статью о данной библиотеке?


Спасибо что дочитали статью до конца! Надеюсь, я помог вам побороть проблему мерцания в ваших приложениях и компонентах.


Вы можете помочь проекту, написав мне, где вы используете данные компоненты и приложив скриншот с примером использования.

Поделиться публикацией

Комментарии 176

    –42
    >Delphi… C++Builder… VCL
    Бедняжка… За что тебя так? Ты хоть адрес напиши — мы тут тебе передачку соберём. Пару бутылок пива, сигареты, gcc, какую-то IDE…
      +25
      Минусанул тебе карму, ибо достали уже такие «знатоки».
        +2
        Согласен. Досих пор использую Delphi 7 которую купил лет еще 15 назад (как то даже диск на глаза попадался :) ). Не спорю это не основная среда разработки, но если надо реально за 30 минут накидать виндовое приложение и которое будет работать практически везде и кроме переноса exe-файла ничего не требует (BDE не использую :) ), то это идеальна вещь. Некоторые проги написанные 10 лет назад все еще работают у людей и они их меня не хотят.

        И чего душой кривить, вот 30 числа нужно было срочно написать конвертор для больших файлов для отправки в ЕГАИС… реально 20 минут и готово.
        +1

        Нам, дельфистам, некогда курить и пить пиво — работы много. И если честно, не видно внятных альтернатив для решения конкретного ряда задач.


        К примеру: система финансово-управленческого учёта специального назначения, Windows native (требование спорное, но оно есть), ничего лишнего, максимальная эргономика с защитой от дурака и зловреда, минимальное время реакции на изменяющиеся требования бизнеса, минимальные требования к инфраструктурному обеспечению (типа ничего, кроме sql сервера).


        Предложите альтернативу, я рассмотрю. Честно-честно. Давно хочу альтернативу.

          –5
          >Предложите альтернативу, я рассмотрю.
          Qt+cpp;
          Кроссплатформенность, поддержка современных компиляторов…
          Если бы автор написал «Lazarus», то я слова бы не сказал. Паскаль так паскаль. Но Дельфи…
          Я даже не удивлён такой негативной реакции на мою безобидную шутку — уверен, что над вами, дельфистами, смеются все и всегда. Вы уже, похоже, не в состоянии понимать шутки, а про самоиронию вы забыли ещё в 2000х.
            +3
            Qt+cpp;

            Это в лучшем случае шило на мыло. Ну т.е. если вы знаете C++, и вам нужна среда быстрой разработки толстых, но нативных приложений, то Qt для вас вполне годится. Но мигрировать на неё с Delphi, это просто головная боль, куча времени на поиск новых граблей, и ноль преимуществ в итоге.
              –8
              Гхм. А Дельфи нынче какой версии используется? Что его компилятор знает про современные процессоры?
              Ничего? Ну это же никому не нужно, правильно?
              Оптимизации? Какие оптимизации? Прогресс в этом направлении остановился в 2008, не так ли. (Нет, прогресс не остановился? Вы кормите мудаков из Эмбракадеро? Ну кто-то же должен кормить мудаков...)
              Qt слишком толстый? Что, есть ещё виндовые машины где лишние 8-9МиБ слишком чувствительны? Впрочем, сомневаюсь, что футпринт дельфи значительно меньше.
              Но даже если Qt слишком толстый, то есть GTK. Он куда более худой.

              P.S. Обиженки с головной болью слили карму, так что я буду отвечать довольно редко. (Кстати, что же нужно иметь в голове, чтобы затыкать человеку рот на целом сайте? Боль, вероятно… и похоже нифига не головную, а ближе к точке возгорания:) )
                +5
                Вы кормите мудаков из Эмбракадеро? Ну кто-то же должен кормить мудаков...)

                Извини, но ты мудак ;)
                На остальное отвечать тебе не вижу смысла.
                Поразительно то, что к каждой статье про Delphi найдется такой мудак, не способный пройти мимо. Благо теперь стали минусовать их.
                  +1
                  Не стоит опускаться до их уровня…
                    +1
                    Не вступайте с таким в диалог. Они безнадежны…
                    +3
                    Я тоже чего-то не понял, почему компилятор Delphi, по вашему мнению, не развивается? И вообще возникло сомнения, — а знаете ли вы что представляют собой современная версия Delphi?
                      +1
                      Убеждать апологетов — бесполезно
                        +2
                        В своё время, ко мне на стримы заходили толпы людей, и пытались убедить, что Delphi умер и стоит разрабатывать на других языках. При этом никто не мог обосновать свою логику. Некоторые уходили с пониманием, некоторые шли искать других, чтобы склонять в свою веру.
                          0
                          Вообще в этих спорах нет смысла. Решая поставленную задачу соревнуешься только с самим собой и предыдущей решенной тобой задачей. Только сделав вторую можно понять можно ли было решить первую лучше.
                      +3
                      Вы кормите мудаков из Эмбракадеро?

                      Так, чисто для справки: коммерческая лицензия на Qt стоит порядка $150/месяц.
                        –6
                        >коммерческая лицензия на Qt стоит
                        Вот только покупать её нужно в исключительнейших случаях. В основном тогда, когда необходимо внести правки в сам код Qt или когда кровь из носа нужна статическая линковка. Остальным хватает LGPL и динамической линковки за бесплатно.
                        Сравнение ещё менее корректно хотя бы уже потому, что в код Дельфи внести изменения вообще невозможно (В жизни не поверю что жлобо-рептилоиды из эмбракадеро открыли код).
                          +3
                          Вообще-то исходный код RTL, VCL, FMX входит в поставку.
                          • НЛО прилетело и опубликовало эту надпись здесь
                            +2
                            А Starter версия делфи нынче вообще бесплатна, да. И уже давно принадлежит конторе с назнваием IDERA. Тоже так, чисто для справки.
                              0
                              Бесплатны почти все образовательные лицензии…
                                0
                                До осени того года стартер был 300+ баксов. Я в своё время ХЕ5 покупал тысяч за 8 рублей…
                          0
                          На Qt будет дольше, с БД работать если и что-то кастомизировать, нужно написать модельки свои, делегатики. В Дельфи это займёт 5 минут мышекликаньем. RAD конечно лучше, чем в Дельфи никто не сделал.
                          +5
                          Шутка не должна выглядеть так, было неприятно получить «это» в качестве первого комментария вместо обоснованной критики под своей статьей, тебя заминусовали за это, и не только дельфисты.
                          А оправдываться что в случае лазаруса ты бы не оставил здесь свой «полезный» комментарий не стоит.
                            +3
                            Взгляд со стороны — это тупая шутка.
                              –6
                              Шутка отличная, но ты просто не можешь посмотреть со стороны.
                                +3
                                Об этом судить не автору шутки.
                              +1
                              1. Lazarus, к сожалению, не является полноценной альтернативой Delphi. Для добавления нового компонента в среду разработки необходима пересборка самой среды разработки, что отнимает немало времени и ломает то самое преимущество "склепал за несколько минут работающее приложение".
                              2. QT не подходит по другой причине: у него намного меньше автоматизации при написании кода. Все это мелочи, конечно, но разработку замедляет существенно, что и определяет все то же конкурентное преимущество Delphi — "быстро склепать приложение".

                              Единственным реальным конкурентом Delphi (в старом понимании сего бренда, сейчас это дело объединили под общим названием) я назвал бы CBuilder, который использует тот же способ быстрой разработки приложений. Но на C++ RAD технология (если правильно помню ее наименование) ложится из-за особенностей языка гораздо хуже.

                                0
                                С теплотой вспоминаю эти среды разработки.
                                Часто встречались на форумах претензии к качеству компилятора у билдера особенно на работу с ссылками. Но по скорости разработки, я в 90х писал и на Дельфи и на Билдере, разницы в принципе никакой не было, на билдере всё было так же быстро и приятно.
                                Жаль, что не сделали среду под Линукс нормальную. (Kylix) так и не взлетел, компилятор вроде не исправили, после перехода на ядро 2.4.18 — помнится, поменяли формат ELF файлов, а Борланду было уже не до него.
                                  0
                                  Линукс собираются на Делфи вновь возродить. Ждём. Лазарь, к слову, уже давно и успешно на линуксе работает и программы на нем пишутся.
                                  0
                                  Ещё была Watcom Optima. Кто не помнит, Watcom — это фирма которая делала оптимизирующий компилятор Watcom C а затем и C++. Многие игры, работавшие под DOS4GW компилировались Watcom-ом. В Википедии об этом продукте только одна строчка:
                                  Watcom was acquired by Powersoft in 1994, and Powersoft merged with Sybase in 1995.[2] In May 2000, Sybase spun off their mobile and embedded computing division into its own company, Sybase iAnywhere (formerly iAnywhere Solutions Inc.). Sybase tried to re-target the Watcom compiler into a visual RAD tool, Optima++, but in 2003, because the product competed directly with the Sybase offering PowerBuilder, the product was discontinued.

                                  Так вот, Optima++ выглядела примерно как первый 32-битный C++Builder, с различными удобными фичами. Причём поскольку она не несла груз совместимости с Win16 в неё не было VCL-подобного монстра, всё было проще, компактней и поддерживались все Win32-контролы и все их свойства. В общем, «Жаль что она умерла».
                                    0

                                    Имел возможность познакомиться с этим продуктом. Неплохая штука. Но рядом с CBuilder и Delphi оно и рядом не стояло в плане быстроты разработки.
                                    В RAD от Borland была достигнута самая высокая степень автоматизации создания приложения. И VCL — закономерная плата за это, иначе было просто невозможно столь просто связывать элементы управления друг с другом, создавать на их базе новые и простым образом подключать получившееся как элемент конструктора.
                                    Что-то отдаленно близкое по идеологии разработки приложений, пожалуй, в те старые добрые времена было только у IBM с их Visual Age C++, хотя и он не был столь удобен, как продукция от Borland.
                                    Даже современным конкурентам до Delphi и CBuilder далеко.
                                    Как правило у конкурентов все ограничивается "рисованием интерфейса" и "созданием основных функций для реакции на события". А вот связать визуальные и не визуальные компоненты парой кликов мышки — уже нет. Да и у тех, кто продвинулся чуть дальше, все равно не то.

                                      0
                                      Delphi и прочие тулзы быстрого связывания GUI с событиями и логикой поощряют смешение GUI с моделью и проводят к антипаттерну «GUI Trap». Поэтому отделение GUI от модели это хорошо и правильно. А delphi-йский подход «всё в кучу» ужасен. Чтобы нормально отделить GUI от модели в delphi надо затратить не меньше, а то и больше усилий чем в средах где это изначально отдельные вещи.
                                        0

                                        Так Delphi в общем-то не для больших долго сопровождаемых проектов.
                                        У них в теории другая ниша: в течении короткого отрезка времени (максимум неделя) сваять достаточно простое работающее приложение. Т.е. для быстрого создания одноразовых (а если вдруг сразу получится хорошо, то и постоянно применяемых, но это уже скорее случайность) инструментов, быстрого макетирования "на коленке".
                                        И именно для этой ниши инструмент подходит идеально, намного обходя конкурентов.
                                        При проектировании более серьезных проектов все эти возможности уже дейчствительно теряют актуальность, и особых преимуществ в разработке не дают. Но описанная выше ниша реально существует и требует соответствующего инструмента, который на данный момент представлен продуктами-наследниками Delphi и CBuilder (как они там сейчас называются, "RAD Studio"?).

                                          0
                                          Бред какой, 15 лет проектам, проекты сложные, ответственные, миллион+ строк кода. Недавно частично перенесены на линукс под лазарусом.
                                          И их таких много, судя по конференциям. Не говорите о том, о чем понятия не имеете.
                                            +1

                                            Я как раз имею понятие, в том числе о разработке сложных и простых проектов под Delphi, поскольку сам и тем, и другим непосредственно занимался.
                                            Возможно, я не вполне по русски выразился, или вы читали через слово, но я совсем не отрицаю возможности использования Delphi для больших сложных проектов.
                                            Просто баланс использования разных возможностей Delphi несколько меняется. И в результате конкуренты Delphi становятся уже не такими "бесконечно отстающими", а очень даже близкими. И выбор в пользу Delphi становится не таким уж и однозначным, как в случае простых приложений.


                                            По моему опыту при создании простых приложений очень полезна возможность все сделать в несколько кликов мышью, в том числе и невизуальную часть. А при создании серьезных приложений это уже отходит на второй план (разве только при макетировании в начале разработки), а основное внимание уже уделяется собственно коду и его правке "руками". И большое количество связей невизуальных компонент, "спрятанных" в дизайн формы, уже начинает в некоторой степени мешать и раздражать, а потому потихоньку частично "переезжает" непосредственно в код.

                                              +1
                                              Даже если ускорение будет не на порядок (т.е. в 10 раз), а в два раза — то и это хороший результат. В проектах же, где форм много (вижу часто на форуме обсуждают 200-300 форм), преимущества быстрой визуальной разработки остаются. Замечу, что визуальная разработка касается не только интерфейса. В невизуальные компоненты обернуто большое количество кода, сложно найти какую-то нишу в программировании, для которой отсутствуют соответствующие компоненты. А если такая ниша и есть — то есть повод сделать недостающие компоненты :)

                                              Вообще, кто-то всерьёз в 21-м веке предлагает делать меню вот так:

                                              image

                                              ???? И ставит мне минусы, когда я предлагаю RAD альтернативы? Удивительные люди :)

                                              Можно также вспомнить отличную переносимость кода вверх. Делфи, может, несколько консервативен, зато переносимость кода у него отличная. А это — один из больших плюсов как раз на долгоиграющих больших проектах. Насколько я знаю — этим могут похвастаться далеко не все языки.
                                                0

                                                Разработка чего-то вроде приведенного примера автоматизируется не только в Delphi, а потому не является преимуществом Delphi,
                                                А вот возможность мышкой быстро набросать неочевидные связи между визуальным и невизуальным (то самое конкурентное преимущество) при сопровождении большого проекта часто превращается в серьезную проблему (т.е. в совсем не преимущество), поскольку эти связи не всегда просто отслеживаются (в силу своей необязательности и неочевидности).
                                                Есть, конечно, вещи достаточно простые и понятные вроде таймера на форме.
                                                А вот когда, например, в изобилии появляются разнообразные компоненты для доступа к БД, связанные с различными визуальными компонентами, то при превышении некоторой сложности проекта становится "весело". Оно, конечно, красиво и на первый взгляд понятно выглядит, но спрятанные связи...

                                                  0
                                                  а потому не является преимуществом Delphi

                                                  Согласен на такую формулировку: является преимуществом не только Delphi.

                                                  Стоит смотреть не только наличие механизма визуального проектирования, но и на количество и качество компонент для Делфи и для других языков.

                                                  Пример с другого форума, сравнивали компоненты TRichEdit для разных языков:

                                                  Delphi:

                                                  image

                                                  C++

                                                  image

                                                  то при превышении некоторой сложности проекта становится «весело».

                                                  У нас не было описанных проблем, и я ни разу не видел жалобы на это на форумах, возможно, просто повезло, конечно.

                                                  Схемы данных не помогут?

                                                  image
                                                    0

                                                    И каким именно образом схемы данных могут помочь быстро понять, с каким именно TDatabase связаны различные TQuery? А таких связей у компонента может быть очень много.

                                                      +1
                                                      И каким именно образом схемы данных могут помочь быстро понять, с каким именно TDatabase связаны различные TQuery?

                                                      Если опустить тот момент, что задумываться о связи между TQuery и TDatabase вам придется скорее всего только один раз, в момент первичной настройки TQuery, то в Delphi для этого был другой инструмент, назывался Object TreeView или что-то в этом роде. В любом случае, VCL спроектирована таким образом, что связи между компонентами в подавляющем большинстве случаев иерархические, а не «многие ко многим», и хорошо визуализируются и анализируются. Я в своё время участвовал во многих достаточно крупных проектах на Delphi, с мегабайтами текстов и сотнями форм. Своих проблем хватало, но чего-чего, а проблем со сложными неочевидными связями между компонентами не было никогда.
                                                    0
                                                    при сопровождении большого проекта часто превращается в серьезную проблему

                                                    Это больше вопрос соблюдения архитектурных стандартов, нежели недостатков среды разработки. Можно подумать, с помощью делегатов и свойств в C# есть какая-то сложность сделать жуткий спагетти-код.
                                                      0

                                                      Вопрос в необходимости отказа в больших проектах от использования главного конкурентного преимущества Delphi, без которого он становится таким же, как остальные IDE с редактором форм. И тогда начинают сказываться другие вопросы вроде крайней падучести многих версий Delphi, особенно обостряющейся на как раз больших проектах.

                                                        0
                                                        У меня и 7-ка, и 2010, и XE6 работают стабильно. Несколько проектов около миллиона строк. Падений минимум, особенно в более свежих версиях. Да, было несколько нестабильных версии — так никто не заставляет на них работать.
                                                        Не знаю — откуда у вас появляется такая глобальная проблема со связями. У меня таких проблем не было ни разу.
                                                        У себя, что бы связи к базам долго не искать, расставляю компоненты на дата-модулях. Вот, например, один из модулей, каждая транзакция под своим коннектом. Всё связанное — снизу. Просто, удобно и всё видно:

                                                        image
                                                          0
                                                          Добавьте к этому многопоточность, connection-пулы, вложенные транзакции и увидите что всё это надо будет переписывать. Кстати вижу тут Indy — сталкивался с этой библиотекой. Чтобы заставить Indy работать многопоточно надо также приложить немало усилий, по сравнению с которыми возможность увидеть его иконку на форме — ничто.
                                                            0
                                                            Чтобы заставить Indy работать многопоточно надо также приложить немало усилий, по сравнению с которыми возможность увидеть его иконку на форме — ничто.

                                                            Я лет десять назад писал HTTP-сервер для клиент-банка на Indy. Многопоточный, с пулом коннектов, с шифрованием и т.д… Поэтому хотелось бы, чтобы вы уточнили, какие там усилия были нужны. У меня, насколько я помню, от библиотеки не потребовалось ничего — там всё прекрасно работало из коробки. Только класс-прокладку от TIdHTTPServer к датамодулю WebBroker надо было подправить.
                                                              0
                                                              Конкретно в Indy нотификация реализуется с большим оверхедом.
                                                              0
                                                              это шот рабочего проекта, если что. пулы, инди. всё работает. 100-200 потоков обрабатывается сервисом, для связи с гуем сервиса используется CmdServer, 40 команд гоняется туда-сюда, этот же сервер является веб сервером, только шот от другого модуля.
                                                                0
                                                                По картинке этого не видно. ps. Я фиксил проблемный проект с видеоаналитикой и трансляцией изображений с ip-камер. А также переводил его на lazarus, linux и местами на другие языки.
                                                              0
                                                              IBQuery1? JvThread1? Серьезно? У Вас несколько проектов в миллион строк с такими вот IBTransaction10? Когда вижу такое, хочется автору руки поотрывать. Неужели сложно потратить две секунды и назвать элемент по-нормальному? Неужели в западло относиться к себе и другим программистом с уважением? Я сейчас в похожем проекте копаюсь — хрен разберешь, что и зачем — приходится трассировать в уме и догадываться, что автор имел ввиду, вместо того, чтобы просто читать.
                                                              Мне бы такое выкладывать было стыдно.
                                                                0
                                                                Удобно по-своему. Я всё вполне разбираю. Люди, с кем работаю, тоже не жалуются.
                                                                0
                                                                del
                                                          –1
                                                          Можно также вспомнить отличную переносимость кода вверх.

                                                          О да!


                                                            var
                                                              Bounds: TRect;
                                                              Width: Integer;
                                                          ...
                                                          
                                                              with Bounds do
                                                              begin
                                                                Width := Right - Left;
                                                          ...
                                                          

                                                          Упс, в новой версии у record появились свойства, в том числе и Width у TRect
                                                          То, что за использование with стоит бить по рукам — другой вопрос.

                                                            0
                                                            Поэтому with не рекомендуют использовать, или хотя бы не давать переменным распространенные имена.
                                                              0
                                                              Поэтому with не рекомендуют использовать

                                                              Что никак не мешает им быть в унаследованном коде.


                                                              не давать переменным распространенные имена.

                                                              Это не серьёзно. Имена должны быть удобными и понятными.

                                                                0
                                                                в данном случае Width не является удобным и понятным, в реальном коде переменная будет иметь составное имя — «чегототамWidth».
                                                                на практике проблема с with возникает когда указывают несколько объектов — with a, b do, и в b появляется то к чему обращаешься в a.
                                                                другие случаи это криворукость программиста
                                                            0
                                                            Апологеты вообще счастливы от блокнота…
                                                          0

                                                          Если же вас зацепила первая фраза, то это просто реальная характеристика инструмента, который можно использовать в самых разных целях.
                                                          Если для быстрого создания простых приложений Delphi подходит идеально и на порядок лучше других, в то время как в других областях он хотя и очень даже подходит, но уже совсем не лучший, то логично предположить, что он все-таки предназначен именно для для быстрого создания простых приложений, а не для больших проектов.

                                                            +1
                                                            Он предназначен для быстрой разработки приложений. А какого у вас будут размера проекты — зависит от вас. Ни QT ни VC ни C# ни Javа не делают код сам по себе лучше. Качество кода зависит от разработчика, как и архитектура. преимущество Delphi в том, что позволяет писать как на чистом API, так и в RAD (rapid application development)стиле.
                                                            И при добавлении каждого нового модуля его можно сразу писать серьезно прорабатывая архитектуру, а можно визуально набросать необоходимый функционал, вернуться к основному функционалу, а затем постепеннно прорработать этот модуль. это сильно сокращает общие сроки разработки проекта.
                                                          0
                                                          У меня есть работы которым уже 25-27 лет…
                                                            0
                                                            это как? Delphi появился в 1995 )
                                                              0
                                                              Возможно проекты еще из TurboPascal
                                                                0
                                                                Мало кто помнит, но было что-то типа Pascal For Windows или что-то такое. Без визуальных плюшек.
                                                                  0
                                                                  Turbo Pascal for Windows. Продавался (если это слово можно применять к софту, который мы видели в СССР) и как отдельный продукт, и в составе пакета Borland Pascal 7.0. Не совсем без визуальных, там был вполне себе визуальный редактор ресурсов, в котором рисовались формы и диалоги.
                                                                    0
                                                                    BPW — Borland Pascal for Windows. Входила в комплект Borland Pascal вместе с Turbo Pascal
                                                    0
                                                    Что будете делать, если нужно будет сделать приложение для mac os или мобильных устройств? Я вижу только альтернативу в веб приложениях js/scalajs/clojurescript + react/angular 2 + electron/cordova. Везде работает, одна кодовая база.
                                                      +1
                                                      Delphi, как и большинство других современных средств разработки, давно позволяет делать приложения и для мобильных устройств, и для macOS. Надо иметь в виду, что у Delphi немного иная ниша, чем у веб-приложений. Delphi чаще всего используют для разработки бизнес-приложений, где чаще всего предполагается какая-либо активная работа с БД. «Фишка» Delphi, которая с ней была изначально, это удобные биндинги к базам данных и data-aware контролы, особенно гриды. В веб-платформах, к сожалению, ничего столь мощного нет в силу ограничений, накладываемых броузерами (ближе всего подобрался devExpress, но там другие нюансы есть). Поэтому потребность делать мобильные клиенты на Delphi не так часто бывает востребованной. Да и, честно говоря, далеко не всегда выгоднее иметь одну кодовую базу и слои адаптации под разные платформы, чем иметь несколько различных клиентов, каждый из которых оптимизирован под свою платформу.
                                                        +3
                                                        Делфи сейчас работает на всех основных платформах — Win32/64, iOS, Android, MacOS, серверный линукс почти сделали. Собранный код по производительности может и проигрывает плюсам в некоторых случаях, но большинство частей библиотек хорошо оптимизированы ассемблером.
                                                        У Лазаруса список поддерживаемых платформ вообще огромен. При том, что он бесплатный, и последние сборки, например, отсюда: https://www.getlazarus.org вполне пригодны для работы. Есть биндинги Qt и Gtk, под виндой и линуксом. Так что для того, что бы работать с Qt совсем не обязательно переходить на плюсы.
                                                        JS, который мы тоже используем, сильно ограничен рамками браузера. Некоторые банальные вещи — например — копирование в буфер в нём сделать просто нельзя. Многие вещи браузеро-зависимые. То есть — получается не просто платформы, а куча браузеров на множестве платформ. Мы активно используем HMLT5, многопоточную обработку, WebGL. У разных браузеров на разных платформах свои особенности. Вместо написания функциональности приходится постоянно заниматься оптимизацией под браузеры.
                                                          0
                                                          http://www.unigui.com

                                                          Платная, да. По цене смартфона. Есть и бесплатные варианты, например http://www.morfik.com
                                                          Есть backend-фреймворки — https://github.com/silvioprog/brookframework
                                                            0
                                                            Возможно будет интересно:

                                                            Web Front End Framework (on Delphi)

                                                            0
                                                            система финансово-управленческого учёта специального назначения


                                                            ничего лишнего, максимальная эргономика с защитой от дурака и зловреда, минимальное время реакции на изменяющиеся требования бизнеса, минимальные требования к инфраструктурному обеспечению (типа ничего, кроме sql сервера)


                                                            Как вариант платформа 1С 8.3.х. (про цену лицензий в требованиях ничего не было :)). Но если система большая то цена лицензий в стоимости системы занимает от силы 1% ну и Delphi тоже не бесплатная.

                                                            По остальным показателям именно в «финансово-управленческом учёте» 1С вполне способна составить конкуренцию Delphi.
                                                            А если нужно и «минимальное время реакции на изменяющиеся требования бизнеса» то delphi сильно отстает от 1С.
                                                              0
                                                              А если нужно и «минимальное время реакции на изменяющиеся требования бизнеса» то delphi сильно отстает от 1С
                                                              Где?!!!

                                                              Главная проблема с 1С — то, что она живёт своей жизнью, развивается и обновляется исходя из собственных интересов. Фактически, с какого-то момента придётся выбирать — или фиксировать их версию, или минимизировать своё вмешательство. И что будет через несколько лет такой жизни? Тут нету полного контроля и владения. Что и зачем там происходит внутри — тема для отдельной платной консультации. Куча лишних сущностей, нам не нужных, при том, что наверняка нет отдельных немаловажных кусочков.

                                                              Это как тот халат — перламутровые пуговицы на него перешить можно, но галоши не наденешь без боли. А бизнес хочет галоши и шляпу.

                                                              Короче, вы упустили слова: «специального назначения» (не универсальная, не общего назначения), «ничего лишнего». Из рыночного ПО — это, наверное, поле для ERP систем, до которых 1С ценой не доросла. Вот только реальное внедрение и сопровождение ERP — очень затратная вещь в реальности.
                                                                0
                                                                Не буду спорить по поводу лишних сущностей и отдельных кусочков. По старой памяти в Delphi всего этого не меньше а с учетом проблем в сторонних компонентах и с учетом того что зачастую сторонние компоненты поставляются с закрытыми исходниками и контроля над ними не намного больше.
                                                                В целом же платформа содержит все что не обходимо:
                                                                1. Работа с почтой, фтп, WEB сервисы (как сервер так и клиент) HTTP сервисы (аналогично), поле HTML документа позволяет встраивать HTML в разрабатываемые решения.
                                                                2. Мощная система контроля доступа на уровне строк RLS
                                                                3. Ведение журналов на уровне системы
                                                                4. Мощная система отчетов которую в т.ч. могут настраивать и пользователи СКД
                                                                5. Работа с COM
                                                                6. Поддержка всего более менее используемого торгового оборудования (Сканеры ШК, терминалы смарт карт, фискальные регистраторы, кассовые аппараты).

                                                                Это далеко не полный список. Плюс многие разработки уже есть в типовых системах и так же доступна БСП в которой накоплен опыт огромной компании и которая доступна при разработке своих решений.

                                                                «Вот только реальное внедрение и сопровождение ERP — очень затратная вещь в реальности.»
                                                                Вы хотите сказать что написать с 0 систему уровня ERP и затем внедрить ее на предприятии намного дешевле? Сколько времени вам потребуется на написание подобной системы?

                                                                В целом же при прочих равных на 1С можно гораздо быстрее и гораздо дешевле написать для бизнеса практически все что можно написать на Delphi.

                                                                Притом вы сразу получаете WEB клиент + в нагрузку возможность создать клиентов для Android/IOS

                                                                В качестве примера, сколько денег и времени у вас уйдет на реализацию:
                                                                https://www.youtube.com/watch?v=DgPuF_WTyB8
                                                                В 1С это будет доступно по подписке ИТС (которая в общем то есть почти всегда).
                                                                А бизнесу это нужно еще вчера.
                                                                  0
                                                                  «Вот только реальное внедрение и сопровождение ERP — очень затратная вещь в реальности.»
                                                                  Вы хотите сказать что написать с 0 систему уровня ERP и затем внедрить ее на предприятии намного дешевле? Сколько времени вам потребуется на написание подобной системы?

                                                                  Лет 13-14 и можно не останавливаться… Её не надо писать — она есть. И вопрос в том, целесообразен ли переход на 1с, если организационных проблем возникнет — море, а существующая система в полном порядке, развивается, дорабатывается и свои задачи выполняет?

                                                                  Я не говорю, что 1с — это неправильно. Кому как. Есть много правильных решений, и наше — одно из них. И если вернуться чуть пораньше, речь шла не о выборе разрабатывать / покупать, а «Несчастные, вы пишете на Дельфи?!!! Есть же куча более модных языков!» — «Да, мы пишем на Дельфи, и нам кайфно! Мы счастливые, правда!». Честно, не знаю никого, кто наслаждается программированием под 1С. Не говорю, что их нет, но правда — не знаю.

                                                                  Видео про 54-ФЗ глянул, честно говоря, мельком — уж очень медленно барышня вещает. Кассу с передачей данных коллега прикрутил со вторника по утро пятницы, и самое сложное было, судя с моей стороны наблюдателя, это экономия бумаги (торгуем оптом, чеки получаются многометровые). Вот и все затраты на более, чем 10 филиалов. Никаких настроек на местах, никаких семинаров по 3500 рублей за билет. Купят несколько десятков касс, подключат, будут работать. У бизнеса это уже есть, и даже не вчера.

                                                                  по нумерованным пунктам — у нас это тоже есть. Есть автоматизация склада, есть интеграция с системами мобильной торговли, есть электронный документооборот с поставщиками… Много чего есть. И зачем платить за 1С? Из любви к революциям? Может, у нас меньше перламутровых пуговиц и размеренно вещающих видеобарышень, но всё, что нужно, есть сейчас и будет потом.
                                                                    0
                                                                    Сразу скажу, что у меня два рабочих инструмента 1С и Delphi. И оба инструмента знаю на экспертном уровне. Так вот…

                                                                    Есть гигантская масса учетных задач где 1С нет равных по скорости разработки. Стоимость лицензии, по сравнению экономией на разработке и стоимостью владения, ничтожна.
                                                                    Есть гигантская масса других задач, которые решить с помощью 1С никак нельзя. Тут вотчина Delphi и еже с ними.

                                                                    В спорах по лицензиям 1С не нужно забывать и такой аспект как единовременность этих выплат. Лицензия покупается один раз и потом на этой машине можно использовать столько баз (конфигураций) – сколько душе угодно. Так часто и поступают: Берут «бухгалтерию», а потом еще с десяток внутренних систем внедряют.

                                                                    Можно долго спорить что круче, но если 1С-ник понятия не имеющий про SQL, утечки памяти и прочие «интересности», может в мгновение ока, буквально на коленке, наклепать мини учетную систему — это дорогого стоит. И только за одно это 1С можно уважать.

                                                                    Главная проблема с 1С — то, что она живёт своей жизнью

                                                                    Я бы лучше и не сформулировал. Они ведь на основе чего-то строят свои планы по развитию платформы. Или типа художник так видит….
                                                            0
                                                            а таки ваше гецэцэ умеет вицээль? А поддерживать старое легаси писанное и переписанное на дельфях как на гэцеце? Ах да, Reference counting и DCOM+ из коробочки, выньте да положте, пожалуйста…
                                                            +5
                                                            Спасибо. Будем пробовать.
                                                              +2
                                                              В свое время пользовался Delphi разных версий 3, 4, 5, 6. Большую часть делал интерфейсы, кастомные контролы и псевдо 3D на cos и sin. Были времена, приятно вспомнить.
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  –1
                                                                  Плюсую WM_ERASEBKGND.
                                                                  Изучать надо WinAPI, тогда и проблем не будет.
                                                                  Работаю с OWL/VCL более 20 лет. Про «проблему мерцания» слышу в первый раз.
                                                                    +6
                                                                    Откройте стандартный «диспетчер устройств» и попробуйте изменять размер окна, оцените мерцание.
                                                                    В VCL также, посмотрите на саму Rad Studio.
                                                                      +1
                                                                      О проблеме с мерцанием слышал любой кто что либо отрисовывал самостоятельно
                                                                      –1
                                                                      но вы форсируете «папу» «напечататься» (WM_PRINTCLIENT), а это может иметь побочные эффекты. Лет адцать назад я встречал контролы, которые не давали «печататься» — чтобы нельзя было сделать, скажем, слепок с защищенной пдфки, которую контрол демонстрирует.

                                                                      Как я уже писал, класс показал свою надежную работу :)
                                                                      Но для таких «кривоватых» контролов я сделал виртуальный метод procedure DrawBackground(DC: HDC); virtual;, в котором для обхода проблем подобных компонентов можно переопределить отрисовку фона.
                                                                      Кроме того компонент имеет множество настроек буферизации.
                                                                      +2
                                                                      Спасибо! Интересно.
                                                                        –2
                                                                        Про мерцание уже забыл по двум причинам: не использую большое количество контролов на форме и, наоборот, использую дискретные видео-карты и хорошим 2D-рендерингом.
                                                                          +2
                                                                          К сожалению начиная с Windows Vista, GDI практически перестало использовать 2д ускорение, и к сожалению проблема мерцания никуда не ушла, да о чем говорить — многие стандартные Windows приложения мерцают.
                                                                            0

                                                                            Блин, вы о чем?
                                                                            Что за мерцание?


                                                                            Пример такого стандартного Windows приложения и как это самое мерцание воспроизвести?

                                                                              +1
                                                                              Как я уже писал: «Откройте стандартный «диспетчер устройств» и попробуйте изменять размер окна, оцените мерцание.».
                                                                                0
                                                                                Windows 10

                                                                                Открыл Device Manager — ничего не мерцает, как я ни меняю размер окна.

                                                                                С какой частотой и как быстро его надо менять?
                                                                                  +1
                                                                                  Важно чтобы было не было отключено «Отображать содержимое окна при перетаскивании».
                                                                                  Можно еще открыть «Управление компьютером» и полистать там вкладки.
                                                                                    0

                                                                                    Ну предположим, что я совсем идиот, и флаг "отображать содержимое" у меня был отключен.
                                                                                    Проблема в том, что он был включён и поведение не воспроизводилось.
                                                                                    Аналогично с вкладками компьютера.


                                                                                    Вы можете уточнить свою конфигурацию, на которой вы это воспроизводите?

                                                                                      +1
                                                                                      Win7, открываю диспетчер задач и начинаю его ресайзить туда-сюда: мерцают верхние вкладки.
                                                                                        0
                                                                                        Последняя стабильная Windows 10. При изменении размера влево вправа мерцает. Не каждое изменение, но мерцает. Попробуйте секунд 5 изменять и увидите
                                                                                          0
                                                                                          Воспроизвелось.

                                                                                          Действительно надо достаточно долго его туда/сюда дергать.

                                                                                          Но я не уверен, что подобное требует фикса :)
                                                                                            0
                                                                                            Это не проявляется так явно в простом интерфейсе, без активной кастомной отрисовки, если же интерфейс сложный, наполнен кастомными контролами, то от мерцания вытекают глаза.
                                                                                              –1
                                                                                              ЕМНИП, году в 1996/1997 на VB4.0 решалась подобная задача путем отрисовки «всей» формы в буфер и только после этого вывода ее.

                                                                                              Наследовать компоненты не требовалось, более того этот механизм работал и с вообще чужими компонентами, которые и знать не знали что мы боремся с мерцанием.
                                                                                                0
                                                                                                Как вы интересно боролись если не знали о мерцании?
                                                                                                Блин, вы о чем?
                                                                                                Что за мерцание?

                                                                                                  –1
                                                                                                  Как вы интересно боролись если не знали о мерцании?

                                                                                                  Легко боролись.
                                                                                                  Точно так же как друг на курсовой боролся с ним же при рендере самодельной ртс.

                                                                                                  Но вот где-то с начала 2000-х как-то не доводилось встречать настолько криворукие офисные приложения где прямо необходимо было с этим бороться.
                                                                                                  Отсюда и удивление, что кто-то сейчас умудряется это мерцание встречать…
                                                                                                  И что удивительно создавать его в своих приложениях, чтобы
                                                                                                  от мерцания вытекают глаза.
                                                                                                    0
                                                                                                    Легко боролись.
                                                                                                    Точно так же как друг на курсовой боролся с ним же при рендере самодельной ртс.

                                                                                                    Я к тому, что странно бороться с мерцанием, не зная что это:
                                                                                                    Блин, вы о чем?
                                                                                                    Что за мерцание?


                                                                                                    И что удивительно создавать его в своих приложениях, чтобы

                                                                                                    Ну майкрософт создает как-то же?

                                                                                                    решалась подобная задача путем отрисовки «всей» формы в буфер и только после этого вывода ее.

                                                                                                    К слову в Win32 невозможно все окно забуферизировать нормально.

                                                                                                    Я думаю вы просто «не в теме», проблема есть, и простых способов «из коробки» нет
                                                                                                      –1
                                                                                                      Я к тому, что странно бороться с мерцанием, не зная что это:

                                                                                                      Я к тому, что не у рукожопов эта проблема в офисных приложениях отсутствует как класс настолько давно, что упоминание вызывает удивление.

                                                                                                      И решения из коробки не было и в 90-х
                                                                                                      В том же vb4 приложении использовались вызовы winapi функций из gdi32 библиотеки.
                                                                                                      Какие конкретно уже не помню. В памяти остались лишь ключевые слова getDC, HWND

                                                                                                      Ну майкрософт создает как-то же?

                                                                                                      Угу, ровно там и так, что надо очень постараться, чтобы его заметить и воспроизвести.

                                                                                                      Это сильно отличается от ваших приложений от которых, по вашим словам «вытекают глаза».
                                                                                                        0
                                                                                                        Я к тому, что не у рукожопов эта проблема в офисных приложениях отсутствует как класс настолько давно, что упоминание вызывает удивление.

                                                                                                        Ну раз я, все отблагодарившие меня, люди использующие в своих проектах данное решение «рукожопы», то я не намерен продолжать дискуссию.
                                                                                                        Минусанул.
                                                                                                          –2
                                                                                                          Это ваши личные комплексы.

                                                                                                          Миллионы леммингов не могут ошибаться…
                                                                                                            0
                                                                                                            Этим вы показываете свое невежество и некомпетентность.
                                                                                                              –2
                                                                                                              Естественно.
                                                                                                              Ведь вы один из тех миллионов.
                                                                                                              И вы, конечно же, не ошибаетесь
                                                                                                                0
                                                                                                                Откуда вы только беретесь?
                                                                                                                Успокойся, если не понимаешь о чем речь — промолчи, не надо засирать комментарии.
                                                                                                                  –2
                                                                                                                  Оттуда откуда и такие же «программисты» вроде вас юноша…

                                                                                                                  Сначала пишут такие программы
                                                                                                                  image

                                                                                                                  Потом героически преодолевают трудности…
                                                                                                                    0
                                                                                                                    О, я смотрю вы заглянули мне в профиль?
                                                                                                                    Переход на личности — еще один показатель вашей полной некомпетентности.
                                                                                                                      –2
                                                                                                                      Только не говорите, что эта случайная картинка плод ваших трудов… :-D
                                                                                                                      +1
                                                                                                                      В чём проблема этой картинки?
                                                                                                                        –1
                                                                                                                        перегруженность интерфейса вестимо.

                                                                                                                        Хотя это не худший из образцов, просто первый попавшийся
                                                                                                                          +1
                                                                                                                          Не совсем понимаю, как Вы по одной картинке определили перегруженность.
                                                                                                                          Возможно, в данном случае такой вид был оправдан.
                                                                                                                            –1
                                                                                                                            Это психология.
                                                                                                                            Объем человеческого внимания ограничен.

                                                                                                                            Оправдан или нет это отдельная тема
                                                                                                                              +1
                                                                                                                              Под этим лозунгом делают новомодные «упрощенные приложения для домохозяек», в которых чтобы добраться до нужного функционала надо пробираться через тонны менюшек, или функционал вообще выпилен.
                                                                                                                                –1
                                                                                                                                Учитесь проектировать интерфейсы.

                                                                                                                                ЕМНИП у того же SAP в каких-то продуктах доступ к экрану интерфейса любого уровня вложенности можно было получить в три нажатия клавиши
                                                                                                                                  0
                                                                                                                                  Ну покажите пример хороших интерфейсов спроектированных вами, а то у вас в профиле ссылки на домены неоплаченные ;)
                                                                                                                                  Не всегда есть резон упрощать интерфейс, ломая при этом удобство использования.
                                                                                                                                    –2
                                                                                                                                    Во-первых, профилю примерно треть вашей жизни уже.
                                                                                                                                    Соответственно редактировался он года 3-4 назад

                                                                                                                                    Во-вторых, офисным ПО не занимаюсь уже примерно половину вашей жизни.

                                                                                                                                    В-третьих, ваше «не всегда» это детский лепет.
                                                                                                                                    Поскольку удобство использования напрямую зависит от навыков человека и в том числе его внимания.
                                                                                                                                    Если этого самого внимания не хватает, то и скорость и удобство использования будут отрицательными.
                                                                                                                                      0
                                                                                                                                      Во-первых, профилю примерно треть вашей жизни уже.

                                                                                                                                      Во-первых нет ничего тупее апеллирования возрастом оппонента.

                                                                                                                                      Соответственно редактировался он года 3-4 назад

                                                                                                                                      Тем не менее это показатель.

                                                                                                                                      Во-вторых, офисным ПО не занимаюсь уже примерно половину вашей жизни.

                                                                                                                                      Во-вторых, если вы не занимаетесь уже давно разработкой ПО, ваши знания могли устареть.
                                                                                                                                      А очередное упоминание возраста не красит вас.

                                                                                                                                      В-третьих, ваше «не всегда» это детский лепет.
                                                                                                                                      Поскольку удобство использования напрямую зависит от навыков человека и в том числе его внимания.
                                                                                                                                      Если этого самого внимания не хватает, то и скорость и удобство использования будут отрицательными.

                                                                                                                                      В-третьих, вы вообще не привели никаких логичных доводов, кроме упоминания возраста.

                                                                                                                                      Я, как пользователь, чувствую неудобство от «интерфейсов для домохозяек», и я рад когда применяется инженерный подход, пусть и не всегда красивый, но при этом удобный и функциональный.

                                                                                                                                      Предлагаю закончить этот флейм.
                                                                                                                                        –3
                                                                                                                                        Во-вторых, если вы не занимаетесь уже давно разработкой ПО, ваши знания могли устареть.


                                                                                                                                        Я тупых не люблю категорически.

                                                                                                                                        Упоминание vb4 должно было явно вам показать когда это было. Более того никакой отсылки к современному апи не было.
                                                                                                                                        Вы решили вернуться к этому вопросу повторно.
                                                                                                                                        В связи с этим у меня есть вопрос — вы тупой?

                                                                                                                                        Тем не менее это показатель.

                                                                                                                                        Показатель это то, что указанные домены в свое время держали нагрузку в 2-2.5 млн уникальных посетителей в сутки
                                                                                                                                        А это вам статистика рунета сейчас
                                                                                                                                        http://www.liveinternet.ru/rating/ru/

                                                                                                                                        Для понимания.

                                                                                                                                        Я, как пользователь, чувствую неудобство от «интерфейсов для домохозяек», и я рад когда применяется инженерный подход, пусть и не всегда красивый, но при этом удобный и функциональный.


                                                                                                                                        Вы путаете инженерный подход и тяп-ляп перегруженный интерфейс по факту.
                                                                                                                                        В результате инженерного подхода может родиться как тысячекнопочный так и двухкнопочный интерфейс.
                                                                                                                                          +1
                                                                                                                                          Вы путаете инженерный подход и тяп-ляп перегруженный интерфейс по факту.
                                                                                                                                          В результате инженерного подхода может родиться как тысячекнопочный так и двухкнопочный интерфейс.

                                                                                                                                          Вот именно, при инженерном подходе может выйти интерфейс с кучей контролов, который для кого-то будет выглядеть перегруженным, но для человека пользующегося этой программой он будет очень удобным.

                                                                                                                                          Показатель это то, что указанные домены в свое время держали нагрузку в 2-2.5 млн уникальных посетителей в сутки
                                                                                                                                          А это вам статистика рунета сейчас
                                                                                                                                          http://www.liveinternet.ru/rating/ru/

                                                                                                                                          Вы можете написать что угодно задним числом, почему я должен в это верить?
                                                                                                                                          А писал я о том, как можно обвинять кого-то в криворукости, тупости и т.д. когда вы не удосужились из профиля ссылки нерабочие потереть за столько лет?

                                                                                                                                          Я тупых не люблю категорически.

                                                                                                                                          Упоминание vb4 должно было явно вам показать когда это было. Более того никакой отсылки к современному апи не было.
                                                                                                                                          Вы решили вернуться к этому вопросу повторно.
                                                                                                                                          В связи с этим у меня есть вопрос — вы тупой?

                                                                                                                                          Я долго пытался не поддаваться на провокации, но вы усердно добивались своего, и вы добились!
                                                                                                                                          Вы — критин.
                                                                                                                                            –1
                                                                                                                                            Вот именно, при инженерном подходе может выйти интерфейс с кучей контролов, который для кого-то будет выглядеть перегруженным, но для человека пользующегося этой программой он будет очень удобным.


                                                                                                                                            Это не инженерный подход.
                                                                                                                                            Рекомендую сходить в интернет и почитать об этом.

                                                                                                                                            Вы можете написать что угодно задним числом, почему я должен в это верить?
                                                                                                                                            А писал я о том, как можно обвинять кого-то в криворукости, тупости и т.д. когда вы не удосужились из профиля ссылки нерабочие потереть за столько лет?

                                                                                                                                            Не обязаны. И мне собственно говоря насрать верите вы или нет.
                                                                                                                                            Презентационная роль профиля мне сейчас не требуется. Соответственно зачем мне его исправлять или тем более писать что-то вроде «лучший контрол....»? :-D

                                                                                                                                            P.S.
                                                                                                                                            Вы — критин.

                                                                                                                                            крЕтин
                                                                                                                                            +1
                                                                                                                                            Вы, однако, очень вежливый.
                                                                                                                                              –1
                                                                                                                                              У вас есть какие-то претензии к ответу лично вам?
                                                                                                                                              +2
                                                                                                                                              Я тупых не люблю категорически.

                                                                                                                                              Получается вы сами себя недолюбливаете?
                                                                                                                                                –1
                                                                                                                                                Не получается.

                                                                                                                                                3 человека из 1000 с таким же или более высоким интеллектом немного примиряют меня с моими недостатками.
                                                                                                                                                  +2
                                                                                                                                                  У истинного интеллектуала хватит интеллекта чтобы не писать о своем интеллекте в комментариях.
                                                                                                                                                  Не знаю о ваших недостатках, но у вас явно комплексы связанные с уровнем IQ
                                                                                                                                                    0
                                                                                                                                                    Боже, ещё один дурак.

                                                                                                                                                    Раскрою вам глаза.

                                                                                                                                                    Нетерпимость к чужой тупости естественно произрастает из собственных комплексов о недостатке моего личного интеллекта.

                                                                                                                                                    Что не отменяет указанных выше цифр.

                                                                                                                                                    P.S. И, да, не вам судить об истинности. Не тот уровень у вас
                                                                                                                                                      0
                                                                                                                                                      Ваш уровень видно из вашего профиля (два неработающих сайта). А о моем уровне вы понятия не имеете
                                                                                                                                                +2
                                                                                                                                                Показатель это то, что указанные домены в свое время держали нагрузку в 2-2.5 млн уникальных посетителей в сутки


                                                                                                                                                И мы типа джентльмены должны верить друг другу на слово?
                                                                                                                                                А нагрузка разве в уникальных посетителях измеряется? правда?

                                                                                                                                                тогда и мне есть чем похвастать:
                                                                                                                                                Мой домашний компьютер легко справляется с обработкой 15-80 тысяч запросов (статика) в секунду. Получается если в среднем уникальный посетитель генерирует 100 хитов, то мой домашний компьютер будет «держать» 25-30 млн. уникальных посетителей в сутки.

                                                                                                                                                Понятно что мой пример (как и ваш) — это метрики сферических коней в вакууме. Но все же получается, что ваши домены справлялись с нагрузкой на порядок меньшей чем моя домашняя (не самая навороченная) машина.

                                                                                                                                                Таким образом вытекает самый главный вопрос:
                                                                                                                                                Как же вы умудрились добиться столь низких показателей производительности для вашего сферического коня в вакууме?

                                                                                                                                                А теперь верну вам ваше же выражение — вопрос (цитата):
                                                                                                                                                В связи с этим у меня есть вопрос — вы тупой?

                                                                                                                                                и закончу вашим же выражением
                                                                                                                                                Я тупых не люблю категорически

                                                                                                                                                тут согласен. Вы мне явно не нравитесь…
                                                                                                                                                  –1
                                                                                                                                                  А нагрузка разве в уникальных посетителях измеряется? правда?

                                                                                                                                                  тогда и мне есть чем похвастать:


                                                                                                                                                  Ну вот когда вашу статику захотят посмотреть в таких объемах, тогда и поговорим о разнице в нагрузке между статикой и полнотекстовым поиском.

                                                                                                                                                  А до тех пор не обезьяничайте, пытаясь подражать мне
                                                                                                                                                    +1
                                                                                                                                                    Ну вот когда вашу статику захотят посмотреть в таких объемах, тогда и поговорим о разнице в нагрузке между статикой и полнотекстовым поиском.

                                                                                                                                                    Так статистику вашего полнотекстового поиска никто и не видел. Это лишь ваши слова — то есть пшик.
                                                                                                                                                    А до тех пор не обезьяничайте, пытаясь подражать мне

                                                                                                                                                    Обезьянничая подражать вам? Вы себя считаете обезьяной, коль подражая вам обезьянничают?
                                                                                                                                                    Но позвольте! Вы себе явно льстите, я вам не вовсе и подражал.Много чести для обезьяны…
                                                                                                                                                      0
                                                                                                                                                      То есть, если я внезапно найду в архивах статистику, то вы начнёте бить челом и извиняться? :-D
                                                                                                                                                      Ну ок, а мне-то это зачем?

                                                                                                                                                      Вы себе явно льстите, я вам не вовсе и подражал.Много чести для обезьяны…

                                                                                                                                                      Ну раз для вас слишком много чести…
                                                                                                                                                      То, гражданин обезьяна, самоликвидируйтесь из обсуждения непонятно чего.
                                                                                                                                      +1
                                                                                                                                      А иногда ещё и время надо экономить этому пользователю и не путать его в десятке окон с двумя параметрами.
                                                                                                                                        –1
                                                                                                                                        См. ответ выше.
                                                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                                              0
                                                                                              Я бы в качестве ещё одного примера привёл окно проводника. Особенно момент уменьшения до размера, когда пропадает риббон
                                                                                          0
                                                                                          странно что вообще проблема с мерцанием еще есть, всмысле, ведь ее решили на уровне винды — «композитор» с висты работает с двойной буферизацией в принципе.
                                                                                            +2
                                                                                            «композитор» решает проблему композиции только окон верхнего уровня.
                                                                                              –2
                                                                                              и? как раз для этого он держит буфер для каждого окна и туда пишутся все команды отрисовки а потом результат выводится, мерцание возможно только если дать команду заставляющую «композитор»отрисовать окно на экране.
                                                                                              да и вообще, прежде чем написать, я проверил — с выключенными темами мерцает с включенными нет.
                                                                                                +3
                                                                                                Как я уже писал: «Откройте стандартный «диспетчер устройств» и попробуйте изменять размер окна, оцените мерцание.».
                                                                                                Возможно у вас быстрая машина и вы не успеваете заметить мерцание, или приложение на котором вы проверяете буферизирует отрисовку.
                                                                                                  0
                                                                                                  т.е. по хорошему, двойную буферизацию надо отключать когда программу по RDP запустили а с висты еще когда темы включены, как-то так.
                                                                                                    +2
                                                                                                    Повторяю — проблема мерцания в классических WinApi приложениях никуда не исчезла, те что не мерцают — сами буферизируют отрисовку или вообще используют один из фреймворков для GUI.
                                                                                              +8
                                                                                              А если моргать синхронно с мерцанием, то мерцание вообще не увидеть =)
                                                                                                +1
                                                                                                Главное не перепутать фазу моргания — иначе окно будет вообще пропадать.
                                                                                                0
                                                                                                Поднадоели исходники С# и C++ в функциональном стиле, как приятно видеть старый добрый Паскаль, только из-за этого прочёл всю статью, да и в общем-то актуальная тема, но не хватает гифок. Ещё бы с Russian AI CUP какой-нибудь Дельфист выложил мемуары, вообще было бы шикарное начало года.
                                                                                                  0
                                                                                                  Присоединяюсь
                                                                                                  +3
                                                                                                  Очень хотелось почитать какие-нить свежие статьи по free-pascal или delphi, но устранением мерцания в VCL я занимался лет 18-20 назад. Неужели ничего нового с тех пор не возникло?
                                                                                                    0
                                                                                                    Проблема мерцания и прозрачности все еще актуальна, по причине актуальности VCL, который базируется на довольно консервативном WinApi.
                                                                                                    Новое есть — если про VCL, то теперь есть поддержка юникода, скинов.
                                                                                                    Появился новый интерфейсный фреймворк — FMX, а с ним и поддержка MacOS, Android, iOS.
                                                                                                    В следующем релизе будет серверный Linux.
                                                                                                    Ну и кончено же в языке много изменений произошло.
                                                                                                    0
                                                                                                    Если позволите, небольшое замечание по имени переменной
                                                                                                    Класс имеет свойство BufferedChildrens

                                                                                                    Используйте Childs или Children (а еще лучше глагол: BufferChilds). Слова Childrens не существует, ибо Children — уже множественное число.
                                                                                                      0
                                                                                                      Да, есть ошибка, знаю, но исправить «легко и просто» нельзя к сожалению, это сломает много кода(не моего).
                                                                                                      Но я знаю как можно в течении времени исправить, добавив правильный «двойник» свойства, скрыть в дизайнере неправильное свойство через ToolsApi и т.д., при этом из dfm будет считываться неверное свойство, а записываться будет верное свойство. Если возможно будет исправить не сломав совместимость, то будет исправлено.
                                                                                                      +1
                                                                                                      Небольшое отступление. В MS видимо в курсе проблемы, но для приложений, использующих классический API (в т.ч. это большинство системных) видимо нормального решения нет. Именно по-этому для классов ListView и TreeView они добавили встроенную двойную буферизацию, которая работает явно лучше, предложенной в VCL.
                                                                                                      См. TVS_EX_DOUBLEBUFFER и LVS_EX_DOUBLEBUFFER.
                                                                                                        0
                                                                                                        Согласен, у меня в EsVclComponents есть модуль специальный, который включает «родную» буферизацию для TListView: ES.VclFix.pas
                                                                                                        Его достаточно подключить в файле с формой :)
                                                                                                        +3
                                                                                                        {$ifdef VER210UP} {$REGION 'BACKUP'}
                                                                                                        (*
                                                                                                        // Main magic located here:
                                                                                                        procedure TESCustomControl.PaintWindow(DC: HDC);
                                                                                                        var
                                                                                                          BufferDC, TempDC: HDC;
                                                                                                          BufferBitMap: HBITMAP;
                                                                                                          UpdateRect: TRect;
                                                                                                          SaveViewport: TPoint;
                                                                                                          Region: HRGN;
                                                                                                        begin

                                                                                                        Зачем вам система контроля версий, если вы продолжаете хранить полуразложившиеся трупы в комментариях?

                                                                                                          +2

                                                                                                          (я не автор поста) Иногда удаленный код удобно оставлять в комментариях, чтобы видеть его происхождение через blame/diff. Иначе отследить, что тут когда-то давно был какой-то код, довольно сложно

                                                                                                            +3
                                                                                                            Ну потому что если код может вызвать вопросы «откуда это произошло», ответ лучше оставить рядом с кодом, а не заставлять каждого вопрошающего заниматься прикладной археологией в системе контроля версий.
                                                                                                              –1
                                                                                                              Ответ уже написали сверху.
                                                                                                              0

                                                                                                              На сколько я помню всё можно было буферизировать кроме richedit эта редиска плевать хотела на dc который ей передают и вместо текста получалась дырка от бублика. Вы смогли это победить?

                                                                                                                0
                                                                                                                К сожалению RICHEDIT не победить.
                                                                                                                  0
                                                                                                                  Вот и я своё время не смог, и сделал свой редактор текста с подсветкой синтаксиса… Ричэдит страшная штука.
                                                                                                                    +1
                                                                                                                    Кроме всего прочего он имеет много версий, поведение которых немного разное :/
                                                                                                                –1
                                                                                                                Иногда использую вариант такой
                                                                                                                memo1.visible.false
                                                                                                                // много изменений в сожержимом
                                                                                                                memo1.visible.true

                                                                                                                нет ни мерцания, и задержка на обращение к компоненту снижается по времени раз в 10, придумал такое «методом тыка». Может кому-то пригодится тоже.
                                                                                                                  +2
                                                                                                                  Настоятельно не рекомендую пользоваться таким способом, вам «повезло» что он работает, но нет никаких гарантий что не сломается или не даст необычные глюки.
                                                                                                                  Используйте:
                                                                                                                    Memo1.Lines.BeginUpdate;
                                                                                                                    try
                                                                                                                      // изменения
                                                                                                                    finally
                                                                                                                      Memo1.Lines.EndUpdate;
                                                                                                                    end;
                                                                                                                  
                                                                                                                    –1
                                                                                                                    Да, ваш метод лучше. Глюки могут быть, если задержать процесс обновления, компонент просто исчезнет с экрана. Например, при обращении к БД. Только для быстрых изменений, добавления уже готовых данных.
                                                                                                                    +2
                                                                                                                    Специально попробовал ваш способ (думал магия есть).
                                                                                                                    Мерцания пропало (как и ожидалось), но теперь получился просто исчезающий и появляющийся компонент.
                                                                                                                    По мне так лучше будет мерцание, чем моргание всем компонентом ))
                                                                                                                      –1
                                                                                                                      В моем случае, он не успевает моргать, и время процессора не тратится на перерисовку, пример
                                                                                                                      memo1.visible.false
                                                                                                                      memo1.lines.add('test'); // много изменений
                                                                                                                      memo1.clear;
                                                                                                                      memo1.lines.add('test');
                                                                                                                      memo1.lines.add('test');
                                                                                                                      memo1.visible.true

                                                                                                                      Но, конечно, метод с BeginUpdate делает всё это лучше.
                                                                                                                    +1
                                                                                                                    Спасибо за статью! Интересный подход.
                                                                                                                      +2
                                                                                                                      Ого! Вот это зачет!
                                                                                                                      Помню, сколько я часов провел на форумах, применяя всякие уловки против мерцания… Честно признаюсь, я в то время так и не нашел годного решения. Потому данная статья вызвала лично мне приятную ностальгию, а автору — реальное уважение.

                                                                                                                      Я когда-то около трех лет работал с VCL, правда на CBuilder. Задачей было организовать автоматизацию с красивым интерфейсом быстрыми темпами в одном банке. На то время лучшего решения чем Delphi5/CBuilder5 (кому что нравилось) попросту не было. Вот, столько лет прошло, а наши программы до сих пор нормально и успешно там работают без всякого саппорта.

                                                                                                                      Теперь же, работая чуть в другой сфере, отойдя от оконных приложений, мне лично, особенно когда надо «нафигачить что-то под винду с окнами побырику» кроме RADStudio я ничего не хочу знать, он меня устраивает всем. На CBuilder, к примеру, я пишу редактор уровней для своего движочка. Правда, чтобы избавиться от мерцания основного поля, я полностью отказался от TCanvas в его классическом применении, рендерю в него прямо OpenGL контекст.
                                                                                                                        +2
                                                                                                                        Спасибо за статью. У меня были свои методы борьбы с таким, но как правило с собственными контролами. Стандартные почти всегда работали вполне приемлемо и так. А сейчас я вообще в делфи ушел в геймдев, поэтому совсем неактуально стало. Но всё-равно любопытно.
                                                                                                                          +2
                                                                                                                          Спасибо за статью, часто борюсь с тем или иным мерцанием в своих компонентах. Какие только пути не изобретал.
                                                                                                                            +1
                                                                                                                            Спасибо за статью.
                                                                                                                              +1
                                                                                                                              Присоединяюсь, спасибо за статью и компоненты.
                                                                                                                              Уже использую в проекте, обнаружил непонятное поведение TEsLayout при использовании тем.
                                                                                                                              Пишу в личку.
                                                                                                                                +3
                                                                                                                                Upd.
                                                                                                                                Проблема оказалась в моем коде, компонент работает как ожидается.
                                                                                                                                Петр, спасибо!
                                                                                                                                +1
                                                                                                                                Почему то все статьи об Delphi вызывают кучу негатива и много холивара. Во всяком случае Delphi просто инструмент, который позволяет быстро реализовать приложение, и не важно будет это Delphi, Visual Studio или XCode — все равно в итоге это будет бинарник. Я думаю, что каждый пользуется тем, чем ему удобнее и он лучше ориентируется в языке и среде.
                                                                                                                                  +3
                                                                                                                                  А разгадка проста — людям завидно, что вместо Делфи им приходится работать на том, что ранее выбрали 'эффективные менеджеры' за них в качестве рабочего инструмента:

                                                                                                                                  Поднадоели исходники С# и C++ в функциональном стиле, как приятно видеть старый добрый Паскаль

                                                                                                                                  При этом кто-то продолжает спокойно, в своё удовольствие, работать с полюбившимся инструментом, не смотря на то, что его активно постоянно поливали и поливают грязью. Зарабатывая при этом достаточно, что бы выкупить землю в центре областного города и построить там частный дом. Завидуйте дальше :)
                                                                                                                                    0
                                                                                                                                    Я уже примерно 15 лет слышу о смерти Delphi…
                                                                                                                                      0
                                                                                                                                      Пффф, когда я только начал изучать Delphi — он уже был «мертвым» ))
                                                                                                                                    0
                                                                                                                                    А мне нравится, что Delphi вызывает холивар — значит он до сих пор интересен людям и многие о нем знают. Некоторые и споривших даже пробуют Delphi, чтобы действительно понять.
                                                                                                                                      0
                                                                                                                                      Это все напоминает религиозные войны…

                                                                                                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                                                                  Самое читаемое