Вопрос по Windows Forms

Спасибо! Помогло. Я использовал эту статью http://pascalabc.net/stati-po-pascalabc-net/obuchenie-programmirovaniyu/22-modul-formsabc-sozdanie-prostogo-interfejsa Поэтому решил подключить FormsABC. Но теперь возникли другие проблемы. Я использовал метод textbox.addline(). Но он, похоже, был описан как раз в модуле FormsABC. Без него он не объявлен. Каким ещё способом можно добавить в textbox строку?

И ещё: пытаюсь изменить цвет формы следующим образом: form1.backcolor:=clred; Но возникает ошибка: Unit1.pas(54) : Нельзя обратиться к свойству BackColor через тип. Как её исправить и что она означает?

P.S. Я тут новичок. В каком разделе мне можно создавать темы с мелкими вопросами по созданию приложений на PascalABC.NET?

Каким ещё способом можно добавить в textbox строку?

Как я понял, если Вы используете именно проект Windows Forms, то стоит:

  1. Установить свойство Multiline компонента TextBox в True (если это ещё не сделано)
  2. Использовать метод AppendText нужного компонента, и в конце добавить символ перевода строки (хранится как константа System.Environment.NewLine), например, так:
textBox1.AppendText('Hello, line 1!' + System.Environment.NewLine);
    textBox1.AppendText('Hello, line 2!' + System.Environment.NewLine);

Вообще, вместо System.Environment.NewLine можно на платформе Windows использовать последовательность chr(13) + chr(10) (CRLF, аналог “\r\n” из C/C++), но ИМХО делать так не стоит хотя бы из соображений того, что на других платформах может использоваться другая последовательность символов (например, для Linux это будет только chr(10), или “\n”)

И ещё: пытаюсь изменить цвет формы следующим образом: form1.backcolor:=clred; Но возникает ошибка: Unit1.pas(54) : Нельзя обратиться к свойству BackColor через тип. Как её исправить и что она означает?

Судя по исходникам шаблонного проекта, в PascalABC.Net в проектах Windows Forms нет явным образом созданного объекта Form1, как это сделано в Delphi, например. Вместо этого Form1 - это просто тип (правильнее говорить - класс), а нам нужна сама форма - экземпляр этого класса. К счастью, если требуется изменить свойство именно текущей формы, можно использовать ключевое слово self:

self.BackColor := Color.Red;

Кстати, обратите внимание на то, что цвета можно обозначать не через cl*, а через Color.* (редактор после точки откроет окно со всевозможными константами). Ну и если уже заготовленных цветов не хватит - можно воспользоваться методом FromArgb:

self.BackColor := Color.FromArgb(red, green, blue);

где red, green, blue и alpha - компоненты красного, зелёного, синего цветов.

1 лайк

Забыл добавить - self доступен только в методах объекта Form1 (совсем грубо - в процедурах и функциях, начинающихся с Form1.). В принципе, изменять свойства формы вне этих процедур - не очень-то хорошая идея, но если прям очень-очень надо - то можно сохранить self во внешней переменной (но, опять же, идея, прямо скажем, так себе).

1 лайк

У меня почему-то ничего не открывается, хотя intellisense включён на полную.

Не понимаю. Если self доступен только в методах объекта Form1, то как вообще можно изменять свойства формы вне этих процедур, ведь там же это будет просто невозможно?

Как это сделать? И что такое внешняя переменная? И почему идея плохая? (Простите за большое количество вопросов - это моё первое приложение с использованием Forms)

Проверьте свою секцию uses на наличие там System.Drawing. Собственно, тип Color оттуда - попробуйте его вводить как System.Drawing.Color, возможно, тогда подсказки появятся?

Например, так:

  • Создаёте в секции Implementation Вашего unit’a, в котором объявлена форма, переменную типа Form1:
Unit Unit1;

interface
{...}
implementation

var frm: Form1;
{...}
  • При срабатывании события Load формы (обработчик по умолчанию будет называться Form1.Form1_Load) сохраняете в эту внешнюю переменную значение self:
procedure Form1.Form1_Load(sender: Object; e: EventArgs);
begin
  frm1 := self;
end;
  • Теперь можете использовать переменную frm в процедурах, не являющихся частью Form1:
procedure changeFormColor;
begin
  frm1.BackColor := Color.Blue;
end;

Делать так не стоит хотя бы из-за того, что:

  • Нет гарантии, что переменная frm будет хранить нужное значение при вызове процедуры/функции;
  • Вы смешаете логику приложения и его интерфейс, и распутать такой комок будет непросто;
  • Как Вы сами заметили, self будет доступен в методах Form1, и можно никакого огорода не городить.

Если хочется (надо) вынести какую-то часть кода - например, процедуру, рисующую что-либо в каком-то компоненте - лучше передавать нужные объекты (в данном случае, Graphics) как параметр процедуры:

procedure drawSomethingNontrivial(g: Graphics);
begin
  var p := Pen.Create(Color.Red, 1);
  g.Clear(Color.White);
  { ... - здесь происходит отрисовка }
end;

procedure Form1.button1_Click(sender: Object; e: EventArgs);
begin
  { Предполагается, что мы хотим выводить изображение в PictureBox1 }
  drawSomethingNontrivial(PictureBox1.CreateGraphics);  
end;
1 лайк

Большое спасибо за подробные ответы!

Мне не понятно, почему так? Ведь мы записали в неё значение, никакая процедура его не изменяет. Тогда почему оно будет изменяться?

Я правильно понимаю, что интерфейс - это (в данном случае) свойства формы, а логика - это наша новая переменная frm1? И мне не понятно, почему плохо их смешать.

Я решил эту проблему. Оказывается, в исходниках проекта были синтаксические ошибки. И когда я его открывал, то Intellisense отключался почему-то.

Моих знаний не достаточно, чтобы это понять… :confused:

У меня возник новый вопрос: как перевести курсор из одного TextBox в другой при нажатии Enter?

Имейте ввиду, что для работы intellisence требуется, чтобы в файле не было никаких ошибок. И даже в этом случае он иногда отключается просто так.

Спасибо, но я это уже понял:

Форумчане, ответьте, пожалуйста, хотя бы на этот вопрос:

Есть несколько способов перехода по TabIndex, в двух словах - нужно отлавливать событие нажатия на кнопку в поле ввода, и либо генерировать событие нажатия кнопки Tab, либо сразу перенаправлять к следующему контролу.

Я написал такой код:

procedure Form1.textbox2_keypress(sender: Object; e: KeyEventArgs);
begin
if e.KeyCode=Keys.Enter then selectnextcontrol((control)sender,true,true,true,true);
end;

Но он выдаёт ошибку: Unit1.pas(59) : Встречено ‘sender’, а ожидалось ‘)’. Что делать? (Мне нужно перевести курсор из textbox2 в textbox3, сразу перенаправляя к следующему контролу, а не генерируя событие нажатия tab).

наверно надо не (control)sender, а control(sender), это все таки не настолько .NET

Изменил. Теперь ошибка “Ожидалось имя процедуры или функции”. (Ругается на Control(sender)).

А он знает что такое Control? в uses перечислено? Возможно просит полностью писать - System.Windows.Forms.Control

System.Windows.Control написал, не помогает - та же ошибка. Что писать в uses? Там уже есть system.windows.forms.

не System.Windows.Control, а System.Windows.Forms.Control

Да, я так писал. Я в предыдущем сообщении перепутал.

тогда надо попробовать разбить вызов selectNextControl на две строки, сначала преобразовать sender в control, потом уже вызывать саму функцию, чтобы было проще понять на что именно ругается.

var cnt := control(sender);
Control.selectNextControl(cnt,true,true,true,true);

Написал. Ругается на selectnextconrol: Unit1.pas(60) : Mетод SelectNextControl не является статическим для типа System.Windows.Forms.Control

procedure Form2.textBox2_KeyPress(sender: Object; a: KeyPressEventArgs);
begin
   if a.KeyChar = #13 then SelectNextControl(sender as Control, true, true, true, true);
end;

Может, это поможет вам.