Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Выпадающий список в ComboBox


Автор: Albinos_x 23.4.2005, 11:24
Тут возник у меня вопрос:
Как сделать фильтрацию в выпадающем списке в ComboBox, в зависимости от набраемой строки.
Т.е. сделать приблизительно то же, что и в Делфи по нажатью "Ctrl+Space" только в ComboBox-е

Автор: Garmahis 23.4.2005, 11:34
Пример помойму из DRKB

Код

var 
  LastKey: Word; 

procedure TForm1.ComboBox1KeyDown(Sender: TObject; var Key: Word; 
  Shift: TShiftState); 
begin 
  LastKey := Key; 
end; 

procedure TForm1.ComboBox1Change(Sender: TObject); 
var 
  Srch: string; 
  ix: Integer; 
begin 
  Srch := combobox1.Text; 
  if LastKey = $08 then 
  begin 
    LastKey := 0; 
    Exit; 
  end; 
  LastKey := 0; 
  ix := combobox1.Perform(CB_FINDSTRING, - 1, Longint(PChar(Srch))); 
  if ix > CB_ERR then 
  begin 
    combobox1.ItemIndex := ix; 
    combobox1.SelStart  := Length(Srch); 
    combobox1.SelLength := (Length(combobox1.Text) - Length(Srch)); 
  end; 
end;



Автор: Albinos_x 23.4.2005, 12:00
Не... этот код не делает фильтрацию... Он просто выбирает по набронному из списка
тоже самое можно сделать
Код

combobox1.AutoComplete:=true;



Автор: Pakshin A. S. 23.4.2005, 12:45
Код

var
 i:integer;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
 ComboBox1.Items.Clear;
 for i:=0 to ListBox1.Items.Count-1 do
  begin
   if Copy(ListBox1.Items[i], 1, Length(ComboBox1.Text)) = ComboBox1.Text
    then
     ComboBox1.Items.Add(ListBox1.Items[i]);
   Application.ProcessMessages
  end
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 for i:=0 to ListBox1.Items.Count-1 do
  begin
   ComboBox1.Items.Add(ListBox1.Items[i]);
   Application.ProcessMessages
  end
end;

Где в спрятанном ListBox'е хранится полный список...
Добавлено @ 12:46
Вот только большой список не очень красиво работать будет... smile
Добавлено @ 12:47
С тысячей быстро справляется...

Автор: Albinos_x 23.4.2005, 12:56
У меня был подобный вариант, но в нем как и в этом возникает проблема: курсор при каждом выполнении процедуры перемещяется в начало текста, что приводит к тому что текст набирается в обратном порядке

Автор: Pakshin A. S. 23.4.2005, 13:27
Код

 ComboBox1.Items.Clear;
 for i:=0 to ListBox1.Items.Count-1 do
  begin
   if Copy(ListBox1.Items[i], 1, Length(ComboBox1.Text)) = ComboBox1.Text
    then
     ComboBox1.Items.Add(ListBox1.Items[i]);
   Application.ProcessMessages
  end;
 ComboBox1.SelStart:=Length(ComboBox1.Text); // Вот это возращает курсор в конец текста

Автор: Albinos_x 23.4.2005, 13:46
Большое спасибо!

А есть возможность считывать положение курсора и его устанавливать в назначенную позицию?
// допустим пользователь совершил опечатку. Он нажмет Del или BackSpase курсор переместится в конец - это не есть хорошо...

Автор: Pakshin A. S. 23.4.2005, 13:48
Код

var
 s:string;
 j:integer;
begin
 s:=ComboBox1.Text;
 ComboBox1.Items.Clear;
 j:=0;
 for i:=0 to ListBox1.Items.Count-1 do
  begin
   if Copy(ListBox1.Items[i], 1, Length(s)) = s
    then
     begin
      ComboBox1.Items.Add(ListBox1.Items[i]);
      if ListBox1.Items[i] = s
       then
        j:=ComboBox1.Items.Count-1;
     end;
   Application.ProcessMessages
  end;
 ComboBox1.ItemIndex:=j;
 ComboBox1.SelStart:=Length(s);

Теперь еще можно и выбирать из списка элемент... и не бояться за ItemIndex...
Добавлено @ 13:49
Код

 ComboBox1.SelStart:=length(s)-1;

На предпоследнюю позицию...

P. S.
Пояcни конкретнее задачу про DEL... ^)

Автор: Albinos_x 23.4.2005, 14:02
Цитата
P. S.
Пояcни конкретнее задачу про DEL...


допустим пользователь набрал вместо Albinos_x -> Alninos_x, ему надо пеменять букву 'n' на букву 'b'. Он устанавливает курсор перед буквой 'n' и нажимает Del, курсор перемещяется в конец текста. Он по невнимательности нажимает 'b' и получает Alinos_xb. Даже если он будет внимательным, то каждый раз перемещять курсор не очень удобно...

Цитата
...Теперь еще можно и выбирать из списка элемент... и не бояться за ItemIndex...


Этот вариант не покатит

Автор: Pakshin A. S. 23.4.2005, 18:13
Ok... щас доработаю исключения на данные клавиши... smile

Автор: Pakshin A. S. 23.4.2005, 18:44
Идея такова:
1) Добавляем новую глобальную переменную
Код

flag: boolean;

2) Делаем еще один обработчик
Код

procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: Char);
begin
 flag:=key <> #8
end;

3) Изменяем процедуру:
Код

procedure TForm1.ComboBox1Change(Sender: TObject);
var
 s:string;
 j:integer;
begin
 if flag
  then
   begin
    s:=ComboBox1.Text;
    ComboBox1.Items.Clear;
    j:=-1;
    for i:=0 to ListBox1.Items.Count-1 do
     begin
      if Copy(ListBox1.Items[i], 1, Length(s)) = s
       then
        begin
         ComboBox1.Items.Add(ListBox1.Items[i]);
         if ListBox1.Items[i] = s
          then
           j:=ComboBox1.Items.Count-1;
        end;
      Application.ProcessMessages
     end;
    ComboBox1.ItemIndex:=j;
    ComboBox1.SelStart:=Length(s)
   end
 else
  flag:=true
end;


А вот по поводу кнопочки Del... пока не получается...

P. S.
То, что надо реализовал? Или надо что-то иное?

Автор: Albinos_x 23.4.2005, 20:10
к сожалению, нет...
почти что как надо работает первый предложенный тобой вавриант...

второй делает выборку по первому символу, а третий - последний - при удалении символов не возвращяет ранее отфильтрованное...Знаю в чём проблема, но не заню как решить.

Решение было бы проще если можно было считывать и также устанавливать позицию курсора...
Добавлено @ 20:17
Цитата
А вот по поводу кнопочки Del... пока не получается...


с этим не получается потому, что Key почему-то не возвращяет значение нажатие этой кнопки.. я пробовал делать #46 {VK_DELETE}, но не помогает

Автор: Pakshin A. S. 23.4.2005, 20:57
М-дя... Так... Скажи четко, что тебе нужно и я попробую все это реальзовать... Опиши различные ситуации и выборки...

Автор: Albinos_x 24.4.2005, 00:11
Нужна фильтрация, чтобы в списке отображалось только то, что совпадает по маске с набранным(имеющимся) в комбобоксе. И чтобы с этим было удобно работать (имею ввиду что поведение курсора не должно отличатся от стандартного поверения)

Автор: Pakshin A. S. 24.4.2005, 08:07
Ну первое, думаю уже реализовано... причем работает нормально...

Теперь второе... что подразумевается под "поведение курсора не должно отличатся от стандартного поверения"... Идеально - приведи пример типа такого:
1) Вводим ыва
2) Переходим в позицию и нажимаем что-то
3) У нас должно получиться то-то...
4) и т. д. и т. п.

smile

Автор: Albinos_x 24.4.2005, 12:20
Пример:

1. введено 'Alninos_x|'
2. переходим в позицию 'Al|ninos_x' или 'Aln|inos_x'
3. нажимием #Del или #BackSpace cooтветственно
4. получаем 'Al|inos_x'
5. нажимаем 'b'
6. получаем ' Alb|inos_x'

где | - позиция курсора

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

Автор: Pakshin A. S. 24.4.2005, 12:35
Делаем упомянутый выше обработчик на нажатие клавиши...
и изменяем процедуру:
Код

procedure TForm1.ComboBox1Change(Sender: TObject);
var
 s:string;
 j:integer;
begin
    s:=ComboBox1.Text;
    ComboBox1.Items.Clear;
    j:=-1;
    for i:=0 to ListBox1.Items.Count-1 do
     begin
      if Copy(ListBox1.Items[i], 1, Length(s)) = s
       then
        begin
         ComboBox1.Items.Add(ListBox1.Items[i]);
         if ListBox1.Items[i] = s
          then
           j:=ComboBox1.Items.Count-1;
        end;
      Application.ProcessMessages
     end;
    ComboBox1.ItemIndex:=j;
    if flag
     then
      ComboBox1.SelStart:=Length(s)
     else
      flag:=true;
   end
end;

Автор: Albinos_x 24.4.2005, 13:05
В этом случае курсор перемещяется в начало текста... что в принципе не отличается от других вариантов...

Ладно сделаю пока так как есть...
Вот что получилось:


Код
...
var
  List : TStringList;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
...
List:=TStringList.Create;
ComboBox1.Tag:=1:
...
end;

....

procedure TForm1.ComboBox1Enter(Sender: TObject);
begin
IniFilter(Table1,inttostr(ComboBox1.Tag));
IniList(Table1, List);
ComboBox1.Items:=List;
end;

procedure FilterComboList(Combo: TComboBox; L:TStringList);
var i, k:word;
begin
k:=L.Count;
if k<>0 then
   begin
   Combo.Items.Clear;
   for i:=0 to (k-1) do
      begin
      if AnsiLowerCase(Copy(L.Strings[i], 1, Length(Combo.Text))) = AnsiLowerCase(Combo.Text) then
         Combo.Items.Add(L.Strings[i]);
      Application.ProcessMessages;
      end;
   Combo.SelStart:=Length(Combo.Text);
   end;
end;
...
procedure TForm1.ComboBox1Change(Sender: TObject);
begin
FilterComboList(ComboBox1, List);
end;
...

Автор: Pakshin A. S. 24.4.2005, 13:19
О! Вспомнил.... Вроде так получится

Код

procedure TForm1.ComboBox1Change(Sender: TObject);
var
 s:string;
 j,k:integer;
begin
    s:=ComboBox1.Text;
    k:=ComboBox1.SelStart;
    ComboBox1.Items.Clear;
    j:=-1;
    for i:=0 to ListBox1.Items.Count-1 do
     begin
      if Copy(ListBox1.Items[i], 1, Length(s)) = s
       then
        begin
         ComboBox1.Items.Add(ListBox1.Items[i]);
         if ListBox1.Items[i] = s
          then
           j:=ComboBox1.Items.Count-1;
        end;
      Application.ProcessMessages
     end;
    ComboBox1.ItemIndex:=j;
    if flag
     then
      ComboBox1.SelStart:=Length(s)
     else
      ComboBox1.SelStart:=k;
      flag:=true;
   end
end;


Да, так работает... курсор сохраняет позицию...
Добавлено @ 13:20
Кстати, а почему ты не изменяешь ItemIndex? У меня без него нельзя было выбрать корректно элемент из списка... smile

Автор: Albinos_x 24.4.2005, 14:27
Ок!!! Всё работает!!!

Внес изменения в
Код

procedure FilterComboList(Combo: TComboBox; L:TStringList);
var i, k:word;
    pos:word;
    j: integer;
begin
pos:=Combo.SelStart;
k:=L.Count;
j:=-1;
if k<>0 then
   begin
   Combo.Items.Clear;
   for i:=0 to (k-1) do
      begin
      if AnsiLowerCase(Copy(L.Strings[i], 1, Length(Combo.Text))) = AnsiLowerCase(Combo.Text) then
         begin
         Combo.Items.Add(L.Strings[i]);
         if L.Strings[i] = Combo.Text then
            j:=Combo.Items.Count-1;
         end;
      Application.ProcessMessages;
      end;
   Combo.ItemIndex:=j;
   Combo.SelStart:=pos;
   end;
end;


Цитата
Кстати, а почему ты не изменяешь ItemIndex? У меня без него нельзя было выбрать корректно элемент из списка...


Не заметил косяк, увлёкся фильтрацией и курсором... Спасибо за замечание!... smile

Спавибо за оказанную помощь!!! smile
Добавлено @ 14:32
Pakshin A. S.
+1

Автор: Pakshin A. S. 24.4.2005, 14:32
Не за что... рад немного подсобить... обращайся, если что не так пойдет... smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)