Dwie kolumny w ComboBox

0

Witam,
chciałbym dodać dane w ComboBox, aby istniały dwie kolumny.

Jeśli używam


IBQUERY1.Close;
IBQuery1.SQL.Clear;
IBQuery1.SQL.add('Select  NAZWA1,NAZWA2 FROM TABELA');
IBQuery1.Open;

while not IBQuery1.Eof do
begin
ComboBox2.Items.Add(IBQuery1.FieldByName('NAZWA1').asString+'  '+IBQuery1.FieldByName('NAZWA2').asString);
IBQuery1.Next;
end;
 

Nie daje nazw drugiej kolumny w równej linii, na czym mi zależy.
Wie ktoś jak to rozwiązać ?
Proszę o pomoc.

0
IBQUERY1.First

przed pętlą.

0

@boznoo111 przeoczyłem bo dałem przykład ale nie o to chodzi.

0

Dajesz spacje. Zamiast spacji spróbuj 2 albo 3 tabulatory, może wyrówna... Ewentualnie na swój sposób rysuj w comboboxie

0

@Johnny_Bit
Nic to nie da, bo są różne szerokości liter,kropek itp..
Myślałem, że jest dodawanie wartości do kolumny 1,2 itp to by rozwiązało problem.

0

Spróbuj tabami. A jak się nie uda tabami (\t, nie pamiętam jaki kod znaku ;)) to masz własne rysowanie elementów. A jak się nie chcesz bawić w takie coś to masz customowe komponenty

0

W takim przypadku to jedynym sensownym rozwiązaniem jest własne rysowanie itemów; Nie ma dobrego rozwiązania, które bazuje na podstawowej funkcjonalności komponentu;

Jedyną kwestią do zastanowienia się jest łączenie dwóch ciągów w jeden, a potem ich rozdzielenie podczas rysowania; Nie ważne jakiego znaku użyje się jako separatora - ważne, aby nie wystepował on w łańcuchach pobranych z bazy; Reszta to pikuś.

0

Spróbuj takiej konstrukcji , u mnie działa , pobieram z kolumny "Imię" i z kolumny "Nazwisko" następnie sklejam wartości na zasadzie
<imię>+' '+<nazwisko>

 with DM.DataModule1.temp_query do
         begin
           SQL.Clear;
           SQL.Add('select imie,nazwisko  from PRACOWNIK');
           Open;
             while not EOF  do
              begin
               ComboBox1.Items.Add(DM.DataModule1.temp_query['nazwisko']+' '+DM.DataModule1.temp_query['imie']);
               Next;
              end;
         end;

 
1

Nikt nie podawał przykładu, więc pokażę jak takie coś wykonać;

Skoro problemu z odczytem danych z bazy nie ma, to nie będę pokazywał jak łączyć łańcuchy - to raczej sensu nie ma; W każdym razie wygodnie będzie oddzielać składowe znakiem spacji (0x20), bo będzie można wyświetlić całość w głównym polu komponentu; Dla przykładu dodajemy do komponentu poniższe itemy:

xxxxx xxxxxxxxxxxx
xxxxxxx xxxxxxx
xxxx xxxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxx
xxxxxxxxx xxxx
xxxxx xxxxxxxxx

Wartość po lewej to imię, a po prawej to nazwisko - jak widać mają różne długości (i składowe, i całość); Teraz trzeba napisać algorytm rozdzielający łańcuch na składowe - najprościej jest to wykonać za pomocą klasy TStringList:

type
  TItemTextParts = record
    Name: AnsiString;
    Surname: AnsiString;
  end;

procedure TMainForm.SplitItemText(AText: AnsiString; out AResult: TItemTextParts);
const
  COMBO_ITEM_PARTS_SEPARATOR = AnsiChar(' ');
begin
  with TStringList.Create() do
  try
    Delimiter       := COMBO_ITEM_PARTS_SEPARATOR;
    DelimitedText   := AText;
    AResult.Name    := Strings[0];
    AResult.Surname := Strings[1];
  finally
    Free();
  end;
end;

Metoda ta rozdziela łańcuch itema na składowe, po czym wrzuca je do pól rekordu z parametru AResult; Oczywiście zabezpieczenia można sobie dodać, jeśli któryś z łańcuchów może zaiwerać tylko imię/nazwisko; To nam wystarczy, teraz czas na malowanie itemów; Samo malowanie jest banalne - wystarczy tylko ustalić kolor tekstu i tła na podstawie zawartości zbioru z parametru State i pomalować item:

procedure TMainForm.bcPreviewDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
const
  COMBO_SURNAME_OFFSET = 100;
var
  Combo: TComboBox;
  itpItem: TItemTextParts;
begin
  Combo := TComboBox(Control);

  with Combo.Canvas do
  begin
    { set colors for background and text }
    if odSelected in State then
    begin
      Brush.Color := clMenuHighlight;
      Font.Color  := clHighlightText;
    end
    else
    begin
      Brush.Color := clWindow;
      Font.Color  := clWindowText;
    end;

    { fill background rectangle }
    FillRect(Rect);

    if odComboBoxEdit in State then
      { draw name and surname }
      TextOut(Rect.Left + 2, Rect.Top + 2, Combo.Items[Index])
    else
    begin
      { split item text }
      SplitItemText(Combo.Items[Index], itpItem);

      { draw name }
      TextOut(Rect.Left + 2, Rect.Top + 2, itpItem.Name);
      { draw surname }
      TextOut(Rect.Left + COMBO_SURNAME_OFFSET, Rect.Top + 2, itpItem.Surname);
    end;

    { remove focus rectangle }
    if odFocused in State then
    begin
      Pen.Color := Pen.Color xor $FFFFFF;
      DrawFocusRect(Rect);
    end;
  end;
end;

Jak widać rysowanie tekstu jest w warunku; Istnienie enuma odComboBoxEdit istnieje w zbiorze State oznacza, że malowany jest item w edytorze komponentu, a nie na jego rozwijanej liście; Sprawdzenie to ma na celu narysowanie tekstu razem, aby przy zwiniętej liście nie było dużego odstępu pomiędzy imieniem a nazwiskiem; W przeciwnym razie imię i nazwisko rysowane są w odpowiednim odstępie od lewej krawędzi obszaru roboczego itema; Offset nazwiska można sobie wyregulować według własnych upodobań - ustalenie odpowiedniego odstępu od lewej krawędzi jest konieczne, aby imię nie nachodziło na nazwisko;

Efekt działania powyższego kodu:

combo.png

Item widoczny w edytorze to ten sam co item podświetlony na liście; Źródła programu bez pliku wykonywalnego wrzucam do załączników posta.

1
boznoo111 napisał(a):
IBQUERY1.First

przed pętlą.

A po co IBQUERY1.First, skoro po IBQUERY1.Open kursor w DataSet (czyli w IBQUERY1) zawsze jest ustawiony na pierwszym rekordzie - nie ostatnim, środkowym czy innym - tylko na pierwszym.

Natomiast ładowanie danych do ComboBoxa, zwłaszca jak jest ich sporo, powinno być otoczone Combo.Items.BeginUpdate i Combo.Items.EndUpdate; dzięki czemu Combo nie będzie powiadamiane o dodaniu nowego itema, przy dodawaniu każdego itema.
I tyle poza tematem ;-)

A tak w temacie - dlaczego nie użyć TDBLookupCombobox zamiast zwykłego ComboBoxa, ponieważ DBLookup posiada to o czym mowa ot tak, w standardzie?
Poza tym zawsze można zerknąć do metody TDBLookupCombobox.Paint i zobaczyć jak można zrobić wiele kolumn w ComboBox. Bez ogromnego narzutu dodatkowego i krzywgo kodu z uzyciem StringLsity i Bóg wie czego jeszcze?
Tak tylko gdybam...

1
user2324 napisał(a)

Bez ogromnego narzutu dodatkowego i krzywgo kodu z uzyciem StringLsity i Bóg wie czego jeszcze?
Tak tylko gdybam...

No, no, narzut niesamowity - godzinę będzie się odmalowywało...

To co podałem to tak jak zaznaczyłem - najprostsze rozwiązanie; Bez problemu można łańcuch podzielić znaczenie szybszymi metodami, np. przeszukiwaniem wskaźnikowym i wypakowany ciąg od razu malować na kanwie, bez zwracania go przez parametr; Masz więc dużo szybszą wersję:

procedure TMainForm.bcPreviewDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
const
  COMBO_SURNAME_OFFSET  = Integer(100);
  COMBO_PARTS_DELIMITER = AnsiChar(#32);
const
  COMBO_BACK_COLORS: array [Boolean] of TColor = (clWindow, clMenuHighlight);
  COMBO_FONT_COLORS: array [Boolean] of TColor = (clWindowText, clHighlightText);
var
  Combo: TComboBox;
  strText, strValue: AnsiString;
  pchrBegin, pchrEnd: PAnsiChar;
begin
  Combo := TComboBox(Control);

  with Combo.Canvas do
  begin
    Brush.Color := COMBO_BACK_COLORS[odSelected in State];
    Font.Color  := COMBO_FONT_COLORS[odSelected in State];

    FillRect(Rect);

    if odComboBoxEdit in State then
      TextOut(Rect.Left + 2, Rect.Top + 2, Combo.Items[Index])
    else
    begin
      strText := Combo.Items[Index] + COMBO_PARTS_DELIMITER;
      pchrBegin := @strText[1];
      pchrEnd   := pchrBegin + 1;

      while pchrEnd^ <> COMBO_PARTS_DELIMITER do
        Inc(pchrEnd);

      SetLength(strValue, pchrEnd - pchrBegin);
      Move(pchrBegin^, strValue[1], pchrEnd - pchrBegin);
      TextOut(Rect.Left + 2, Rect.Top + 2, strValue);

      pchrBegin := pchrEnd + 1;
      pchrEnd   := pchrBegin + 1;

      while pchrEnd^ <> COMBO_PARTS_DELIMITER do
        Inc(pchrEnd);

      SetLength(strValue, pchrEnd - pchrBegin);
      Move(pchrBegin^, strValue[1], pchrEnd - pchrBegin);
      TextOut(Rect.Left + COMBO_SURNAME_OFFSET, Rect.Top + 2, strValue);
    end;

    if odFocused in State then
    begin
      Pen.Color := Pen.Color xor $FFFFFF;
      DrawFocusRect(Rect);
    end;
  end;
end;

Takie rysowanie jest tak samo bardzo proste i wystarczająco szybkie; Poza tym nikt mądry raczej nie wpakuje setek tysięcy itemów do ComboBoxa, więc problem malowania po prostu nie istnieje;

Wcześniej podałem jedno z najprostszych rozwiązań, bo pytacz jak widać ma problem z podsawami; Można to rozwiązać na wiele innych sposobów - trzeba tylko pomyśleć i coś wybrać.

0

Dzięki ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1