Jak w jednej procedurze obsłużyć odwołanie do 40-tu obiektów TPanel?

0

Mam 40 obiektów TPanel, dla każdego potrzebuję obsłużyć OnMoseDown - jak zrobić to rekurencyjnie?
Kod do obsługi w zdarzeniu OnMouseDown:

If (Button=mbLeft) And (IleKul<7) Then Begin
    If P02.BevelInner=bvNone Then Begin
      With P02 Do Begin
        BevelInner:=bvLowered;
        BevelOuter:=bvNone;
        Color:=clHighlight;
        Font.Color:=clYellow;
      End;
      Inc(IleKul);
    End Else Begin
      With P02 Do Begin
        BevelInner:=bvNone;
        BevelOuter:=bvRaised;
        Color:=clBtnFace;
        Font.Color:=clBlack;
      End;
      Dec(IleKul);
    End;
  End;
  If IleKul<6 Then ZapiszLosN1.Enabled:=False Else ZapiszLosN1.Enabled:=True;
0

Te 40 paneli jest jeden z drugiem i kolejne?
Wiesz co to jest rekurencja?

2

Wyszukujesz wszystkie panele na formie, np. przerabiając tą procedurę: https://4programmers.net/Forum/1598705 i dla każdego przypisujesz zdarzenie OnMouseDown

PanelX.OnMouseDown:=MouseDownProc;

Gdzie zawartością MouseDownProc jest podany przez Ciebie kod

1
pstmax napisał(a):

Mam 40 obiektów TPanel, dla każdego potrzebuję obsłużyć OnMoseDown […]

A co do znaczy „obsłużyć”? Bo ja pod tym pojęciem rozumiem to, że każdy z tych paneli ma zdefiniowane zdarzenie OnMouseDown i chcesz w takim zdarzeniu coś wykonać. W takim przypadku każdemu panelowi przypisuje się jedno zdarzenie, a w jego kodzie zamiast bazować na nazwach kontrolek, korzysta się z argumentu Sender, który zawiera referencję klikniętego panelu.

Natomiast jeśli z jakiegoś powodu potrzebujesz odszukać wszystkie te panele i jeśli wszystkie czterdzieści paneli znajduje się wewnątrz jednego komponentu-rodzica, to tak jak zasugerował @Paweł Dmitruk, możesz je wyszukać iterując po kontrolkach i testując ich klasę.

[…] - jak zrobić to rekurencyjnie?

Rekurencja przyda się tylko w przypadku, gdy niektóre z tych paneli służą do grupowania innych, a te inne do kolejnych – w przeciwnym razie wystarczy wyszukiwanie iteracyjne.

Aby móc wykonać wyszukiwanie rekurencyjne, należy sobie zadeklarować metodę, która w parametrze przyjmie referencję komponentu-rodzica, w którym owe panele się znajdują (bezpośrednio lub pośrednio). Przy wywołaniach rekurencyjnych w parametrze przekazuje się odnalezione komponenty. Przykład niżej:

procedure TMainForm.EnumeratePanels(AParent: TWinControl);
var
  InnerControl: TControl;
  InnerPanel: TCustomPanel absolute InnerControl;
var
  ControlIndex: Integer;
begin
  for ControlIndex := 0 to AParent.ControlCount - 1 do
  begin
    InnerControl := AParent.Controls[ControlIndex];

    if InnerControl.InheritsFrom(TCustomPanel) then
      // tu modyfikujesz panel za pomocą "InnerPanel"

    if InnerControl.InheritsFrom(TWinControl) then
      EnumeratePanels(InnerControl);
  end;
end;

Przykład pierwszego wywołania:

procedure TMainForm.EnumerateButtonClick(ASender: TObject);
begin
  EnumeratePanels(Self);
end;

Jeszcze jedna sprawa:

  If IleKul<6 Then ZapiszLosN1.Enabled:=False Else ZapiszLosN1.Enabled:=True;

12 lat zajmujesz się programowaniem i nie wiesz, że instrukcja warunkowa sprowadza się do wartości logicznej, którą możesz bezpośrednio przypisać do zmiennej logicznej? Przecież wystarczy tyle:

ZapiszLosN1.Enabled := IleKul >= 6;

Poza tym nie pisz kodu po polsku, a słowa kluczowe pisz małymi literami. No i nie zostawiaj begin na końcu linii z nagłówkiem – przenoś begin do nowej linii. Kod (tak jak wszystko inne) czyta się od lewej do prawej, a skoro po lewej nie ma begin to ten kod wygląda tak, jakby wewnętrzne instrukcje w ogóle nie były grupowane.

0

Witam wszystkich,
Dziękuję za pomoc. Stare przyzwyczajenia swoje robią. Zawsze kod był dla mnie bardziej czytelny, gdy umieszczałem begin na końcu wiersza, ponieważ łatwiej znaleźć i domknąć taki begin, ale to tak jak napisałem każdy ma swoje przyzwyczajenia. Zawsze byłem "estetą" jeżeli chodzi o pisanie kodu i tak się "wkręciłem", że słowa kluczowe piszę z Wielkiej litery. Takie moje spaczenie. :-)

Generalnie "bawię się" w programik, który ma umożliwić zaznaczenie tylko 6 dowolnych pozycji z 49 (lotto 6 z 49) z możliwością dowolnego ich modyfikowania zanim nie kliknę przycisku zapisz.
Dziękuję za każdą sugestię i wskazówkę. Poniżej zrzut ekranu:screenshot-20190622122546.png

0

No właśnie jak już Panowie napisali i powiedzieli przysłowiowe A, to może pociągnę jeszcze troszeczkę ten temat. Powiedzmy, że mam taką sytuację:
screenshot-20190622124244.png
Czyli mam liczby 1,5,7,8,10,13 i teraz zmieniam 8 powiedzmy na 9 - chciałbym aby ciąg po zmianie wyglądał w ten sposób: 1,5,7,9,10,13 mają Panowie jakieś sugestie?
Moje programowanie jest całkowicie hobbistyczne i to, że przynależę już 12 lat do 4programmers.net niestety nie oznacza, że tyle lat intensywnie programuję (a szkoda).

1
pstmax napisał(a):

Czyli mam liczby 1,5,7,8,10,13 i teraz zmieniam 8 powiedzmy na 9 - chciałbym aby ciąg po zmianie wyglądał w ten sposób: 1,5,7,9,10,13 mają Panowie jakieś sugestie?

Z tego wychodzi, że jeśli jest już zaznaczonych dokładnie sześć liczb, to przy zaznaczeniu siódmej program ma znaleźć najbliższą, która jest mniejsza od nowo zaznaczonej i ją odznaczyć. Nic trudnego. Problem nie polega na odznaczaniu nadmiarowej liczby, a na organizacji komponentów i ustawieniu ich w taki sposób, aby kodu nie trzeba było pisać dużo.


Sugeruję abyś sobie zadeklarował listę obiektów (przycisków) i np. w konstruktorze okna wpisał referencje wszystkich tych przycisków do tej listy. Dzięki temu komponenty wyszuka się raz, a później będzie można bazować wyłącznie na tej dodatkowej liście. Zdarzenie OnChange wygeneruj tylko dla pierwszego przycisku i przypisz je wszystkich pozostałym. Odstawiam, że używasz przycisków klasy TToggleBox, więc to zdarzenie będzie odpowiednie.

Czyli po kolei. Najpierw zadeklaruj pomocniczą listę dla referencji tych przycisków i kilka metody służace do jej utworzenia, wypełnienia i zwolnienia. W Delphi powinno to wyglądąć tak:

type
  TMainForm = class(TForm)
  {..}
  private
    FNumberBoxes: TList<TToggleBox>;
  private
    procedure InitNumberBoxes();
    procedure FillNumberBoxes(AParent: TWinControl);
    procedure DoneNumberBoxes();
  {..}
  end;

{..}

procedure TMainForm.InitNumberBoxes();
begin
  FNumberBoxes := TList<TToggleBox>.Create();
end;

procedure TMainForm.FillNumberBoxes(AParent: TWinControl);
var
  InnerControl: TControl;
  ControlIndex: Integer;
begin
  for ControlIndex := 0 to AParent.ControlCount - 1 do
  begin
    InnerControl := AParent.Controls[ControlIndex];

    if InnerControl is TToggleBox then
      FNumberBoxes.Add(InnerControl as TToggleBox);
  end;
end;

procedure TMainForm.DoneNumberBoxes();
begin
  FNumberBoxes.Free();
end;

Teraz w konstruktorze i destruktorze wywołaj te metody:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  InitNumberBoxes();
  FillNumberBoxes(NumberBoxesPanel);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  DoneNumberBoxes();
end;

Ten NumberBoxesPanel to nazwa panelu, w którym osadzone są przyciski z numerkami (i tylko one). Aby wiedzieć ile przycisków jest zaznaczonych w danym momencie, przyda się dodatkowe pole:

type
  TMainForm = class(TForm)
  {..}
  private
    FNumberBoxesChecked: Integer;
    {..}
  end;

Teraz należy wygenerować zdarzenie OnChange pierwszego przycisku i przypisać to zdarzenie do wszystkich przycisków – dzięki temu będzie można w jednym miejscu reagować na kliknięcie dowolnego. No i w tym zdarzeniu odpowiednio reagujemy:

procedure TMainForm.ToggleBox1Change(Sender: TObject);
var
  Box: TToggleBox absolute Sender;
  BoxIndex: Integer;
begin
  if Box.Checked then
    Inc(FNumberBoxesChecked)
  else
    Dec(FNumberBoxesChecked);

  if FNumberBoxesChecked > 6 then
    for BoxIndex := FNumberBoxes.IndexOf(Box) - 1 downto 0 do
      if FNumberBoxes[BoxIndex].Checked then
      begin
        FNumberBoxes[BoxIndex].Checked := False;
        Break;
      end;
end;

Najpierw sprawdzamy czy kliknięty przycisk został zaznaczony czy odznaczony i na tej podstawie inkrementujemy lub dekrementujemy licznik. Następnie sprawdzamy czy wciśniętych przycisków jest więcej niż 6 i jeśli tak, szukamy w pętli najbliższego wciśniętego przycisku, od indeksu tego klikniętego w kierunku początku listy. Po znalezieniu odznaczamy ten przycisk i kończymy pętlę.

Ważne jest to, aby lista zawierała referencje w kolejności rosnącej. I to wszystko.


Jeśli ktoś chce się pobawić to do załączników dodaję testowy projekt. Ale że Delphi nie używam, to projekt ten przeznaczony jest dla Lazarusa. W każdym razie działa prawidłowo.

0

Z tego wychodzi, że jeśli jest już zaznaczonych dokładnie sześć liczb, to przy zaznaczeniu siódmej program ma znaleźć najbliższą

Niestety nie to miałem na myśli. Po prostu jak masz do wpisania 6 liczb z 49 to może się zdarzyć, iż się pomylisz i "klikniesz" nie tą liczbę. Chciałbym móc dowolnie poprawiać te 6 liczb. Czyli:

  1. Wybieram 6 liczb z 49 i mogę w dowolnej chwili zmienić mój wybór, chyba, że nacisnę ZAPISZ.
  2. W Delphi 10.3.1 nie ma kontrolki ToggleButton dlatego moja "zabawa" z TPanel i obsługą OnMouseDown.

Chodzi o to, że mamy kolejność wylosowanych liczb.

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