я хочу чтобы у меня после какого-то действия вызывалась процедура OnDrawCell для StringGrid'a.
Налицо ошибка проектирования. Событие не должно вызываться, оно должно происходить. Как только событие произойдет - будет вызван и обработчик события.
Можно посмотреть код, зачем тебе понадобилось вызывать OnDrawCell вручную? А если в это время ячейка, для которой будет вызван OnDrawCell, невидима (не находится на экране, а прокручена, например, вверх)?
Автор: Сергей Меркурьев 27.06.2010 23:52
Что касается видимости ячеек, то этого у меня не произойдет. Поле небольшое и всё размещено на форме под нужные размеры. Я просто хочу сделать так, чтобы у меня сначала нарисовались в каких-либо ячейках изображения. А потом также продолжать их рисовать при определенных действиях. Как я понял OnDrawCell для этого лучше не использовать? И можно ли каким-либо другим способом рисовать в ячейках StringGrid?
For i := 1 to 5 do begin XR := Random (9); YR := Random (9); BallR := Random (7); While Field [XR, YR] = 1 do begin XR := Random (9); YR := Random (9); end; Field [XR, YR] := 1; Rect := Form1.StringGrid1.CellRect(XR, YR); Case BallR of 0 : Form1.StringGrid1.Canvas.Brush.Bitmap := Pink; 1 : Form1.StringGrid1.Canvas.Brush.Bitmap := Green; 2 : Form1.StringGrid1.Canvas.Brush.Bitmap := Yellow; 3 : Form1.StringGrid1.Canvas.Brush.Bitmap := Red; 4 : Form1.StringGrid1.Canvas.Brush.Bitmap := Blue; 5 : Form1.StringGrid1.Canvas.Brush.Bitmap := LightBlue; 6 : Form1.StringGrid1.Canvas.Brush.Bitmap := Brown; end; form1.StringGrid1DrawCell(Sender: TObject; XR, YR: Integer; Rect: TRect; State: TGridDrawState); end;
Автор: volvo 28.06.2010 0:32
Цитата
Как я понял OnDrawCell для этого лучше не использовать? И можно ли каким-либо другим способом рисовать в ячейках StringGrid?
Похоже, ты не понимаешь основ событийного программирования.
Смотри: для того, чтобы рисовать в ячейке Грида, нужно определить обработчик события OnDrawCell. Но вот когда это событие произойдет - не от тебя зависит. Оно произойдет тогда, когда система должна будет перерисовать ячейку. То есть, провел ты над своей программой окно блокнота, когда твое окно снова стало видимым - ячейки должны перерисоваться. Тогда происходит событие, которое вызывает обработчик, написанный тобой. Понимаешь? Не ты вызываешь что-то, а операционная система, зная, что участок окна был невидимым, а потом опять стал видимым, решает: "чтобы не потерять содержимое окна, надо его перерисовать", и делает то, что нужно для этого. Среди прочих действий системы вызывается и определенное тобой в OnDrawCell действие.
То есть, для того, чтобы инициировать перерисовку ячеек, тебе надо действовать другими методами, а не прямым вызовом обработчика OnDrawCell...
Еще раз: Что именно ты хочешь рисовать, и при каких именно (слово "определенных" меня не устраивает, чтобы дать тебе ответ, я просто не смогу ничего посоветовать) условиях ячейки должны перерисовываться, и как именно перерисовываться?
Автор: Сергей Меркурьев 28.06.2010 0:41
Хорошо объясню ситуацию по конкретнее. Спасибо что рассказали про OnDrawCell, просто в интернете мало информации про это и у меня сложилось не правильное мнение о событие.
Когда-то я писал, что делаю небольшую игру. В общем для начала мне нужно вывести несколько изображений в СтрингГрид - это при начальных условиях. А потом после выделения ячейки с изображением передвинуть его в свободное место. После этого действия может произойти две вещи: 1) Добавятся новые изображения на поле (рандом). 2) Часть изображений исчезнут.
Как мне лучше это осуществить?
Автор: volvo 28.06.2010 3:35
Цитата
Как мне лучше это осуществить?
Не использовать StringGrid для работы с изображениями - это во-первых.
Я бы сделал проще: сгенерировал бы (автоматически, разумеется, тебе же известны размеры изображений, раз у тебя "всё размещено на форме под нужные размеры") нужное количество панелей (TPanel), на каждую из которых опять же автоматически положил бы TImage. Вот и все, собственно. TImage приспособлен для работы с изображениями, вот и пусть работает. В любую секунду можно обратиться к любому изображению напрямую, и перенести его с одного места в другое. И не надо извращаться с событиями...
Автор: Сергей Меркурьев 28.06.2010 13:47
Вы везде употребляете слово "автоматически". А как это сделать? Просто опыта пока, что маловато
Автор: Unconnected 28.06.2010 15:46
Ну наверное имелось в виду при запуске программы создать нужное количество панелей и на них закинуть картинки. Если количество картинок заранее известно, можешь создать панели при проектировании вручную, если неизвестно - динамически.
Автор: volvo 28.06.2010 16:03
Когда я говорю "автоматически" - подразумевается, конечно, "во время работы программы". То есть, я не стал бы бросать все эти панели и изображения в режиме редактирования формы, потом с ними замучаешься разбираться, что где. А сделал бы вот так:
type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } arrPanels: array of array of TPanel;
procedure myPanelClick(Sender: TObject); procedure myImageClick(Sender: TObject); public { Public declarations } end;
var Form1: TForm1;
implementation
{$R *.dfm}
const Rows = 10; Cols = 10;
picWidth = 22; picHeight = 22;
procedure TForm1.myPanelClick(Sender: TObject); begin (Sender as TPanel).BevelOuter := bvRaised; end;
procedure TForm1.myImageClick(Sender: TObject); var myParent: TPanel; begin myParent := (Sender as TImage).Parent as TPanel; myParent.OnClick(myParent); end;
procedure TForm1.Button1Click(Sender: TObject); var currTop, currLeft: Integer; i, j: integer; begin SetLength(ArrPanels, Rows, Cols);
currTop := 10; for i := 0 to Pred(Rows) do begin currLeft := 10; for j := 0 to Pred(Cols) do begin // Создаем панельку arrPanels[i, j] := TPanel.Create(Form1); with arrPanels[i, j] do begin Parent := Form1; Width := picWidth; Height := picHeight; Left := currLeft; Top := currTop; BevelOuter := bvNone; OnClick := myPanelClick; end;
// Создаем изображение на панельке with TImage.Create(arrPanels[i, j]) do begin Parent := arrPanels[i, j]; Align := alClient; Picture.LoadFromFile('smile.bmp'); OnClick := myImageClick; end;
// Эта процедура показывает, как можно обращаться к любому изображению. // Достаточно у панельки, его содержащей, найти Component[0]. Поскольку // при создании Image мы его родителя установили в arrPanels[i, j], то // у этого самого arrPanels[i, j] в списке детей появился наш Image... procedure TForm1.Button2Click(Sender: TObject); var img: TImage; begin img := (ArrPanels[2, 2].Components[0] as TImage); if img <> nil then img.Picture := nil; img.Picture.LoadFromFile('blink.bmp'); end;
end.
Вот чего получается на форме после того, как я сначала нажал кнопку "Создать", а потом - "Изменить":
К тому же, как ты заметил я показал еще и как обрабатывать клик по панельке (или по изображению), если понадобится...
По-моему, этот способ гораздо проще, чем кидать компоненты на форму, выравнивать их, да еще и запоминать, какой компонент как называется, или пользоваться неподходящими компонентами типа StringGrid-а.
Добавлено через 3 мин.
Цитата
Если количество картинок заранее известно, можешь создать панели при проектировании вручную
И ты туда же... Ну, брошу я эти 100 картинок вручную. А как потом к ним обращаться? Мне вот надо перенести первую картинку во втором ряду на 4 ряда ниже. Я что, должен помнить, как пронумерованы картинки? А оно мне надо, если можно обращаться к любому изображению, как к обычному элементу массива?
Автор: Сергей Меркурьев 28.06.2010 19:04
Действительно, оказывается всё настолько просто Спасибо за код, но у меня есть ещё пара вопросов по данному коду. Во-первых, что такое SetLength? И для чего он нужен?
img := (ArrPanels[2, 2].Components[0] as TImage); if img <> nil then img.Picture := nil;
Зачем мы пишем Components[0]? И что мы делаем во второй строке? И ещё, вот в процедурах myPanelClick и myImageClick, что мы в них делаем? В myPanelClick, как я понял, мы меняем рамку панели, или я не прав?
Автор: Client 28.06.2010 20:18
Цитата
И ещё, вот в процедурах myPanelClick и myImageClick, что мы в них делаем?
Думаю, что этот обработчик надо установить для всех форм и имеджей на щелчок
Автор: volvo 28.06.2010 21:15
Цитата
Зачем мы пишем Components[0]?
Комментарии к коду читал? Там все написано.
А во второй строке - если у нас уже есть изображение в TImage, то перед тем, как заносить новую картинку, надо прежнюю-то удалить (для этого в img.Picture присваивается nil.)
Цитата
И ещё, вот в процедурах myPanelClick и myImageClick, что мы в них делаем?
Это пример того, как обрабатывать события. Если ты положил на форму панель, то там все просто, заходишь в список обработчиков, щелкаешь на нужный тебе, и вписываешь нужное действие. А если делаешь все во время выполнения? Как назначить обработчик? Выбрать-то не из чего, форма - пустая в дизайнере... Вот я и показал, как назначить обработку событий. Попробуй, запусти мой код и щелкни мышью на какой-нибудь картинке. Увидишь, что событие обработается...
Цитата
этот обработчик надо установить для всех форм и имеджей на щелчок
Не форм, а панелей.
Автор: Сергей Меркурьев 28.06.2010 21:37
Спасибо!
Автор: Сергей Меркурьев 30.06.2010 20:34
А вот еще один вопрос по поводу первых процедур - Допустим, я нажал на определенный компонент TImage, и как в этих процедурах узнать какие координаты имеет TImage (в массиве ArrPanels)?
Автор: volvo 30.06.2010 20:56
Ну, например, написать вот такую вот функцию:
// В классе формы: private { Private declarations } function GetIndexes(AImage: TImage; var ALine, ACol: Integer): Boolean;
// ...
// Реализация: пробегаем по всей матрице Panel-ей, и проверяем // хранящееся в каждой из них изображение (выше мы уже выяснили, // что Image хранится в списке дочерних компонентов под нулевым номером) // на равенство тому изображению, по которому щелкнули мышью.
// Если такое изображение найдено - немедленно возвращаем // его координаты (Line/Col) и выходим. Если ни одно изображение // не соответствует - функция вернет False, как признак неудачи. function TForm1.GetIndexes(AImage: TImage; var ALine, ACol: Integer): Boolean; var i, j: integer; begin result := True; for i := 0 to Length(arrPanels) - 1 do for j := 0 to Length(arrPanels[0]) - 1 do if (arrPanels[i, j].Components[0] as TImage) = AImage then begin ALine := i; ACol := j; Exit; end; result := False; end;
// Вызывать - вот так: procedure TForm1.myImageClick(Sender: TObject); var myParent: TPanel; Line, Col: integer; begin myParent := (Sender as TImage).Parent as TPanel;
// Вызываем функцию с учетом того, что может быть неудача - это нужно делать // всегда, даже если тебе это кажется невероятным. Мало ли что может произойти, // лучше добавить if ... then, чем потом сидеть и думать, где ошибка.
// Теперь о том, что передается первым параметром. Как известно, в обработчик // OnClick передается указатель на тот объект, на котором произошел щелчок мышью. // Соответственно, и в myImageClick тоже будет передан адрес того объекта (а точнее - // того изображения), на котором пользователь щелкнул мышью. Вот именно потому, что // я уверен: "Объект - это именно изображение, и ничего кроме него" (почему я уверен // в этом - попробуй догадаться и рассказать мне), я и передаю в GetIndexes адрес // объекта, приведенный к типу TImage... Что произойдет, если вдруг Sender будет // другого типа - это тоже ты мне расскажешь, договорились?
if GetIndexes(Sender as TImage, Line, Col) then Memo1.Lines.Add(Format('i = %d, j = %d', [Line, Col])); // Для теста - вывожу в мемо
myParent.OnClick(myParent); end;
Читай комментарии внимательно. Там есть два вопроса для тебя Я очень хотел бы, чтобы ты на них ответил. Это важно.
Автор: Сергей Меркурьев 30.06.2010 22:34
А как сделать обмен таких изображений, зная координаты обоих TImage?
И как Sender может быть другого типа в данном случае? Мы же ведь обращаемся конкретно по клику на изображение. И мне кажется, что он не может быть другим
Автор: Client 30.06.2010 23:00
Цитата
А как сделать обмен таких изображений, зная координаты обоих TImage?
А что ты делаешь если надо поменять 2 переменные целого типа? добавляешь 3 переменную того же типа и с ее помощью меняешь первые две. В данном случае - TImage
Автор: Сергей Меркурьев 30.06.2010 23:03
b1 := arrPanels[x,y].Components[0] as TImage; arrPanels[x,y].Components[0] as TImage := arrPanels[i, j].Components[0] as TImage; arrPanels[i, j].Components[0] as TImage := b1;
пытался сделать что-то типа этого... не вышло в итоге
Автор: Client 30.06.2010 23:20
свойство Picture тоже поменяй
Автор: volvo 30.06.2010 23:39
Цитата
пытался сделать что-то типа этого... не вышло в итоге
Этого недостаточно. Такой способ сработает только при простых типах (копирование Integer-ов, строк всяких). А Picture - это сложный тип, его надо копировать по-другому:
procedure TForm1.Button3Click(Sender: TObject);
// Чтоб не таскать за собой все время эту длинную строку - сокращаем: function GetPic(i, j: integer): TPicture; begin result := (ArrPanels[i, j].Components[0] as TImage).Picture; end;
// Это процедура копирования изображения из CopyFrom в CopyTo. // При этом CopyFrom затирается... procedure CopyPic(CopyTo: TPicture; CopyFrom: TPicture); begin CopyTo.Assign(CopyFrom); CopyFrom := nil; end;
var Pic: TPicture; begin // А вот теперь - сам обмен, как и с целыми. Только сначала // надо создать класс нужного типа, а потом его удалить. А так - все как и обычно... Pic := TPicture.Create; CopyPic(Pic, GetPic(4, 4)); CopyPic(GetPic(4, 4), GetPic(2, 2)); CopyPic(GetPic(2, 2), Pic); Pic.Free; end;
Цитата
И как Sender может быть другого типа в данном случае?
Не надо отвечать вопросом на вопрос. Я тебя спросил, почему Sender не может быть другого типа. И что будет, если это произойдет.
Автор: Сергей Меркурьев 1.07.2010 12:42
volvo, в общем-то я затрудняюсь ответить на Ваши вопросы А за код спасибо