• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 478
  • Last Modified:

Editable ListBox

I have a code that allows me to edit in ListBox (actually it temporary paces EditBox in front of ListBox) and I would like to have the same "edit" efect on newly added ListBox item. Click on Add button should add new item and place EditBox in front of it.
I'm new to programming and open for any alternatives.
I use Delphi 7.
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
const
  UM_DESTROYCONTROL = WM_USER +333;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    procedure ListBox1DblClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    procedure eDone(Sender: TObject);
    procedure eMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure eKeyPress(Sender: TObject; var Key: Char);
    procedure UmDestroyControl(Var msg: TMessage);
      message UM_DESTROYCONTROL;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//==============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
  edt: TEdit;
  rect: TRect;
  lbox: TListbox;
begin
  lbox := (sender as TListbox);
  if lbox.ItemIndex < 0 then exit;
  edt:= TEdit.Create(self);
  edt.BorderStyle:= bsNone;
  rect:= lbox.ItemRect( lbox.itemindex );
  rect.topleft := lbox.ClientToScreen( rect.topleft );
  rect.BottomRight := lbox.clienttoscreen( rect.bottomright );
  rect.topleft := ScreenToClient( rect.topleft );
  rect.BottomRight := screenToClient( rect.bottomright );
  edt.text := lbox.Items[lbox.itemindex];
  edt.setbounds( rect.left+2, rect.top, lbox.clientwidth-2, rect.bottom-rect.top);
  edt.OnExit := eDone;
  edt.OnMouseDown:= eMouseDown;
  edt.OnKeyPress:= eKeyPress;
  edt.Parent := Self;
  SetCapturecontrol( edt );
  edt.SetFocus;
end;
//==============================================================================
procedure TForm1.eDone(Sender: TObject);
var
  dlgBtn: Integer;
begin
  if (Sender as TEdit).Text = '' then begin
    ListBox1.Items.Delete(ListBox1.itemindex);
    PostMessage( handle, UM_DESTROYCONTROL, 0, Integer(Sender));
  end else begin
    ListBox1.Items[ ListBox1.itemindex ] := (Sender as TEdit).Text;
    PostMessage(Handle, UM_DESTROYCONTROL, 0, Integer(Sender));
  end;
end;
//==============================================================================
procedure TForm1.eMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if not PtInrect( (Sender as TControl).ClientRect, Point(X,y) ) then eDone(Sender);
end;
//==============================================================================
procedure TForm1.eKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then eDone(Sender);
end;
//==============================================================================
procedure TForm1.UmDestroyControl(var msg: TMessage);
begin
  TObject(msg.lparam).Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('sampleText');
  ListBox1.TopIndex := ListBox1.Items.Count-1;
end;

end.

Open in new window

0
3axap
Asked:
3axap
  • 4
  • 2
  • 2
1 Solution
 
epasquierCommented:
put a list box, a TEdit and a button.
set the TEdit BorderStyle = bsNone

Add an onCreate event for the form to calculate some key values (max elements, drawing offsets)
Add Edit's events :
- onExit : will hide the edit box
- onChange : will update the corresponding listbox item value

Create a new function : ListBoxEdit , parameter i:integer (index of the item to edit). It will :
a) make sure that item is visible and selected
b) put the edit visible and on top of that item, precisely, so that the user can't tell the difference except that it's editable, and of course give it focus

then add a button events to add an item and call the edit function

You're done !
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListBox: TListBox;
    Edit: TEdit;
    Button: TButton;
    procedure ButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure EditExit(Sender: TObject);
    procedure EditChange(Sender: TObject);
  private
    { Déclarations privées }
    LbDrawOffset,NbItemsMax:Integer;

    procedure ListBoxEdit(i:integer);
  public
    { Déclarations publiques }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
Var
 R:TRect;
begin
 // Get maximum of items visible in listbox
 R:=ListBox.ItemRect(0);
 LbDrawOffset:=ListBox.Width-R.Right; // Usualy 4 pixels
 NbItemsMax:=(ListBox.Height-LbDrawOffset) Div ListBox.ItemHeight;
// ListBox.Height:=NbItemsMax*ListBox.ItemHeight+LbDrawOffset; // make the listbox the exact height of NbItemsMax elements
 LbDrawOffset:=LbDrawOffset Div 2;
end;

procedure TForm1.EditExit(Sender: TObject);
begin
 Edit.Visible:=False;
end;

procedure TForm1.EditChange(Sender: TObject);
begin
 ListBox.Items[Edit.Tag]:=Edit.Text;
end;

procedure TForm1.ListBoxEdit(i:integer);
Var
 R:TRect;
begin
 if i>=NbItemsMax then ListBox.TopIndex:=i-NbItemsMax+1;
 ListBox.ItemIndex:=i;
 R:=ListBox.ItemRect(i);
 With Edit do
  begin
   Tag:=i; // Set Edit.Tag to the currently edited index
   Top:=ListBox.Top+R.Top+LbDrawOffset;
   Left:=ListBox.Left+R.Left+LbDrawOffset+2; // add 1 to make both lb text and edit text at the same place
   Width:=R.Right-R.Left-2;
   Height:=R.Bottom-R.Top;
   Text:=ListBox.Items[i];
   Visible:=True;
   SetFocus;
  end;
end;

procedure TForm1.ButtonClick(Sender: TObject);
begin
 ListBoxEdit(ListBox.Items.Add('New')); // Add item, get its index and call edit mode for this item index
end;

end.


// DFM 
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 337
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object ListBox: TListBox
    Left = 8
    Top = 39
    Width = 121
    Height = 81
    ItemHeight = 13
    TabOrder = 0
  end
  object Edit: TEdit
    Left = 144
    Top = 47
    Width = 121
    Height = 13
    BorderStyle = bsNone
    Ctl3D = False
    ParentCtl3D = False
    TabOrder = 1
    Text = 'Edit'
    Visible = False
    OnChange = EditChange
    OnExit = EditExit
  end
  object Button: TButton
    Left = 8
    Top = 8
    Width = 75
    Height = 25
    Caption = 'Button'
    TabOrder = 2
    OnClick = ButtonClick
  end
end

Open in new window

0
 
Geert GruwezOracle dbaCommented:
just use a treeview with only top level items
far easier, just set editing = true in options

why make life so difficult ?
0
 
epasquierCommented:
because it's fun :o)

And maybe also because a TListBox will not have the same look as a TListView, when you select a line in a list box all the line is properly selected, but in the treeview only the text of the node is. I find that not so pretty in some situations
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
epasquierCommented:
besides, I could have made it less complex by putting some constants instead of evaluating the offsets and such.

You only need the EditExit, EditChange, and a simpler ListBoxEdit function to make this work. But for educational purpose I felt necessary to do it right

Of course, Geert, you are right that most of the time it's better to find alternate and quicker solutions to a need, even if that means loose a few unnecessary functionalities. In that way, your solution is effective
0
 
Geert GruwezOracle dbaCommented:
epasquier,
looks like you have found some spare time again
0
 
3axapAuthor Commented:
I've chosen ListBox because of how it looks (especially with skin I use).
pasquier thank you so much for such complete solution.
0
 
3axapAuthor Commented:
I've chosen ListBox because of how it looks (especially with skin I use).
epasquier thank you so much for such complete solution.
0
 
epasquierCommented:
> looks like you have found some spare time again
yes and no. That one was an easy one, It took me 5 minutes

3axap : thanks, and you got it right the 1st time : pasquier is my name
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 4
  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now