TreeView + Checkbox oraz radioBox

0

Witam

Mam dwa pytania dotyczące TreeView oraz łączenia go z Checkbox oraz radioBOXami

PYTANIE 1:
Mam problem z dołożeniem CheckBOXów oraz radioBOXów w taki sposób aby były widoczne tylko i wyłącznie w "liściach" drzewka TreeView.

Coprawda znalazłem w FAQ (Jak utworzyć TreeView posiadający Checkbox) jak dołożyć CheckBOXy do treeView, ale w ten sposób pojawiają się one we wszystkich gałęziach, a ja potrzebuje je mieć tylko i wyłacznie w liściach.

Natomiast na temat radioBOXów nie znalazłem ani słowa.

Generalnie żecz ujmując chciałbym aby moje drzewko wygladało tak jak na obrazku:
user image

PYTANIE 2:
Co zrobić aby po uruchomieniu programu wszystkie gałęzie były schowane (zwinięte) ??
Jest to dla mnie o tyle istotne że po kliknięciu (w celu rozwinięcia gałęzi) ma nastąpić wysłanie polecenia SQL (i jego wizualizacja w DBGrid)

0

Proponuję samemu rysować Item'y wraz z checkboxami i radioboxami - wszystko będzie pod kontrolą, lub jeśli chcesz iść na łatwiznę lub nie masz czasu, to skorzystać z jakiegoś komponentu, na torry.net na pewno coś znajdziesz.

PS. A co do tego drugiego pytania to nie jestem pewien czy oto Ci chodzi - TreeView.FullCollapse.

0

Po kiego tak się męczyć zainstalować VirtualTreeView masz to (CheckBox oraz RadioButton) właściwie w jednym kliknięciu wraz z całą obsługą.

1

A tu przykład jak zrobić identyczne drzewo jak na screenie:

unit uMain;

interface

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

type
  PVirtualRec = ^TVirtualRec;
  TVirtualRec = record
    Caption: WideString;
  end;

  TfrmMain = class(TForm)
    VirtualStringTree1: TVirtualStringTree;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure VirtualStringTree1GetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }

  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.FormCreate(Sender: TObject);
var
  PNode: PVirtualNode;
  PData: PVirtualRec;
begin
  VirtualStringTree1.CheckImageKind:= ckXP;
  VirtualStringTree1.TreeOptions.MiscOptions:=
    VirtualStringTree1.TreeOptions.MiscOptions + [toCheckSupport];

  VirtualStringTree1.NodeDataSize := SizeOf(TVirtualRec);

  VirtualStringTree1.RootNodeCount:= 2;

  PNode:= VirtualStringTree1.GetFirst;
  PData:= VirtualStringTree1.GetNodeData(PNode);
  PData.Caption:= 'jeden';

  VirtualStringTree1.ChildCount[PNode]:= 1;

  PNode:= VirtualStringTree1.GetFirstChild(PNode);
  PData:= VirtualStringTree1.GetNodeData(PNode);
  PData.Caption:= '1-1';

  VirtualStringTree1.ChildCount[PNode]:= 1;

  PNode:= VirtualStringTree1.GetFirstChild(PNode);
  PNode.CheckType:= ctCheckBox;
  PData:= VirtualStringTree1.GetNodeData(PNode);
  PData.Caption:= '3-3';

  PNode:= VirtualStringTree1.GetNext(PNode);
  PData:= VirtualStringTree1.GetNodeData(PNode);
  PData.Caption:= 'dwa';

  VirtualStringTree1.ChildCount[PNode]:= 2;

  PNode:= VirtualStringTree1.GetFirstChild(PNode);

  PData:= VirtualStringTree1.GetNodeData(PNode);
  PNode.CheckType:= ctRadioButton;
  PData.Caption:= '2-1';

  PNode:= VirtualStringTree1.GetNext(PNode);
  PNode.CheckType:= ctRadioButton;
  PData:= VirtualStringTree1.GetNodeData(PNode);
  PData.Caption:= '2-2';

  VirtualStringTree1.FullExpand(); //rozin wszyskie
  //VirtualStringTree1.FullCollapse(); //zwin wszyskie
end;

procedure TfrmMain.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
var
  PData: PVirtualRec;
begin
  PData:= Sender.GetNodeData(Node);
  CellText:= PData.Caption;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  PNode: PVirtualNode;
  PData: PVirtualRec;
begin
  //sprawdza ktore sa zaznaczone
  PNode:= VirtualStringTree1.GetFirstChecked(csCheckedNormal, True);
  while Assigned(PNode) do
  begin
    PData:= VirtualStringTree1.GetNodeData(PNode);
    ShowMessage(PData.Caption);
    PNode:= VirtualStringTree1.GetNextChecked(PNode, csCheckedNormal, True)
  end;
end;

end.
0

Ten kod w ostanim poście bardzo mi pomógł. Dziękuje.

Ale mam jeszcze 1 pytanie:
Co zrobić aby wyeliminować taka sytuację:
user image
Zasadniczo chodzi o to aby zaznaczenie (podświetlenie tekstu na niebiesko) zawsze pokrywało się z zaznaczoną kropką (radioBox) ??
A w przypadku zaznaczenia samego tekstu (znajdującego się wyżej, w tym wypadku "INTEL") zaznaczenie z niższej gałęzi znika całkowicie (zarówno kropka z radioBOXa jak i podświetlenie) ??

0

Nie rób tego, bo wyłączysz możliwość zaznaczania odznaczania przez klawiaturę.
Zaawansowani użytkownicy windows, dużo rzeczy robią przez klawiaturę, bo myszą - wolniej.

0

@_13th_Dragon prawdopodobnie dlatego to nie jest wbudowane, nie mniej jak autor się upiera to można to zrobić tylko pytanie do @viper_2000 czy nadal chcesz to zrobić kosztem popsucia przemieszczania się po drzewie klawiaturą?

0

A nie da się zrobić tego w taki sposób że jeśli zaznaczam klawiaturą kropkę to automatycznie obok zaznacza się napis
natomiast podczas pracy myszką (niezależnie czy użytkownik kliknie na napis czy na kropkę to i tak) zaznaczone zostaną obydwa te elementy równocześnie ??

0

OnChecked i OnChange

0

_13th_Dragon - pisząc

OnChecked i OnChange
nie pomogłeś mi zbytnio ponieważ nadal nie wiem co pod tymi procedurkami powinienem wpisać

Teraz zaczynam programować akcje pod konkretne gałęzie mojego drzewka. I tu pojawia się kolejny problem...

Napisałem taką procedurkę:

 procedure TForm1.VirtualStringTree1Click(Sender: TObject);
var
  PNode: PVirtualNode;
  PData: PVirtualRec;
begin
  //sprawdza ktore sa zaznaczone
PNode:= VirtualStringTree1.GetFirstChecked(csCheckedNormal, True);
  while Assigned(PNode) do
    begin

      PData:= VirtualStringTree1.GetNodeData(PNode);
      form1.Label1.Caption:=(PData.Caption);
      PNode:= VirtualStringTree1.GetNextChecked(PNode, csCheckedNormal, True)
    end;

if PData.Caption='Pamięć RAM' then
  begin
    DM.ZReadOnlyQuery1.SQL.Add('SELECT * FROM NB_Pamieci_DDR_III');
    DM.ZReadOnlyQuery1.Active:=true;

    DM.ZQuery1.SQL.Add('SELECT * FROM NB_Pamieci_DDR_III');
    DM.ZQuery1.Active := true;
  end;
end;

Ale nie mam pojęcia dlaczego po kliknięciu w dowolną gałąź drzewka wywala mi błąd i program się zawiesza, zamiast wykonać odpowiednie akcje.

0
if PData.Caption='Pamięć RAM' then 

Po pierwsze PData może być nil powinieneś to sprawdzić Assigned, po drugie jak dla mnie dziwna jest taka "zabawa" w porównywanie stringów... no i nawet nie wiesz co robiła pętla wzięta z mojego koduu ona sprawdzała się przy checkboxach po kolei pobierała zaznaczone pozycje a w ten sposób zadziałała (dziwny sposób zapamiętywania w labelu) tylko ostatnią.

EDIT//

type
  PVirtualRec = ^TVirtualRec;
  TVirtualRec = record
    Caption: WideString;
    TableName: string; //tu moze sobie dodawac pola na potrzebne dane np. nazwe tabeli
  end;

Ale aby dobrze to działało muszą być oprogramowane zdarzenia OnGetNodeDataSize i OnFreeNode:

procedure TfrmMain.VirtualStringTree1GetNodeDataSize(
  Sender: TBaseVirtualTree; var NodeDataSize: Integer);
begin
  NodeDataSize:= SizeOf(TVirtualRec);
end;

procedure TfrmMain.VirtualStringTree1FreeNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  PData: PVirtualRec;
begin
  PData:= VirtualStringTree1.GetNodeData(Node);
  if Assigned(PData) then
  begin
    PData.Caption:= '';
    PData.TableName:= '';
    //gdyby byl przechowywany jakis obiekt nalezalo by go zwolnic
  end;
end;

Oczywiście wartości pól dodatkowych (poza Caption) wypełniasz / modyfikujesz/ pobierasz w dowolnym momencie więc przypuśćmy że mamy w polu TableName nazwę tabeli z której będą pobierane dane gdy ktoś kliknie na pozycję z RadioButton (i go zaznaczy) chsz pobrac te dane 9po to aby coś zrobić):

procedure TfrmMain.VirtualStringTree1Checked(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
const
  SELECT_SQL = 'Select * From ';
var
  QueryStr: string;
var
  PData: PVirtualRec;
begin
  if Assigned(Node) then //to wlasciwie tylko aby dmucha na zimne...
  begin
    if (Node.CheckType = ctRadioButton) and //interesuja nas tylko pola RadioButton
       (Node.CheckState = csCheckedNormal) then  //i tylko gdy sa zaznaczone
    begin
      PData:= VirtualStringTree1.GetNodeData(Node);
      if Assigned(PData) then
      begin
        QueryStr:= SELECT_SQL + PData.TableName;
        //teraz w QueryStr masz cale zapytanie i robisz z nim co chcesz
        ShowMessage(QueryStr); //ja je dla przykladu wyswietle
      end;
    end;
  end;
end;
0

jak dla mnie dziwna jest taka "zabawa" w porównywanie stringów...
Jak możesz zobaczyć na poniższym obrazku, moje drzewko jest już trochę "wyrośnięte" i z biegiem czasu na pewno będzie jeszcze rosło. To porównywanie miało na celu sprawdzić co jest zaznaczone i wybranie odpowiedniego zbioru instrukcji w celu wwykonania odpowiedniego polecenia SQL.

W założeniu każda gałąź drzewa ma mieć inną instrukcję SQL do wykonania. Instrukcje mają się wykonać po zaznaczeniu gałęzi.

(dziwny sposób zapamiętywania w labelu) tylko ostatnią..
Ponieważ DBGrid (w danym momęcie) może wyświetlać wynik wykonania tylko 1 instrukcji SQL, dlatego zależało mi na tym aby zanaczenie w CheckBOXach czy radioBOXach pokrywało się z podświetleniem na niebiesko(tekstu). Niestety podpowiedź _13th_Dragon niewiele mi dała ponieważ napisał mi tylko
OnChecked i OnChange
podpowiadając mi tym samym gdzie powinienem zapisać instrukcję, ale w dalszym ciągu nie wiem w jaki sposób to zrobić.

no i nawet nie wiesz co robiła pętla wzięta z mojego kodu ona sprawdzała się przy checkboxach po kolei pobierała zaznaczone pozycje a w ten sposób zadziałała
Tak się składa że przed rozpoczęciem modyfikacji twojego kodu, dokladanie przetestowalem działanie tego kodu, a potem rozpoczolem zabawę z tym kodem w celu lepszego zrozumienia go i przystosowania do swoich potrzeb.
Większość kodu zrozumiałem dzięki czemu mogłem mocniej rozbudować przedstawiony na początku tematu przykład oraz zintegrować go z moim wcześniejszym kodem. Niestety miałem problem z tym jak wyciągnąć informację o aktualnie zaznaczonej gałęzi w celu dopsania do niej odpowiedniej instrukcji SQL

W tym momęcie tak wygląda okienko mojego programu:
user image
Chodzi mi o to że jeśli w drzewie (po lewej strony) zaznaczona zostanie gałąź główna (np. "Karty Muzyczne") wówczas z prawej strony (w DBGrid) wyświetlona zostaje cała tabela (np. NB_KartyMuzyczne). Jeśli natomiast zostaną zaznaczone pola CheckBOX czy radioBOX (w niższych gałęziach) wówczas ta tabela zostanie "okrojona".
CheckBOXy i radioBOXy mają pełnić funkcję "Filtrów"


PS. Widzę że (w między czasie) zanim skończyłem mój ostatni post pojawił się kolejny z fragmętami kodu. Jutro go dokładnie sprawdzę

0

Dla gałęzi producent też postaw - CheckBox z tym że 3-state.
Wtedy automagicznie jak pod nim zaznaczono wszystko to jest zaznaczony, ja pod nim nie zaznaczono nic to jest odznaczony, jak pod nim część oznaczona a część nie to on ma stan pośredni.
Po prawej stronie też użyj VirtualTreeView.

Zdarzenie OnChange - zmienił się bieżący element listy możesz zmienić stan zaznaczenia - nie wiesz jak, obejrzyj sobie przykłady który są w komplecie do VirtualTreeView.
Zdarzenie OnChecked - zmienił się status zaznaczenia, przestaw sobie bieżący element na ten zmieniony - nie wiesz jak, obejrzyj sobie przykłady który są w komplecie do VirtualTreeView.

Pamiętaj że odradzam takie udziwnienia.
Polecenia SQL ma sens przypisać tylko do górnego poziomu drzewa.
Następne poziomy identyfikują wyłącznie parametry tego polecenia.

0

"

Dla gałęzi producent też postaw - CheckBox z tym że 3-state.
Wtedy automagicznie jak pod nim zaznaczono wszystko to jest zaznaczony, ja pod nim nie zaznaczono nic to jest odznaczony, jak pod nim część oznaczona a część nie to on ma stan pośredni. Po prawej stronie też użyj VirtualTreeView.
" - Nie rozumiem twojej myśli -po co wstawiać CheckBoxy przy producencie skoro gałęzie PRODUCENT i TYP mają pozostać Neutralne tzn. po ich kliknięciu nie będzie wykonana żadna akcji. Akcje mają być wykonane tylko po zaznaczeniu gałęzi głównej oraz liści. Dlatego uważam że wstawianie dodatkowych CheckBOXów to tylko niepotrzebne utrudnianie sobie sprawy. Jeśli chodzi o wygląd wizualny końcowej aplikacji to nie za bardzo mogę go zmieniać (aplikacja nie jest dla mnie).

Polecenia SQL ma sens przypisać tylko do górnego poziomu drzewa.
Następne poziomy identyfikują wyłącznie parametry tego polecenia.

  • w jaki sposób miały by modyfikowane te polecenia ?? Mógłbyś podać jakiś przykład ??
0

Przy skomplikowanych opcjach instalacji bardzo często
gałąź z checkboxami sama ma checkboxa który oznacza
zaznacz wszystko pod/odznacz wszystko pod
To jest bardzo wygodne, polecam.
TreeView obsługuje to automatycznie.

Przy
Asus,Creative ... X-POWER
masz przypisane Id tych producentów.

Przy producent przypisujesz napis:
"ProducentId in (${COMALIST}0)"

Przy
USB
PCI-E
PCI
Teź odpowiedni Id.

Przy TYP przypisujesz napis:
"Kind=${EASYLIST}"

Przy Karty Muszycne:
"select * from product where ${ANDLIST} Group=###

SQL generujesz z górnego poziomu po dowolnej gałęzi potomnych.
Skladasz na prostej zasadzie:
${costam} składa się z napisów zazanczonych bezpośrednich gałęzi potomnych.
${COMALIST} z przecinkami np "1" "2" i "3" sklejamy jako "1,2,3,"
${ANDLIST} z napisem and np "Ala" "ma" i "kota" sklejamy jako "Ala and ma and kota and "
${EASYLIST} sklejamy bez żadnych podzielników.

0

Powiedzcie mi jak sprawdzić jaka jest aktualnie zaznaczona gałąź (przy której nie ma ani CheckBOXa ani radioBOXa) ??

0
procedure TfrmMain.Button1Click(Sender: TObject);
var
  PNode: PVirtualNode;
  PData: PVirtualRec;
begin
  PNode:= VirtualStringTree1.GetFirstSelected(False);
  //jezeli zaznaczone istnieje to idziemy w kierunku korzenia w poszukiwaniu
  //pierwszego wezla bez ChceckBox i bez RadioButton
  while Assigned(PNode) and ((PNode.CheckType = ctCheckBox) or
    (PNode.CheckType = ctRadioButton)) do
    PNode:= PNode.Parent;
  if Assigned(PNode) then //znaleziono
  begin
    PData:= VirtualStringTree1.GetNodeData(PNode); //pobieramy dane
    ShowMessage(PData.Caption);
  end;
end;
1
var PNode: PVirtualNode;
  PNode:= VirtualStringTree1.FocusedNode;

To ta gałąź od której poruszamy się myszą i ta która reaguje np na spację.

0

@_13th_Dragon racja z helpa

Q: How can I know which node am I working on?

A: You might want to access the currently FocusedNode to add child nodes to etc. or you might want to use the drop target to act on during a drag'n drop operation etc. But usually you are working on the selection. You have two opportunities to get a list of currently selected nodes. One is the GetFirstSelected/GetNextSelected pair which is really fast but returns the nodes precisely as they are in the internal selection array (which is ordered by memory locations, not logically). Or you can use GetSortedSelection which fills a dynamic array with node references in logical (structural) order.

Racja GetFirstSelected (wraz z GetNextSelected) służy bardziej do uzyskania listy zaznaczonych a do tego powinno się użyć FocusedNode.

0

kAzek - w funkcji którą napisałeś :

 procedure TForm1.VirtualStringTree1Checked(Sender: TBaseVirtualTree;
  Node: PVirtualNode);

  const
  SELECT_SQL = 'Select * From ';
var
  QueryStr: string;
  PData: PVirtualRec;
begin
PData.TableName:=  'NB_KartyMuzyczne';  //linia którą dopisałem do twojego kodu w celu przypisania nazwy tabeli do wyświetlanego komunikatu
  if Assigned(Node) then //to wlasciwie tylko aby dmucha na zimne...
  begin
    if (Node.CheckType = ctRadioButton) and //interesuja nas tylko pola RadioButton
       (Node.CheckState = csCheckedNormal) then  //i tylko gdy sa zaznaczone
    begin
      PData:= VirtualStringTree1.GetNodeData(Node);
      if Assigned(PData) then
      begin
        QueryStr:= SELECT_SQL + PData.TableName;
        //teraz w QueryStr masz cale zapytanie i robisz z nim co chcesz
        ShowMessage(QueryStr); //ja je dla przykladu wyswietle
      end;
    end;
  end;
end;

poz zaznaczeniu jakiegokolwiek radioButtona wyświetla się "Select * From ", i nawet dopisanie przeze mnie linii PData.TableName:= 'NB_KartyMuzyczne'; - treść wyświetlanego tekstu się nie zmienia. A według mnie po wykonaniu powyższego kodu powinien wyświetlać się komunikat : "Select * From 'NB_KartyMuzyczne';.
Pytanie tylko GDZIE JEST BŁĄD ?

1

No jasne że nie będzie bo to

PData.TableName:=  'NB_KartyMuzyczne';  //linia którą dopisałem do twojego kodu w celu przypisania nazwy tabeli do wyświetlanego komunikatu

powinieneś przypisywać analogicznie jak Caption wtedy gdy dodajesz wpis do drzewa i będzie działało świetnie.

Można też później przypisać / zmodyfikować ale jakoś tak tak:

var
  PData: PVirtualRec;
begin
   //Node jest typu PVirtualNode jest to węzel modyfikujesz
  PData:= VirtualStringTree1.GetNodeData(Node);
  PData.TableName:= 'NB_KartyMuzyczne';
end;

Zauważ co zrobiłeś:
Ustawiłeś zmienną na NB_KartyMuzyczne później ją nadpisałeś przez GetNodeData to jak chcesz aby były tam dane?

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