Конференция "Компоненты" » Требуется компонент - подобие TEdit [D7, WinXP]
 
  • R1ka (29.04.08 20:23) [0]
    Требуется компонент - подобие TEdit, но с градиентным фоном, вопросы такие:
    какой класс наследовать ? (Tedit, TCustomEdit, TWinControl)
    какие методы перекрывать ?
    в каком методе производить отрисовку градиента и на какой поверхности?
    как быть со стандартным св-вом Color?
    то что понадобятся три новых свойства уже понял и реализовал, вообще буду благодарен любой информации в рамках этой темы.
  • DVM © (29.04.08 22:59) [1]

    > какой класс наследовать ?

    TWinControl круто будет слишком, проще Tedit.


    > какие методы перекрывать ?


    > в каком методе производить отрисовку градиента и на какой
    > поверхности?

    Написать свой обработчик для WM_ERASEBKGND. Возможно, что и для WM_PAINT тоже понадобится.


    > как быть со стандартным св-вом Color?

    Никак. Использовать цвет или не использовать.
  • {RASkov} © (29.04.08 23:17) [2]
    > > как быть со стандартным св-вом Color?
    >
    > Никак. Использовать цвет или не использовать.

    Можно его использовать для одного из градиентного цвета...
    Съэкономим 4 байта...

     TGradEdit = class(TCustomEdit)
     private
       FSecondColor: TColor;
       procedure SetSecondColor(const Value: TColor);
       function GetFirstColor: TColor;
       procedure SetFirstColor(const Value: TColor);
     published
       property FirstColor: TColor read GetFirstColor write SetFirstColor;
       property SecondColor: TColor read FSecondColor write SetSecondColor;
     end;
    .....
    function TGradEdit.GetFirstColor: TColor;
    begin
     Result:=Color;
    end;

    procedure TGradEdit.SetFirstColor(const Value: TColor);
    begin
     if Color<>Value then Color := Value;
    ....
    end;

    procedure TGradEdit.SetSecondColor(const Value: TColor);
    begin
     if FSecondColor <> Value then FSecondColor := Value;
    ....
    end;

  • Игорь Шевченко © (30.04.08 11:19) [3]
    замучаешься Edit переписывать, ищи готовое решение. Я вполне серьезно
  • R1ka © (30.04.08 12:52) [4]
    мне для моей проги не только един нужен, писать все-равно придется
  • R1ka © (30.04.08 20:57) [5]
    Все сделал как сказали:
    Создал буфер для градиента, при создании заполняю его.
    Перекрыл WM_ERASEBKGND и WM_PAINT, в них заменил заполнение цветом на заполнение моим рисунком, все собирается и ставится. Но фона почему-то нету О_О


    // Заполнение буфера-битмапа градиентом
    procedure TMRAEdit.FillBuffer;
    var
     i: integer;
     Deltas: Array [0 .. 2] of real;
    begin
     Deltas[0] := (GetRValue(FBgColorEnd) - GetRValue(FBgColorStart)) / FBGBuffer.Height;
     Deltas[1] := (GetGValue(FBgColorEnd) - GetGValue(FBgColorStart)) / FBGBuffer.Height;
     Deltas[2] := (GetBValue(FBgColorEnd) - GetBValue(FBgColorStart)) / FBGBuffer.Height;

     for i := 0 to FBGBuffer.Height - 1 do
     begin
       FBGBuffer.Canvas.MoveTo(0, i);
       FBGBuffer.Canvas.Pen.Color := RGB(
         Round(GetRValue(FBgColorStart) + I * Deltas[0]),
         Round(GetGValue(FBgColorStart) + I * Deltas[1]),
         Round(GetBValue(FBgColorStart) + I * Deltas[2]));
       FBGBuffer.Canvas.LineTo(FBGBuffer.Width, i);
     end;
    end;

    procedure TMRAEdit.WMEraseBkgnd(var Message: TWmEraseBkgnd);
    begin
     with ThemeServices do
     if ThemesEnabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
       begin
         { Get the parent to draw its background into the control's background. }
         DrawParentBackground(Handle, Message.DC, nil, False);
       end
       else
       begin
         { Only erase background if we're not doublebuffering or painting to memory. }

         if not FDoubleBuffered or
            (TMessage(Message).wParam = TMessage(Message).lParam) then
           //Тут было заполнение пустым цветом
           BitBlt(Message.DC, 0, 0, ClientRect.Right, ClientRect.Bottom, FBGBuffer.Canvas.Handle, 0, 0, SRCCOPY);
       end;

     Message.Result := 1;
    end;

    procedure TMRAEdit.WMPaint(var Message: TWMPaint);
    var
     DC, MemDC: HDC;
     MemBitmap, OldBitmap: HBITMAP;
     PS: TPaintStruct;
    begin
     if not FDoubleBuffered or (Message.DC <> 0) then
     begin
       if not (csCustomPaint in ControlState) and (ControlCount = 0) then
         inherited
       else
         PaintHandler(Message);
     end
     else
     begin
       DC := GetDC(0);
       MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
       ReleaseDC(0, DC);
       MemDC := CreateCompatibleDC(0);
       OldBitmap := SelectObject(MemDC, MemBitmap);
       try
         DC := BeginPaint(Handle, PS);
         Perform(WM_ERASEBKGND, MemDC, MemDC);
         Message.DC := MemDC;
         WMPaint(Message);
         Message.DC := 0;
         // Тут подменил источник копирования на свой буфер
         BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, FBGBuffer.Canvas.Handle, 0, 0, SRCCOPY);
         EndPaint(Handle, PS);
       finally
         SelectObject(MemDC, OldBitmap);
         DeleteDC(MemDC);
         DeleteObject(MemBitmap);
       end;
     end;
    end;

  • DVM © (30.04.08 22:05) [6]
    Зачем такие выкрутасы в TMRAEdit.WMPaint явно скопированные из TWinControl ?
  • Игорь Шевченко © (30.04.08 22:23) [7]

    > Но фона почему-то нету О_О


    И не будет. WM_Paint надо переписывать не вызывая стандартный.
  • DVM © (30.04.08 22:36) [8]

    > R1ka ©

    Глюкавенько, но работает (в image1 jpeg загружен):


    unit Unit1;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, jpeg, ExtCtrls, ComCtrls, StdCtrls;

    type

     TEdit = class(StdCtrls.TEdit)
     private
       FBitmap: TBitmap;
     protected
       procedure WndProc(var Message: TMessage); override;
       procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
     public
       property Bitmap: TBitmap read FBitmap write FBitmap;
     end;

     TForm1 = class(TForm)
       Image1: TImage;
       Edit1: TEdit;
       procedure FormCreate(Sender: TObject);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;

    implementation

    {$R *.dfm}

    uses CommCtrl;

    procedure TEdit.WndProc(var Message: TMessage);
    begin
     if Message.Msg = WM_ERASEBKGND then
     begin
       Message.Result := 1
     end else
     begin
       case Message.Msg of
         WM_KEYDOWN:
           InvalidateRect(Handle, nil, False);
       end;
       inherited
     end
    end;

    procedure TEdit.WMPaint(var Message: TWMPaint);
    var
     DC, TempDC, TempDC1: HDC;
     TempBmp, OldBmp, OldBmp1, TempBMP1: HBITMAP;
     PS: TPaintStruct;
     TempRect: TRect;
    begin
     if Message.DC <> 0 then
       inherited
     else begin
       TempRect := ClientRect;
       BeginPaint (Handle, PS);
       try
         DC := GetDC(Handle);
         TempDC := CreateCompatibleDC(DC);
         TempDC1 := CreateCompatibleDC(DC);
         TempBMP := CreateCompatibleBitmap(DC, TempRect.Right, TempRect.Bottom);
         TempBMP1 := CreateCompatibleBitmap(DC, TempRect.Right, TempRect.Bottom);
         try
           OldBmp := SelectObject(TempDC, TempBMP);
           OldBmp1 := SelectObject(TempDC1, TempBMP1);
           try
             Message.DC := TempDC;
             inherited;
             Message.DC := 0;
             FillRect(TempDC1, TempRect, GetStockObject(WHITE_BRUSH));
             if FBitmap <> nil then
               BitBlt(TempDC1, 0, 0, FBitmap.Width, FBitmap.Height, FBitmap.Canvas.Handle, 0, 0, SRCCOPY);
             TransparentBlt (TempDC1, 0, 0, TempRect.Right, TempRect.Bottom, TempDC, 0, 0, TempRect.Right, TempRect.Bottom, clWhite);
             BitBlt(DC, 0, 0, TempRect.Right, TempRect.Bottom, TempDC1, 0, 0, SRCCOPY);
           finally
             SelectObject (TempDC, OldBmp);
             SelectObject (TempDC1, OldBmp1)
           end;
         finally
           DeleteObject (TempBMP);
           DeleteObject (TempBMP1);
           ReleaseDC (Handle, TempDC);
           ReleaseDC (Handle, TempDC1);
           ReleaseDC (Handle, DC);
         end;
       finally
         EndPaint (Handle, PS)
       end;
     end
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
     Edit1.Color := clWhite;
     Edit1.Bitmap := TBitmap.Create;
     Edit1.Bitmap.Assign(image1.Picture.Graphic);
    end;

    end.


  • Игорь Шевченко © (30.04.08 22:49) [9]
    Microsoft рекомендует делать примерно так:

    unit main;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, StdCtrls;

    type
     TForm1 = class(TForm)
       EditPlace: TEdit;
     private
       procedure WmCtlColorEdit (var Message: TWMCtlColorEdit); message WM_CTLCOLOREDIT;
     end;

    var
     Form1: TForm1;

    implementation
    uses
     GraphUtil;

    {$R *.dfm}

    procedure TForm1.WmCtlColorEdit(var Message: TWMCtlColorEdit);
    var
     ACanvas: TCanvas;
    begin
     if Message.ChildWnd = EditPlace.Handle then
     begin
       ACanvas := TCanvas.Create;
       try
         ACanvas.Handle := Message.ChildDC;
         GradientFillCanvas (ACanvas, clRed, clGreen, EditPlace.ClientRect,
           gdHorizontal);
       finally
         ACanvas.Handle := 0;
         ACanvas.Free;
       end;
       SetBkMode(Message.ChildDC, TRANSPARENT);
       Message.Result := GetStockObject(NULL_BRUSH);
     end;
    end;

    end.

  • DVM © (30.04.08 22:58) [10]

    > Игорь Шевченко ©   (30.04.08 22:49) [9]

    тоже подглюкивает при вводе текста, буквы пропадают и шрифт странный стал.
  • Игорь Шевченко © (30.04.08 23:14) [11]
    DVM ©   (30.04.08 22:58) [10]

    Шрифт изменился, а пропажи букв не заметил. Пропал горизонтальный скорллинг. Но я к чему - к тому, что для полной функциональности надо самому заниматься отрисовкой.
  • R1ka © (30.04.08 23:32) [12]
    Спасибо, принцип понятен, только какой смысл в этом условии?

    if Message.DC <> 0 then
       inherited

  • DVM © (30.04.08 23:59) [13]

    > R1ka ©   (30.04.08 23:32) [12]
    > Спасибо, принцип понятен, только какой смысл в этом условии?
    >
    >
    > if Message.DC <> 0 then
    >    inherited


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


    > try
    >          Message.DC := TempDC;
    >          inherited;
    >          Message.DC := 0;
  • R1ka © (01.05.08 04:38) [14]
    Так, а где отрисовывать если у меня отключены стили XP?
  • Игорь Шевченко © (01.05.08 10:32) [15]
    DVM ©   (30.04.08 22:58) [10]

    Кстати, проблема с шрифтом и скроллингом решилась добавлением одной строчки:

       SetBkMode(Message.ChildDC, TRANSPARENT);
       SelectObject(Message.ChildDC, EditPlace.Font.Handle);
       Message.Result := GetStockObject(NULL_BRUSH);
  • Игорь Шевченко © (01.05.08 11:13) [16]
    Ну и соответственно, в виде компонента это выходит примерно так:

    unit HsGradientEdit;

    interface
    uses
     Messages, Graphics, Controls, StdCtrls;

    type
     THsGradientEdit = class(TEdit)
     private
       FEndColor: TColor;
       FStartColor: TColor;
       procedure CnCtlColorEdit (var Message: TWMCtlColorEdit); message CN_CTLCOLOREDIT;
       procedure SetEndColor(const Value: TColor);
       procedure SetStartColor(const Value: TColor);
     published
       property StartColor: TColor read FStartColor write SetStartColor;
       property EndColor: TColor read FEndColor write SetEndColor;
     end;

    implementation
    uses
     Windows, HsGradient;

    { THsGradientEdit }

    procedure THsGradientEdit.CnCtlColorEdit(var Message: TWMCtlColorEdit);
    var
     ACanvas: TCanvas;
     GradientFill: THSGradientFill;
    begin
     GradientFill := THSGradientFill.Create;
     try
       GradientFill.StartColor := FStartColor;
       GradientFill.EndColor := FEndColor;
       GradientFill.Style := gsHorizontal;
       ACanvas := TCanvas.Create;
       try
         ACanvas.Handle := Message.ChildDC;
         GradientFill.FillRect(ACanvas, ClientRect);
       finally
         ACanvas.Handle := 0;
         ACanvas.Free;
       end;
     finally
       GradientFill.Free;
     end;
     SetBkMode(Message.ChildDC, TRANSPARENT);
     SelectObject(Message.ChildDC, Font.Handle);
     Message.Result := GetStockObject(NULL_BRUSH);
    end;

    procedure THsGradientEdit.SetEndColor(const Value: TColor);
    begin
     if FEndColor <> Value then
     begin
       FEndColor := Value;
       Invalidate;
     end;
    end;

    procedure THsGradientEdit.SetStartColor(const Value: TColor);
    begin
     if FStartColor <> Value then
     begin
       FStartColor := Value;
       Invalidate;
     end;
    end;

    end.



    Вместо HsGradientEdit и соответствующего класса можно вполне применить метод GradientFillCanvas из GraphUtil.pas, не мой вариант больше подходит, так как более переносим между версиями Delphi
  • R1ka © (01.05.08 13:40) [17]
    HsGradient - это что за юнит такой? Гугл молчит
  • {RASkov} © (01.05.08 13:56) [18]
    > [16] Игорь Шевченко ©   (01.05.08 11:13)

    Красиво, но при вводе текст перед курсором пропадает, от него остается только одна буква.
    TextInEdit - текст находится в эдите
      t|InEdit - при редактировании
    | - курсор.
    [D7, WinXP]
  • {RASkov} © (01.05.08 13:58) [19]
    > [17] R1ka ©   (01.05.08 13:40)

    В кладовке поищи... архив ownerdrawmenu.zip в нем есть два требующихся Игоревых модуля:)
 
Конференция "Компоненты" » Требуется компонент - подобие TEdit [D7, WinXP]
Есть новые Нет новых   [134464   +62][b:0][p:0.006]