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


Автор: Elfin 17.12.2005, 19:40
Возникла проблема с реализацией игры "убери лишнее" .
Условия моей программы такие:
Разработать упрощенную версию игры «Убери лишнее»: на экране появляются объекты –рисунки, среди которых есть лишний. Необходимо указать его. Использовать динамическое создание объектов.

Проблема в том что иногда после выбора нужной картинки вылетает сообщение об ошибке. Не могу найти ее причину. Подскажите в чем она может заключаться?

Привожу код

Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ImgList, ExtCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    ImageList1: TImageList;
    Button1: TButton;
    Edit1: TEdit;
    UpDown1: TUpDown;
    procedure Button1Click(Sender: TObject);
  private
    procedure CtrlClk(sender: tobject);
    procedure freeimages;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure Tform1.CtrlClk(sender: tobject);
begin
if (sender as Timage).Name='Target' then
(SENDER AS TCONTROL).FREE;
end;

procedure Tform1.freeimages;
var i: integer;
begin
  for i:=panel1.ControlCount-1 downto 0  do
  if (panel1.Controls[i] is timage) then
  panel1.Controls[i].FREE;
end;

procedure TForm1.Button1Click(Sender: TObject);
Var
  c,d,I,L: byte; E: TImage;
Begin
  freeimages;
  randomize;
  L:=random(5)+1;
  c:=strtoint(edit1.text);
  d:=random(c)+1;
  E:=nil;
  For I:=0 to c-1 Do
  Begin
    E := TImage.Create(SELF);
    E.OnClick:=CtrlClk;
    E.Parent := panel1;
    E.cursor:=crHandPoint;
    E.Left := (I mod 10) * 64 + 10;
    E.Top := 10+64*(i div 10);
    E.Width:=64;
    E.Height:=64;
    if (I+1)=d then
      begin
        ImageList1.GetBitmap(((L-1+(random(4)+1)) mod 5),E.Picture.bitmap);
        E.Name:='Target'
      end
    else
      begin
        ImageList1.GetBitmap(L-1,E.Picture.bitmap);
        E.Name:='Im'+inttostr(i)
      end;
  End
end;

end.

Автор: Zero 17.12.2005, 21:18
Цитата(Elfin @ 17.12.2005, 20:40)
Проблема в том что иногда после выбора нужной картинки вылетает сообщение об ошибке. Не могу найти ее причину. Подскажите в чем она может заключаться?

При выборе какой картинки, и какая ошибка вылетает?
Лучше вставь всю папку с исходниками (без екзешника), а то не охота разбиратся во всём коде чтобы его откомпилить.(внизу есть кнопка Ответить, после нажатия на которую будет возможность вставлять файлы, Zip-архив)

Автор: Albinos_x 17.12.2005, 21:48
1. какое сообщение об ошибке?
2. в каком месте?

Автор: Elfin 18.12.2005, 10:27
К сообщению прикрепил файл - архив с исходниками 22 кб.

После нажатия на бутон1 происходит очищение панели1 от всех контролов а затем заполнение ее новыми контролами, среди которых один отличается от остальных картинкой и именем Targetб у остальных же имя 'IM'+порядковый номер картинки. При нажати на эту картинку с именем 'Target' она должна удалиться, иногда все нормально получается, а иногда происходит ошибка Acces Violation и код ошибки.

Автор: YurikGL 18.12.2005, 11:11
Может я что не понимаю, но

E.Name:='Target'
Зачем так????

Создай массив Image-в и все будет хорошо.... А в Tag-e храни свое (i)

Если уж очень нужно свою строку хранить, то создай наследника от TImige у которого есть нужное поле... А переименовывать объекты по ходу выполнения это как-то неправильно ИМХО....

А то в твоем случае реально существует только один объект Е и ты его много раз создаешь, меняешь имена, а ссылки на старые объекты теряешь...

Автор: Elfin 18.12.2005, 13:27
Цитата
А в Tag-e храни свое (i)

Это как? что-то я не совсем понял.

OK попробовал массив имагов, тоже не робит.
Код

var
  Form1: TForm1; d: byte; E: array of TImage;

implementation

{$R *.dfm}

procedure Tform1.CtrlClk(sender: tobject);
begin
if (sender as Timage)=E[d-1] then
(SENDER AS TCONTROL).FREE;
//или E[d-1].free;
end;

procedure Tform1.freeimages;
var i: integer;
begin
  for i:=panel1.ControlCount-1 downto 0  do
  if (panel1.Controls[i] is timage) then
  panel1.Controls[i].FREE;
end;

procedure TForm1.Button1Click(Sender: TObject);
Var
  c,I,L: byte;
Begin
  freeimages;
  L:=random(5)+1;
  d:=random©+1;
  c:=strtoint(edit1.Text);
  setlength(e,c);
  for i:=0 to c-1 do
    begin
      E[i]:=Timage.Create(panel1);
      E[i].OnClick:=CtrlClk;
      E[i].Parent := panel1;
      E[i].cursor:=crHandPoint;
      E[i].Left := (I mod 10) * 64 + 10;
      E[i].Top := 10+64*(i div 10);
      E[i].Width:=64;
      E[i].Height:=64;
      if (I+1)=d then
        ImageList1.GetBitmap(((L-1+(random(4)+1)) mod 5),E[i].Picture.bitmap)
      else
        ImageList1.GetBitmap(L-1,E[i].Picture.bitmap);
      panel1.Refresh
    end;
end;

end.

Автор: Quadr0 18.12.2005, 13:44
...

Автор: Elfin 18.12.2005, 17:39
Зачем из мухи делать слона? к чему такие навороты, если в теории алгоритм должен быть проще.
Можно также по координатам вычислять в какую картинку я ткнул мышью, но стоит ли?
Единственноен чего я не понимаю - это откуда ошибка, если теоретически все верно.

Автор: YurikGL 18.12.2005, 18:46
Честно говоря, я так и не понял в чем прикол...
Если Image создается методом "кидания на форму" то вызов free из OnClick этого компонента работает нормально...
Вызов free из других объектов по отношению к динамически созданным Image тоже проходит нормально....
Из экспериментов выяснил, что ошибка возникает не всегда. Иногда это - abstract error иногда - AV....
Похоже на глюки самих делфей...
С другими компонентами, кстати, та же фигня...
А при использовании события OnExit (для TButton) все работает...
Добавлено @ 18:53
В принципе, прблема решилась так:


Код

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);

  private
    procedure clickdel(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  mas: array [1..10] of TImage;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
begin
for i:=1 to 5 do begin
  mas[i]:=TImage.Create(Form1);
  mas[i].Parent:=self;
  mas[i].Left:=i*20;
  mas[i].Width:=15;
  mas[i].OnMouseUp:=clickdel;
  mas[i].Picture.LoadFromFile('c:\windows\Zapotec.bmp');
  mas[i].tag:=i;
end;
end;

procedure TForm1.ClickDel(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
a:integer;
Begin
with (sender as TImage) do a:=tag;

mas[a].Free;
mas[a]:=nil;

End;

end.


т.е. вместо OnClick использовать OnMouseUp

Автор: Zero 18.12.2005, 18:58
Цитата(Elfin @ 18.12.2005, 18:39)
Единственноен чего я не понимаю - это откуда ошибка, если теоретически все верно.

Очень просто, я раньше на тотже кручёк раза три подряд ловился, а г-н Посол, меня всё время исправлял... smile
Короче тут вся фишка в том, что при нажатии на картинку, она удаляет сама себя, что не разрешает ОС Winows, вот и выскакивает ошибка. smile
Короче я чуть-чуть подправил, можешь посмотреть в прикреплённом файле.

Автор: YurikGL 18.12.2005, 19:05
Цитата
Zero (Online)

Сами себе сообщение посылаете? Это, наверное, более правильное решение, чем мое...

Автор: p0s0l 18.12.2005, 22:30
Цитата(Zero @ 18.12.2005, 18:58)
что не разрешает ОС Winows
не совсем... Событие OnClick вызывается из других процедур объекта, и при выходе из OnClick идет дальнейшая работа с этим объектом (например, вызов OnMouseUp), а объекта-то уже нет, уничтожили... Вот и получаются AV...

Автор: YurikGL 18.12.2005, 22:42
Цитата
не совсем... Событие OnClick вызывается из других процедур объекта, и при выходе из OnClick идет дальнейшая работа с этим объектом (например, вызов OnMouseUp), а объекта-то уже нет, уничтожили... Вот и получаются AV...

Хм.... а почему тогда иногда срабатывает без ошибок?

Автор: Elfin 19.12.2005, 04:00
Zero
YurikGL
p0s0l Cпасибо огромное за разъяснения, очень благодарен за помощь, все работает.

ZeroВсе работает, но этот код я не понимаю. Если не трудно, объясни как это работает.

Автор: Zero 19.12.2005, 14:43
Цитата(Elfin @ 19.12.2005, 05:00)
Если не трудно, объясни как это работает.

Короче вся фишка в том, что при выполнении этой строки:
Код

(SENDER AS TImage).FREE;
которая раньше была у тебя, то сразу происходило удаление объекта при нажатии на него, а я вставил строку:
Код

PostMessage (Handle, WM_DELIMG, WPARAM(Sender), 0);
которая вызывает другую процедуру после выполнения всех действий с данным объектом (типа такого)
Цитата(p0s0l @ 18.12.2005, 23:30)
при выходе из OnClick идет дальнейшая работа с этим объектом (например, вызов OnMouseUp),

А в вот этой процедуре:
Код

procedure TForm1.delimage;
begin
  TImage(msg.WParam).Free;
end;
происходит удаление объекта после выполнения всех действий, которые произходят после нажатия (onClick)

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