0

We require to replace the blue highlight when we scroll through the items in combo box.

How can we do it?

I tried a sample code to handle the border of the combo drop down, The below is my sample application code.

unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters,
  cxContainer, cxEdit, cxTextEdit, cxMaskEdit, cxDropDownEdit, CXSComboBox, Vcl.StdCtrls;

 type
 TMycxComboBox = class (PDMCXSComboBox)
  protected
    procedure Loaded; override;
  end;

  PDMCXSComboBox = class (TMycxComboBox);


type
  TForm1 = class(TForm)
    cmbSelectionList: PDMCXSComboBox;
    cxComboBox1: TcxComboBox;
    procedure cmbSelectionListPropertiesInitPopup(Sender: TObject);
    procedure cmbSelectionListPropertiesPopup(Sender: TObject);
    procedure cmbSelectionListPropertiesDrawItem(AControl: TcxCustomComboBox; ACanvas: TcxCanvas; AIndex: Integer; const ARect: TRect; AState: TOwnerDrawState);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure DoCustomDrawBorder(AViewInfo: TcxContainerViewInfo; ACanvas: TcxCanvas; const R: TRect; var AHandled: Boolean;
      out ABorderWidth: Integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TMycxComboBox.Loaded;
begin
  inherited;
  ContentParams.Offsets.Left := 20;
end;

procedure TForm1.cmbSelectionListPropertiesDrawItem(AControl: TcxCustomComboBox; ACanvas: TcxCanvas; AIndex: Integer; const ARect: TRect; AState: TOwnerDrawState);
Var
  AFlags : LongInt;
  R : TRect;
begin
  if (Integer(AIndex) >= 0) and (odSelected in AState) then
  begin
    //ACanvas.Brush.Color := clRed;
    //ACanvas.Font.Color := clHighlightText;
    ACanvas.Brush.Color := clWhite;// $00E5DFD7; //clRed
    ACanvas.Font.Color := $006c4e1f;
  end;
  ACanvas.FillRect(ARect);
  if (AIndex >= 0) and (AIndex < AControl.Properties.Items.Count) then
  begin
    R := ARect;
    AFlags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
    if not UseRightToLeftAlignment then
      Inc(R.Left,2)
    else
      Dec(R.Right,2);

    with AControl.Properties do
      DrawText(ACanvas.Handle,PChar(Items[AIndex]),Length(Items[AIndex]), R, AFlags);
  end;
end;

procedure TForm1.cmbSelectionListPropertiesInitPopup(Sender: TObject);
begin
  TcxComboBoxPopupWindow(PDMCXSComboBox(Sender).PopupWindow).ViewInfo.OnCustomDrawBorder := DoCustomDrawBorder;
end;

procedure TForm1.cmbSelectionListPropertiesPopup(Sender: TObject);
begin
TcxComboBoxListBox(PDMCXSComboBox(Sender).ILookupData.ActiveControl).Color := clWhite;
end;

procedure TForm1.DoCustomDrawBorder(AViewInfo: TcxContainerViewInfo;
  ACanvas: TcxCanvas; const R: TRect; var AHandled: Boolean;
  out ABorderWidth: Integer);
begin
  AHandled := True;
  ABorderWidth := 1;
  ACanvas.FrameRect(R, $00E5DFD7);
end;

end.

PDMCXSComboBox is my custom component derived from tcxcombobox.

Also for this combobox when i go through the item selection the top and right border is being invisible:

enter image description here

Component code is below:

unit CXSComboBox;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, cxControls, cxContainer, cxEdit, cxTextEdit,
  cxMaskEdit, cxDropDownEdit, cxGraphics, Winapi.Messages, Winapi.Windows, OvcCmbx;// OvcTccbx;

const
  ParamsLabel = 'Params: ';
  SQLLabel = 'SQL: ';

type
  TpMyKeyValue = ^TrMykeyValue;
  TrMyKeyValue= record
    key: string;
    value: string;
  end;

  TKeyValueShowOption = (soShowValueOnly, soShowKeyOnly, soShowAll);

type
  PDMCXSComboBox = class(TcxComboBox)
  private
    { Private declarations }
    FItemHeight: Integer;
    FOnOwnDrawItem: TcxEditDrawItemEvent;
    FOnPdmDrawItem: TcxEditDrawItemEvent;
    StringListKey: TStringList;
    StringListKey_SACOMBO: TStringList;
    StringListOKey: TStringList;
    FOldWndProc: pointer;
    FHintWin : THintWindow;
    FHintList : TStrings;
    FAutoWidthDropDown:Boolean;
    FShowItemsHint:Boolean;
    FListHandle: HWND;
    FUseDefaultValue: Boolean;
    FDefaultValue: String;
    FKeyValuePairs: Boolean;
    FKeyValueShowOption: TKeyValueShowOption;
    procedure CMFontChanged(var Message: TWMFontChange); message CM_FONTCHANGED;
    procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
    procedure AdjustHeight;
    procedure SetSelKey(const Value: string);

    function  GetListIndex: Integer;
    procedure SetListIndex(Value: Integer);

    procedure SetKeyValuePairs(const Value: Boolean);
    function GetText_PDMADVCOMBO: string;
    procedure SetKeyValueShowOption(const Value: TKeyValueShowOption);
  protected
    { Protected declarations }
    FMRUList             : TOvcMRUList;
    FList                : TStringList;
    FListIndex : Integer;      {ItemIndex sans MRU Items}

    function GetItemHt: Integer; //override;
    procedure Loaded; override;
    //procedure Loaded_PDMCOMBO;
    procedure OwnDrawItem(Control: TcxCustomComboBox; Canvas:TCxCanvas; Index: Integer; const Rect: TRect; State: TOwnerDrawState);
    property OnOwnDrawItem: TcxEditDrawItemEvent read FOnOwnDrawItem write FOnOwnDrawItem;

    procedure PdmDrawItem(AControl: TcxCustomComboBox; ACanvas: TcxCanvas; AIndex: Integer; const ARect: TRect; AState: TOwnerDrawState);
    property OnPdmDrawItem: TCxEditDrawItemEvent read FOnPdmDrawItem write FOnPdmDrawItem;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

  published
    { Published declarations }
    property Value: string read GetSelKey write SetSelKey;
    property DefaultValue: string read FDefaultValue write FDefaultValue;
    property UseDefaultValue: boolean read FUseDefaultValue write FUseDefaultValue;
    property HintList: TStrings read FHintList write SetHintList;
    property ListHandle: HWND read FListHandle write FListHandle;
    property AutoWidthDropDown:Boolean read FAutoWidthDropDown write FAutoWidthDropDown;
    property ShowItemsHint:Boolean read FShowItemsHint write FShowItemsHint;
    property Align;
    property KeyValuePairs: Boolean read FKeyValuePairs write SetKeyValuePairs default True;
    property KeyValueShowOption: TKeyValueShowOption read FKeyValueShowOption write SetKeyValueShowOption default soShowValueOnly;
    property TextPDMADVCOMBO: string read GetText_PDMADVCOMBO;
  end;

  function CompareValueinMyKeyValue(item1, item2:TpMyKeyValue): Integer;
  procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('PDMCXComponents', [PDMCXSComboBox]);
end;

function CompareValueinMyKeyValue(item1,item2:TpMyKeyValue):integer;
begin
  Result := CompareText(item1.value,item2.value);
end;

procedure PDMCXSComboBox.Loaded;
begin
  inherited;
  if not (csDesigning in ComponentState) then
  begin
    // chain OnDraw events
    OnOwnDrawItem := Properties.OnDrawItem;
    Properties.OnDrawItem := OwnDrawItem;

    // chain OnDraw events
    FOnPdmDrawItem := properties.OnDrawItem;
    properties.OnDrawItem := PdmDrawItem;
  end;
end;

{procedure PDMCXSComboBox.Loaded_PDMCOMBO;
begin
  inherited;
  if not (csDesigning in ComponentState) then
  begin
    // chain OnDraw events
    FOnPdmDrawItem := properties.OnDrawItem;
    properties.OnDrawItem := PdmDrawItem;
  end;
end; }

procedure PDMCXSComboBox.OwnDrawItem(Control: TcxCustomComboBox; Canvas:TCxCanvas; Index: Integer;
  const Rect: TRect; State: TOwnerDrawState);
var
  LCanvas: TcxCanvas;
  S: string;
  lRect : TRect;
begin
  lRect := Rect;
  if not (csDestroying in ComponentState) then
  begin
    LCanvas := (Control as PDMCXSComboBox).Canvas;
    LCanvas.FillRect(lRect);
    S := PDMCXSComboBox(Control).Properties.Items[Index];
    InflateRect(lRect, -2, -1);
    DrawText(LCanvas.Handle, S, Length(S), lRect,
      DT_LEFT or DT_NOCLIP or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX);

    // call chained event handler
    if Assigned(OnOwnDrawItem) then
      OnOwnDrawItem(Control,LCanvas, Index, lRect, State);
  end;
end;

procedure PDMCXSComboBox.PdmDrawItem(AControl: TcxCustomComboBox;
  ACanvas: TcxCanvas; AIndex: Integer; const ARect: TRect;
  AState: TOwnerDrawState);
var
  S: string;
  lRect   : Trect;
begin
  if not (csDestroying in ComponentState) then
  begin
    lRect := Arect;
    ACanvas.FillRect(lRect);
    S := PDMCXSComboBox(AControl).properties.Items[AIndex];
    InflateRect(lRect, -2, -1);
    DrawText(ACanvas.Handle, S, Length(S), lRect, DT_LEFT or DT_NOCLIP or DT_VCENTER or DT_SINGLELINE);
     // maybe developer need another drawing

    if Assigned(OnPdmDrawItem) then
      OnPdmDrawItem(AControl,Acanvas, AIndex, lRect, AState);
  end;
end;


constructor PDMCXSComboBox.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  StringListKey := TStringList.Create;
  StringListKey_SACOMBO := TStringList.Create;
  StringListOKey := TStringList.Create;
  FHintList := TStringList.Create;
  FHintWin := THintWindow.Create(Self);
  autosize := false;
  Height := 30;
  properties.DropDownwidth := 0;
  //Properties.ButtonGlyph.LoadFromFile('.\DROPDOWNICON.png');
  style.Font.Size := 10;
  style.Font.Name := 'Noto sans';
  style.Font.Color := $00757575;
  style.BorderStyle := ebssingle;
  style.ButtonStyle := btsSimple;
  style.BorderColor := $00e5dfd7;
  style.LookAndFeel.NativeStyle:= false;
end;

destructor PDMCXSComboBox.Destroy;
begin
  StringListKey.Free;
  StringListOKey.Free;
  FHintList.Free;
  if Assigned(FHintWin) then
    FHintWin.ReleaseHandle;
  if FListHandle <> 0 then
    SetWindowLong(FListHandle, GWL_WNDPROC, longint(FOldWndProc));
  inherited Destroy;
end;

procedure PDMCXSComboBox.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.Style := Params.Style or CBS_OWNERDRAWVARIABLE;
end;

procedure PDMCXSComboBox.AdjustHeight;
var
  I: Integer;
  DC: HDC;
  SaveFont: HFont;
  SysMetrics, Metrics: TTextMetric;
begin
  DC := GetDC(0);
  try
    GetTextMetrics(DC, SysMetrics);
    SaveFont := SelectObject(DC, Font.Handle);
    GetTextMetrics(DC, Metrics);
    SelectObject(DC, SaveFont);
  finally
    ReleaseDC(0, DC);
  end;
  if NewStyleControls then
  begin
    if Ctl3D then I := 4 else I := 2;
    I := GetSystemMetrics(SM_CYBORDER) * I;
  end else
  begin
    I := SysMetrics.tmHeight;
    if I > Metrics.tmHeight then I := Metrics.tmHeight;
    I := I div 4 + GetSystemMetrics(SM_CYBORDER) * 4;
  end;
  FItemHeight := Metrics.tmHeight + I;
end;

procedure PDMCXSComboBox.CMFontChanged(var Message: TWMFontChange);
begin
  AdjustHeight;
  RecreateWnd;
end;

procedure PDMCXSComboBox.CNMeasureItem(var Message: TWMMeasureItem);
begin
  Message.MeasureItemStruct.itemHeight :=  FItemHeight;// Properties.ItemHeight;
end;

end.
userhi
  • 553
  • 1
  • 7
  • 27
  • 2
    If the selected item is not visible, then how does the user know which item is going to be selected when they click in the dropdown area? – Freddie Bell Apr 15 '19 at 06:12
  • Sounds like a feature my users would hate, why do you need this ? – GuidoG Apr 15 '19 at 06:22
  • @GuidoG That is a requirement to be achieved. – userhi Apr 15 '19 at 06:30
  • correct me if i am wrong, but by making the highlight color clwhite, the user will not be able to see which is selected. Also this differs from your original question where you want it erradicated – GuidoG Apr 15 '19 at 06:32
  • 2
    Its not because something is a requirement, that it is therefore a good idea. This does not answers my question. How will your users know which is selected ? – GuidoG Apr 15 '19 at 06:34
  • Inplace of clWhite can we keep some other color which users can see. – userhi Apr 15 '19 at 06:52
  • How about blue? – Freddie Bell Apr 15 '19 at 08:12
  • I want to use $00E5DFD7 this color, I had tried using OnDrawItem, – userhi Apr 15 '19 at 08:48
  • To use `OnDrawItem` event is correct in the `VCL` component, and it works very well. I just tried it quickly, but I don't know about DevEx components. So what does your code and result look like? Please edit your question to include that information. – Tom Brunberg Apr 15 '19 at 09:21
  • @TomBrunberg , I have updated my question, Please check – userhi Apr 15 '19 at 09:42
  • I don't think this is easily possible - see this thread on the Devex site and the reason stated: https://www.devexpress.com/Support/Center/Question/Details/Q312445/selection-color. Btw your q is badly phrased - "eradicate" means to remove completely, whereas from what you've said in comments it sounds more like you want to replace it. – MartynA Apr 15 '19 at 10:43
  • Does the `DevEx` combo or your derivative have a property `Style`? have you set it to `csOwnerDrawFixed`? – Tom Brunberg Apr 15 '19 at 10:45
  • @TomBrunberg That property is not available. I have the code to implement it on cxcomboBox, But that is not working for my custom combobox which is derived from cxcombobox. – userhi Apr 15 '19 at 10:59
  • Ok, that is then the reason for the problem. Can you consider using the standard `TComboBox`, in which case there would be no such error? As I don't have the DevEx component, I can't suggest a fix, also not because of the discussion @MartynA referred to. – Tom Brunberg Apr 15 '19 at 11:16
  • @TomBrunberg: Afaics from the source, the TcxComboBox hosts a separate Devex control to provide its listbox. I dare say that if one could correctly navigate the maze of classes to zero in on the right one (or just inspect it in the debugger), it *may* be possible to implement owner-drawing for that. – MartynA Apr 15 '19 at 11:31

1 Answers1

0

I have fixed the issue and it is working as expected. The only thing i did is I have handled OnDrawItem twice, So I have to disable this at component designing.

unit CXSComboBox;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, cxControls, cxContainer, cxEdit, cxTextEdit,
  cxMaskEdit, cxDropDownEdit, cxGraphics, Winapi.Messages, Winapi.Windows, OvcCmbx;// OvcTccbx;

const
  ParamsLabel = 'Params: ';
  SQLLabel = 'SQL: ';

type
  TpMyKeyValue = ^TrMykeyValue;
  TrMyKeyValue= record
    key: string;
    value: string;
  end;

  TKeyValueShowOption = (soShowValueOnly, soShowKeyOnly, soShowAll);

type
  PDMCXSComboBox = class(TcxComboBox)
  private
    { Private declarations }
    FItemHeight: Integer;
    StringListKey: TStringList;
    StringListKey_SACOMBO: TStringList;
    StringListOKey: TStringList;
    FOldWndProc: pointer;
    FHintWin : THintWindow;
    FHintList : TStrings;
    FAutoWidthDropDown:Boolean;
    FShowItemsHint:Boolean;
    FListHandle: HWND;
    FUseDefaultValue: Boolean;
    FDefaultValue: String;
    FKeyValuePairs: Boolean;
    FKeyValueShowOption: TKeyValueShowOption;
    procedure CMFontChanged(var Message: TWMFontChange); message CM_FONTCHANGED;
    procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
    procedure AdjustHeight;
    procedure SetSelKey(const Value: string);

    function  GetListIndex: Integer;
    procedure SetListIndex(Value: Integer);

    procedure SetKeyValuePairs(const Value: Boolean);
    function GetText_PDMADVCOMBO: string;
    procedure SetKeyValueShowOption(const Value: TKeyValueShowOption);
  protected
    { Protected declarations }
    FMRUList             : TOvcMRUList;
    FList                : TStringList;
    FListIndex : Integer;      {ItemIndex sans MRU Items}

    function GetItemHt: Integer; //override;

  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

  published
    { Published declarations }
    property Value: string read GetSelKey write SetSelKey;
    property DefaultValue: string read FDefaultValue write FDefaultValue;
    property UseDefaultValue: boolean read FUseDefaultValue write FUseDefaultValue;
    property HintList: TStrings read FHintList write SetHintList;
    property ListHandle: HWND read FListHandle write FListHandle;
    property AutoWidthDropDown:Boolean read FAutoWidthDropDown write FAutoWidthDropDown;
    property ShowItemsHint:Boolean read FShowItemsHint write FShowItemsHint;
    property Align;
    property KeyValuePairs: Boolean read FKeyValuePairs write SetKeyValuePairs default True;
    property KeyValueShowOption: TKeyValueShowOption read FKeyValueShowOption write SetKeyValueShowOption default soShowValueOnly;
    property TextPDMADVCOMBO: string read GetText_PDMADVCOMBO;
  end;

  function CompareValueinMyKeyValue(item1, item2:TpMyKeyValue): Integer;
  procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('PDMCXComponents', [PDMCXSComboBox]);
end;

function CompareValueinMyKeyValue(item1,item2:TpMyKeyValue):integer;
begin
  Result := CompareText(item1.value,item2.value);
end;


constructor PDMCXSComboBox.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  StringListKey := TStringList.Create;
  StringListKey_SACOMBO := TStringList.Create;
  StringListOKey := TStringList.Create;
  FHintList := TStringList.Create;
  FHintWin := THintWindow.Create(Self);
  autosize := false;
  Height := 30;
  properties.DropDownwidth := 0;
  //Properties.ButtonGlyph.LoadFromFile('.\DROPDOWNICON.png');
  style.Font.Size := 10;
  style.Font.Name := 'Noto sans';
  style.Font.Color := $00757575;
  style.BorderStyle := ebssingle;
  style.ButtonStyle := btsSimple;
  style.BorderColor := $00e5dfd7;
  style.LookAndFeel.NativeStyle:= false;
end;

destructor PDMCXSComboBox.Destroy;
begin
  StringListKey.Free;
  StringListOKey.Free;
  FHintList.Free;
  if Assigned(FHintWin) then
    FHintWin.ReleaseHandle;
  if FListHandle <> 0 then
    SetWindowLong(FListHandle, GWL_WNDPROC, longint(FOldWndProc));
  inherited Destroy;
end;

procedure PDMCXSComboBox.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.Style := Params.Style or CBS_OWNERDRAWVARIABLE;
end;

procedure PDMCXSComboBox.AdjustHeight;
var
  I: Integer;
  DC: HDC;
  SaveFont: HFont;
  SysMetrics, Metrics: TTextMetric;
begin
  DC := GetDC(0);
  try
    GetTextMetrics(DC, SysMetrics);
    SaveFont := SelectObject(DC, Font.Handle);
    GetTextMetrics(DC, Metrics);
    SelectObject(DC, SaveFont);
  finally
    ReleaseDC(0, DC);
  end;
  if NewStyleControls then
  begin
    if Ctl3D then I := 4 else I := 2;
    I := GetSystemMetrics(SM_CYBORDER) * I;
  end else
  begin
    I := SysMetrics.tmHeight;
    if I > Metrics.tmHeight then I := Metrics.tmHeight;
    I := I div 4 + GetSystemMetrics(SM_CYBORDER) * 4;
  end;
  FItemHeight := Metrics.tmHeight + I;
end;

procedure PDMCXSComboBox.CMFontChanged(var Message: TWMFontChange);
begin
  AdjustHeight;
  RecreateWnd;
end;

procedure PDMCXSComboBox.CNMeasureItem(var Message: TWMMeasureItem);
begin
  Message.MeasureItemStruct.itemHeight :=  FItemHeight;// Properties.ItemHeight;
end;

end.
userhi
  • 553
  • 1
  • 7
  • 27
  • 1
    Why does your answer consist of mostly code that has been commented out, including all of your implementations of OnDrawItem? It is useless code, and shouldn't be in the answer. – Ken White Apr 16 '19 at 12:51
  • 1
    In addition to what @KenWhite says, to be of any use to future readers (which is the main point of posting answers on SO), your answer should explain exactly how and why this avoids the problem specifically stated in your q. – MartynA Apr 16 '19 at 20:38