【问题标题】:tListBox using AutoComplete navigation from other ListBoxtListBox 使用来自其他 ListBox 的自动完成导航
【发布时间】:2016-05-24 12:46:33
【问题描述】:

有没有办法使用原生 tListBox 自动完成导航系统但基于其他 ListBox 的项目?因此,当我键入一些字符时,ListBox1 聚焦时,应根据 ListBox2 中的数据选择一些字符项。两者的物品数量相同。

【问题讨论】:

    标签: delphi autocomplete listbox delphi-7


    【解决方案1】:

    有没有办法使用原生 tListBox AutoComplete 导航系统但基于其他 ListBox 的项目?

    可以,但前提是TListBox.Style 属性设置为lbVirtuallbVirtualOwnerDraw。在这种情况下,您必须使用TListBox.Count 属性和TListBox.OnData 事件向ListBox 提供字符串。然后,自动完成功能将触发 TListBox.OnDataFind 事件,要求您在获取字符串的任何来源中找到键入的字符。在该事件处理程序中,您可以根据需要搜索其他 TListBox。只需知道OnDataFind 事件处理程序返回的Integer 必须是相对于用户正在输入的TListBox 的索引,而不是相对于您正在搜索的TListBox 的索引。当OnDataFind 事件处理程序退出时,将选择您返回的任何索引,除非您返回-1 表示未找到字符。

    【讨论】:

      【解决方案2】:

      好吧,我试图让它成为原生的,但据我所知 - 不可能将不同的项目高度功能 (Style=lbOwnerDrawVariable) 与 DataFind 事件处理结合起来。当然可以在“VCL\StdCtrls”中编辑TCustomListBox.KeyPress 程序,但我不喜欢深入研究vcl 源。所以我决定自己从头开始实现自动完成功能。 首先,我决定索引项目字符串以加快搜索速度,但没有必要制作完整的图表(八叉树......无论如何......)因为在我的情况下搜索(我有大约 1k 个项目)无论如何几乎立即执行- 所以因为我的列表是按字典顺序排序的,所以我只对第一个字母进行索引...

      //...
      const
          acceptCharz=[' '..'z'];
      //...
      type
          twoWords=packed record a,b:word; end;
      //...
      var
          alphs:array[' '..'z']of twoWords;// indexes of first letters
          fs:tStringList;// list for searching in
          fnd:string;// charbuffer for search string
          tk:cardinal;// tickCounter for timing
      //...
      procedure Tform1.button1Click(Sender: TObject);// procedure where list filled with items
      var
          k,l:integer;
          h,q:char;
      begin
          //... here list-content formed ...
          if(fs<>nil)then fs.Free;
          fs:=tStringList.Create;
          // fltr is tListBox which data should be source of AutoCompletion (ListBox2)
          fs.AddStrings(fltr.Items);
          for h:=low(alphs)to high(alphs)do alphs[h].a:=$FFFF;// resetting index
          h:=#0;
          l:=fs.Count-1;
          if(l<0)then exit;
          for k:=0 to l do begin
              s:=AnsiLowerCase(fs.Strings[k]);// for case-insensetivity
              fs.Strings[k]:=s;
              if(length(s)<0)then continue;
              q:=s[1];
              if(h<>q)and(q in acceptCharz)then begin
                  if(k>0)then alphs[h].b:=k-1;
                  h:=q;
                  alphs[h].a:=k;// this will work only with sorted data!
              end;
          end;
          if(h<>#0)then alphs[h].b:=l;
      end;
      //...
      // fl is tListBox with custom drawing and OwnerDrawVariable style (ListBox1)
      // also fl has same amount of items as fltr
      procedure Tform1.flKeyPress(Sender: TObject; var Key: Char);
      var
          n,i,k,e,l,u,m,a:integer;
          s:string;
          h:char;
          function CharLowerr(h:char):char;// make char LowerCase
          begin
              Result:=char(LoWord(CharLower(Pointer(h))));
          end;
      begin
          if(getTickCount-tk>=800)then fnd:='';// AutoComplete timeout
          tk:=getTickCount;
          h:=CharLowerr(key);// for case-insensetivity
          if(h in ['a'..'z','0'..'9',' ','-','.'])then fnd:=fnd+h;// u can add all needed chars
          if(fnd='')then exit;// if no string to find
          h:=fnd[1];// obtain first letter of search-string
          a:=alphs[h].a;// get index of first item starting with letter
          l:=alphs[h].b;// get index of last item starting with letter
          if(a=$FFFF)then exit;// if no such items
          e:=length(fnd);// get length of search-string
          u:=1;// current length of overlap
          m:=1;// max overlap
          i:=a;// index to select
          if(e>1)then for k:=a to l do begin
              s:=fs.Strings[k];
              if(length(s)<e)then continue;
              for n:=2 to e do begin// compare strings char-by-char
                  if(s[n]<>fnd[n])then begin// compare failed
                      u:=n-1;
                      break;
                  end else u:=n;
                  if(u>m)then begin// current overlap is max
                      m:=u;
                      i:=k;
                  end;
              end;
              if(u=e)or(u<m)then break;// if end of search reached
          end;
          // select needed index:
          fl.ClearSelection;
          SendMessage(fl.Handle, LB_SELITEMRANGE, 1, MakeLParam(i, i));
          fl.ItemIndex:=i;
          inherited;
      end;
      //...
      

      是的,这段代码有点难看,但它工作正常,正如我所说的 - 几乎立即,我可以看到选择如何在我输入时跳过项目,而且我输入速度非常快......

      所以这是我昨天写的代码,所有这一切都可以在这里结束,但今天我意识到这绝对是一个愚蠢的决定:就我而言,正如我上面提到的,我有具有 OwnerDrawVariable 样式的列表框,所以我有自定义 MeasureItem 和 DrawItem 过程,在这种情况下,最好的决定是将 AutoComplete 属性设置为 true 并用 ListBox2 中的项目填充 ListBox1。并且无论如何都可以在 DrawItem 过程中显示需要显示的字符串。也可以删除 ListBox2 并保留字符串以在 tStringList 变量中显示。所以士气是 - 不要急于编写样板并在采取行动之前尝试更多思考=))

      附:但是可以将此代码用于一些自定义的自动完成处理...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-09
        • 2020-05-01
        • 2023-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多