ButtonWPF, Click. Варианты назначения обработчика

Двумерный массив Cells[3, 3] содержит записи типа Cell.

Каждая запись состоит из кнопки b и процедуры ClickCell, выдающей текст кнопки.

Кнопки создаются, и на клик каждой кнопки навешивается процедура из этой же записи.

Два варианта назначения процедур.

1 вариант) Назначение процедур происходит вне цикла:

uses WPFObjects, Controls;

type
  Cell = record
    b: ButtonWPF;
    constructor(b: ButtonWPF) := self.b := b;
    procedure ClickCell := MessageBox.Show('Нажата кнопка ' + b.Text);
  end;

var
  Cells := new Cell[3, 3];  
  w := 100.0; // CellWidth=CellHigh

begin
  Window.Width := 3 * w;
  Window.Height := 3 * w;
  
  for var i := 0 to 2 do
    for var j := 0 to 2 do
      Cells[i, j] := new Cell(new ButtonWPF(w * j, w * i, $'{i},{j}', w, 70));
  
  Cells[0, 0].b.Click := procedure -> Cells[0, 0].ClickCell;
  Cells[0, 1].b.Click := procedure -> Cells[0, 1].ClickCell;
  Cells[0, 2].b.Click := procedure -> Cells[0, 2].ClickCell;
  Cells[1, 0].b.Click := procedure -> Cells[1, 0].ClickCell;
  Cells[1, 1].b.Click := procedure -> Cells[1, 1].ClickCell;
  Cells[1, 2].b.Click := procedure -> Cells[1, 2].ClickCell;
  Cells[2, 0].b.Click := procedure -> Cells[2, 0].ClickCell;
  Cells[2, 1].b.Click := procedure -> Cells[2, 1].ClickCell;
  Cells[2, 2].b.Click := procedure -> Cells[2, 2].ClickCell;
end.

– назначение происходит правильно. Проверим: нажмем, например, кнопку [0,1] – срабатывает соответствующая процедура: 1

2 вариант) Назначение процедур происходит в цикле:

uses WPFObjects, Controls;

type
  Cell = record
    b: ButtonWPF;
    constructor(b: ButtonWPF) := self.b := b;
    procedure ClickCell := MessageBox.Show('Нажата кнопка ' + b.Text);
  end;

var
  Cells := new Cell[3, 3];  
  w := 100.0; // CellWidth=CellHigh

begin
  Window.Width := 3 * w;
  Window.Height := 3 * w;
  
  for var i := 0 to 2 do
    for var j := 0 to 2 do
    begin
      Cells[i, j] := new Cell(new ButtonWPF(w * j, w * i, $'{i},{j}', w, 70));
      Cells[i, j].b.Click := procedure -> Cells[i, j].ClickCell;
    end;
end.

– назначение происходит НЕправильно. Какую бы кнопку ни нажал – срабатывает одна и та же процедура, а именно, процедура для кнопки [2, 2]: 2

Вариант 2 компактный, и не хочется от него отказываться. Но почему он работает неправильно? Объясните, пожалуйста. Спасибо!

Вам не нужно procedure -> - присвоить надо процедуру, а ClickCell ею уже является.

Не работает по вашему потому что вы создаёте новую лямбду, которая в свой контейнер захватывает i и j, которые когда вы уже тыкаете - докрутились до своих конечных значений. Это конкретно цикл for такой, он увеличивает существующую переменную, а к примеру foreach создаёт новую для каждой итерации.

А вообще вы неправильно ООП используете. Выбор текста кнопок и присвоение обработчика должны быть в конструкторе вашей записи, а параметрами надо передавать минимум инфы - i и j. Тогда они в каждом вызове конструктора буду отдельными переменными, независимо от цикла.

Вариант с foreach действительно работает. Но лучше вовсе без него: назначать обработчик не снаружи, а внутри.

Последовал вашему совету, упрятал назначение обработчика в конструктор. Получилось компактно и красиво, снаружи ничего лишнего, как и должно быть в ООП. ))

Спасибо!

uses WPFObjects, Controls;

var 
  w := 100.0; // CellWidth = CellHight

type
  Cell = record
    b: ButtonWPF;
    constructor(i, j : integer);
    begin
      b := new ButtonWPF(w * j, w * i, $'{i},{j}', w, 70);
      b.Click := procedure -> ClickCell;
    end;
    procedure ClickCell := MessageBox.Show('Нажата кнопка ' + b.Text);
  end;

var
  Cells := new Cell[3, 3]; 

begin
  Window.Width := 3 * w;
  Window.Height := 3 * w;
  
  for var i := 0 to 2 do
    for var j := 0 to 2 do
      Cells[i, j] := new Cell(i, j);
      //Cells[i, j].b.Click := procedure -> Cells[i, j].ClickCell;
    
  //foreach var x in Cells do
  //  x.b.Click := procedure -> x.ClickCell;
end.

w лучше бы тоже константой внутри записи. Да и вместо глобальной переменной - можно статическое поле All внутри записи. А инициализировать его с MatrGen.

Ну и таки уберите procedure->, оно лишнее делает. ClickCell уже и так процедурой является.
Ну или можно обойтись без метода, присвоить ()->MessageBox(...);. Лямбда это и есть анонимная/одноразовая подпрограмма. Объявлять имя есть смысл если вы в нескольких местах в коде эту подпрорамму вызывать будете.

uses WPFObjects, Controls;

type
  Cell = record
    static w := 100.0;
    b: ButtonWPF;
    constructor(i, j: integer);
    begin
      b := new ButtonWPF(w * j, w * i, $'{i},{j}', w, 70);
      //b.Click := ClickCell;
      b.Click := ()-> MessageBox.Show('Нажата кнопка ' + b.Text);
    end;
    //procedure ClickCell := MessageBox.Show('Нажата кнопка ' + b.Text);
  end;

begin
  Window.Width := 3 * Cell.w;
  Window.Height := 3 * Cell.w;
  var Cells := MatrGen(3, 3, (i, j) -> new Cell(i, j));
end.

Как, однако, всё компактно получается!

Спасибо!

Примеры с графическими объектами – те, что я видел, – там обработчик клика всегда определялся через лямбду. Вот я и стал думать, что в графике обработка событий только через лямбду.

поле All

– что вы имели ввиду? Что за All?

All это название. Придумайте сами что лучше. Статическое поле это поле перед которым стоит слово static. Оно привязано не к экземпляру, а к самому классу. Как w в вашем последнем коде, потому что все константы сразу статические.

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

Ну, это мелочь и не во всех случаях целесообразно, но я имею в виду что массив который сейчас Cells можно тоже внутрь типа засунуть.

Cells, состоящий из элементов типа Cell, засунуть в тип Cell?! Круто! Но, наверное, это для какого-то другого случая. А вообще - поэкспериментирую. Спасибо! Хорошо. Доволен, что заглянул сюда. ))

Лучше сюда писать [Telegram: Contact @PABCofficial] там Вам с большей вероятностью оперативно ответят

1 лайк

Так куда еще оперативнее?! И здесь Sun_Serega мне всегда и оперативно отвечает, и со знанием дела, и не в первый раз уже. Спасибо, Samurai, загляну и в Телеграм ))