powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / формошлёпство? а если заменить class(TForm) на class(TObject) ?
18 сообщений из 18, страница 1 из 1
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392646
Здравствуйте!

Профессионалы рекомендуют отделять бизнес-логику приложения от визуальных элементов и форм, т. е. не делать так, чтобы сама форма и была этой логикой.
Но если я как раз всю логику "нарисую" внутри формы, а потом вместо
Код: pascal
1.
type  TMySuperForm = class(TForm)


напишу

Код: pascal
1.
type  TMySuperForm = class(TObject)

(заодно выкинув все визуальные свойства)
то разве это не будет как раз созданием отдельного невизуального класса реализующего логику ?
(да ещё и созданного вполне себе удобными визуальными средствами IDE в Design time)
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392648
x1ca4064
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Новичок ООП..,

А Вы попробуйте - может что и получится :)

И посмотрите на DataModule
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392671
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторПрофессионалы рекомендуют
Они имеют ввиду, чтобы "под кнопками" больше одной строки кода не бывало. Такой подход позволяет, кроме прочих удобств и порядка в голове, легко сменить интерфейс и сделать несколько интерфейсов.

А как ты логику с помощью "визуальных средств IDE" собрался делать, я не знаю. Всё, что для этого может понадобиться - редактор кода ну и средства отладки.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392730
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Новичок ООП..,

Это называется MVC (Model View Controller).
Среда в Делфи изначально устроена так, что не следует этому принципу.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392741
hottabych31
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Valery_B,

Можно было бы и возразить, но вообще, да. Чтобы реализовать MVC паттерн, нужно больше времени на проектирование. Делфи навязывает сделай это проще и не парься, сейчас. Зато парься потом)
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392750
hottabych31
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Новичок ООП..
Профессионалы рекомендуют отделять бизнес-логику приложения от визуальных элементов и форм, т. е. не делать так, чтобы сама форма и была этой логикой.
Но если я как раз всю логику "нарисую" внутри формы, а потом вместо

Продумай сущности, с которыми будет работать твое приложение, опиши их в отдельных модулях, и обращайся к ним из форм. Это вкратце. И не мудри с type TMySuperForm = class(TObject).
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392762
LSV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hottabych31Делфи навязывает сделай это проще и не парься, сейчас. Зато парься потомЭкая чушь. Ничего не навязывается.
Делфи дает выбор: сделать по-правильному, но медленно, или сделать быстро но не совсем правильно.
Во многих случаях второй вариант единственно возможный (по ряду причин).
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392767
hottabych31
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LSV,

Я в принципе, это и имел ввиду.
В ряде причин 95%
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392797
Кар-Кар
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Компоненты? У меня лично вот че вышло: наследованый класс от класса главной формы в отдельный юнит, но с сохранением оригинальной. В наследованности просто базовая логика, базовые действия.



В оригинальной остается лишь управление базовыми действиями.



Глупость вышла, _наверно_. У меня глобал переменная MyApp: TMyApp (что есть главная форма) и обращаюсь к ней, чтобы "поуправлять", хотя управляльщик-то мой наследник!
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392809
schi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Новичок ООП..Здравствуйте!

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


Фаулера почитай, он довольно толково пишет про этот аспект.

Новичок ООП..Но если я как раз всю логику "нарисую" внутри формы, а потом вместо
Код: pascal
1.
type  TMySuperForm = class(TForm)


напишу

Код: pascal
1.
type  TMySuperForm = class(TObject)

(заодно выкинув все визуальные свойства)
то разве это не будет как раз созданием отдельного невизуального класса реализующего логику ?


Это будет непонятно что и зачем.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392818
Valery_B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кар-Кар,

Зачем ты выкладываешь сюда такой "чудо-код" и тем более в картинках ?
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392820
hottabych31
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не вдаваясь в подробности, настораживает фраза
Кар-Карнаследованый класс от класса главной формы в отдельный юнит, но с сохранением оригинальной.
Зачем? Наследование- жесткая связь. Наоборот, принцип MVC в максимальном отделении бизнес-логики логики от интерфейса.

Ps. Это действительно кар-кар)
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39392839
Кар-Кар
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я у cheatengine мельком пока только просматривал исходники, больше не видел толком большого софта с сырцами и как там все реализовывают. Вы-то как все пишите? Я не знаю, я первый раз программу пытаюсь делать... Так-то все рабочие лошадки написаны уже, надо визуализировать теперь, предоставить контроль человеческому существу.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39394935
stanilar
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Новичок ООП..то разве это не будет как раз созданием отдельного невизуального класса реализующего логику ?

Не надо путать паттерн ООП и приемы разработки.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39513766
Кар-Кар
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hottabych31Не вдаваясь в подробности, настораживает фраза
Кар-Карнаследованый класс от класса главной формы в отдельный юнит, но с сохранением оригинальной.
Зачем? Наследование- жесткая связь. Наоборот, принцип MVC в максимальном отделении бизнес-логики логики от интерфейса.

Ps. Это действительно кар-кар)Заменил TMain на TInterfacedObject. Ниче не изменилось, хотя если посмотреть, то как бы нужно распилить класс на 3 или даже 4(!), если следовать теоретическому ооп. Только дублирование-агрегирование будет в моем случае. Мульти-режимный вариант замутил, чтобы один exe, программа в программе и формочки лепить легко =)

Так че у меня вышло, Класс 3 в 1 (менеджер режимов, частично контроллер(Open/Save диалоги, хранение пути к проекту) и собственно экспорт апи для режимов, вообще он взял на себя все банальные New/Open/Save/Undo/Redo).

Типа апи:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
const
  FACILITY_PROGRAM = $F;

  { Sent when hold is added.

    Remarks: this is not sent to plug-ins and used internally to update undo/redo lists. }
  NOTIFY_PROGRAM_HOLD = HEVENT($000F0010);

  { Sent when mode is changed.
    CallParam is BOOL value:
    • The value TRUE hints plug-ins to start its any work.
    • The value FALSE hints plug-ins to stop its any work. }
  NOTIFY_PROGRAM_MODE = HEVENT($000F0011);

  { Sent when plug-in is loaded.
    CallParam is IPlugin interface:
    • Already registered actual plug-ins may query other interfaces from it for own purposes. }
  NOTIFY_PROGRAM_PLUGIN = HEVENT($000F0012);

  { Sent when modified is updated.

    Remarks: this is not sent to plug-ins and used internally to control * in a scene's name. }
  NOTIFY_PROGRAM_MODIFY = HEVENT($000F0013);

  { Sent when New operation is performed.
    CallParam is empty.
    • The notification hints plug-ins to reset its any data. }
  NOTIFY_PROGRAM_NEW = HEVENT($000F0020);

  { Sent when Open operation is performed.
    CallParam is IStream interface.
    • Loading file from disk, hints plug-ins to load its any data from this stream. }
  NOTIFY_PROGRAM_OPEN = HEVENT($000F0021);

  { Sent when Save operation is performed.
    CallParam is IStream interface.
    • Saving file to disk, hints plug-ins to save its any data to this stream. }
  NOTIFY_PROGRAM_SAVE = HEVENT($000F0022);

  { Sent when Undo operation is performed.

    Remarks: UNDEFINED YET. }
  NOTIFY_PROGRAM_UNDO = HEVENT($000F0023);

  { Sent when Redo operation is performed.

    Remarks: UNDEFINED YET. }
  NOTIFY_PROGRAM_REDO = HEVENT($000F0024);

  { Sent when Project Folder button is clicked.
    CallParam is empty.
    • This is the "Project Folder" button that has no implementation. }
  NOTIFY_PROGRAM_PROJ = HEVENT($000F0025);

  { Sent when file params are required.
    CallParam is PProgramFile structure pointer.
    • Used in open/save dialogs. Note that only the first extension passed to Filter is used. }
  NOTIFY_PROGRAM_FILE = HEVENT($000F0026);

  { Sent when Search Help is performed.

    Remarks: UNDEFINED YET. }
  NOTIFY_PROGRAM_HELP = HEVENT($000F0027);

  { Sent when a hint string is required.
    CallParam is PWideString pointer.
    • Used in the "Project Folder" button. }
  NOTIFY_PROGRAM_HINT = HEVENT($000F0028);

type
  PProgramFile = ^TProgramFile;
  TProgramFile = record
    Title  : WideString;
    Filter : WideString;
  end;

  PProgramHelp = ^TProgramHelp;
  TProgramHelp = record
    Keyword  : WideString;
    Found    : WideString;
    HelpFile : WideString;
  end;

type
  IHold = interface;
  IRestoreObj = interface;
  TRestoreObjProc = procedure (const Data:IInterface) of object;

  IProgram = interface
  ['{1A01E0DC-51AB-43D1-99FC-D0B5BCD4CE3D}']
    function Handle:THandle;
    function Mode:TGUID;
    function Modified(const Make:BOOL=True):BOOL;
    procedure New;
    procedure Open;
    procedure Save;
    procedure SaveAs;
    function Undo(const Count:Integer;const Execute:BOOL):BOOL;
    function Redo(const Count:Integer;const Execute:BOOL):BOOL;
    procedure Shutdown;
    function Hold(const Proc:TRestoreObjProc):IHold;
    function Message(const Text:WideString;const Flags:DWORD):Integer;
  end;

  //UNDEFINED YET
  IHolding = interface
  ['{800EFAB5-B286-4439-9D34-865161B6EF14}']
    function Hold(const Proc:TRestoreObjProc):IHold;
  end;

  IHold = interface
  ['{54BAA75C-BF45-4B4A-97E4-6A52B3E7FDBA}']
    procedure Accept(const Name:WideString;const Data:IInterface);
    procedure Cancel;
  end;

  //UNDEFINED YET
  IRestoreObj = interface
  ['{17703F5D-7836-4ADB-98DB-20D597F888AA}']
    function Holding:Byte;
    function Name:WideString;
    procedure Restore;
  end;

  TPluginFunc = function:IInterface;
  IPlugin = interface
  ['{C800BE91-C6CF-4F95-B4B5-18CC7B08D298}']
    function ClassName:WideString;
    function ClassInfo:WideString;
    function ClassGuid:TGUID;
  end;

  IPluginMode = interface
  ['{45BFBB9A-C9FF-48EA-9236-E6C530E1F0A1}']
    procedure Place(const Handle:THandle);
    procedure Placement(const Left,Top,Height,Width:Integer);
    procedure Activate;   //does not work?
    procedure Deactivate; //does not work?
    procedure Notify(const Event:HEVENT;const CallParam:Pointer);
  end;

function _IProgram:IProgram;external 'Program.exe';

Реализация:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
  TProgram = class(TInterfacedObject, IProgram, IPlugin)
  public type

    THold = class(TInterfacedObject, IHold, IRestoreObj)
    strict private
      FName : WideString;
      FData : IInterface;
      FProc : TRestoreObjProc;
      FPuts : IVector<IRestoreObj>;
      FEdit : Int64;
      FFlag : Byte;
    public
      constructor Create(const Proc:TRestoreObjProc;const Edit:Int64);
      procedure Accept(const Name:WideString;const Data:IInterface);
      procedure Cancel;
      function Holding:Byte;
      function Name:WideString;
      procedure Restore;
    end;

    TPlugin = class
    strict private
      FPlugin  : IPlugin;
      FModule  : HMODULE;
      FModFull : WideString;
      FModPath : WideString;
      FModName : WideString;
      FName    : WideString;
      FInfo    : WideString;
      FGuid    : TGUID;
      FMode    : IPluginMode;
    public
      constructor Create(const Module:HMODULE;const Plugin:IPlugin);
      destructor Destroy;override;
      procedure Place(const Handle:THandle);
      procedure Placement(const Left,Top,Height,Width:Integer);
      procedure Activate;
      procedure Deactivate;
      procedure Notify(const Event:HEVENT;const CallParam:Pointer);
      property ModFull:WideString read FModFull;
      property ModPath:WideString read FModPath;
      property ModName:WideString read FModName;
      property Name:WideString read FName;
      property Info:WideString read FInfo;
      property Guid:TGUID read FGuid;
    end;

  strict private
    FNotify       : INotify;
    FIDs          : IVector<TGUID>;
    FCurrent      : TPlugin;
    FModes        : IVector<TPlugin>;
    FPlugins      : IVector<TPlugin>;
    FHolding      : IVector<IRestoreObj>;
    FHistory      : IHistory<IRestoreObj>;
    FScene        : WideString;
    FSaveDialog   : BOOL;
    FSaveRequired : BOOL;
  strict private
    class constructor Create;
    class destructor Destroy;
    function ClassName:WideString;
    function ClassInfo:WideString;
    function ClassGuid:TGUID;
  protected
    function _AddRef:Integer;stdcall;
    function _Release:Integer;stdcall;
    function GetScene(const FileName,Filter:WideString):WideString;
  public
    constructor Create;
    destructor Destroy;override;
    procedure ChangeMode(const ID:TGUID);
    procedure LoadPlugin(const FileName,FuncName:WideString);
    function Handle:THandle;
    function Mode:TGUID;
    function Modified(const Make:BOOL=True):BOOL;
    procedure Shutdown;
    function Hold(const Proc:TRestoreObjProc):IHold;
    procedure New;
    procedure Open;
    procedure Save;
    procedure SaveAs;
    function Undo(const Count:Integer;const Execute:BOOL):BOOL;
    function Redo(const Count:Integer;const Execute:BOOL):BOOL;
    function Message(const Text:WideString;const Flags:DWORD):Integer;
    procedure Broadcast(const Event:HEVENT;const CallParam:Pointer);
    procedure Register(const Event:HEVENT;const Param:NativeUInt;const Proc:TNotifyProc);
    procedure Unregister(const Event:HEVENT;const Param:NativeUInt;const Proc:TNotifyProc);
    procedure Subscribe(const Facility:Word;const Param:NativeUInt;const Proc:TNotifyProcEx);
    procedure Unsubscribe(const Facility:Word;const Param:NativeUInt;const Proc:TNotifyProcEx);
    property Current:TPlugin read FCurrent;
    property Modes:IVector<TPlugin> read FModes;
    property Plugins:IVector<TPlugin> read FPlugins;
    property History:IHistory<IRestoreObj> read FHistory;
    property Scene:WideString read FScene;
  end;

const
  Prog : TProgram = nil;

implementation

uses
  FMode, FReport, FReport2;

{$REGION 'TProgram'}
class constructor TProgram.Create;
begin
PPointer(@Prog)^ := TProgram.Create;
end;

class destructor TProgram.Destroy;
begin
MIFreeAndNil(PPointer(@Prog)^);
end;

function TProgram.ClassName:WideString;
begin
Result := 'PROGRAM';
end;

function TProgram.ClassInfo:WideString;
begin
Result := '';
end;

function TProgram.ClassGuid:TGUID;
begin
Result := IProgram;
end;

constructor TProgram.Create;
begin

{ Constructing. }
FNotify := _INotify;
FIDs := IVector<TGUID>(_IVector(_IT(TYPE_GUID)));
FModes := IVector<TPlugin>(_IVector(_IT(TYPE_CLASS)));
FPlugins := IVector<TPlugin>(_IVector(_IT(TYPE_CLASS)));
FHolding := IVector<IRestoreObj>(_IVector(_IT(TYPE_INTF)));
FHistory := IHistory<IRestoreObj>(_IHistory(_IT(TYPE_INTF)));

{ Making standard mode. }
FIDs.Add(IProgram);
FCurrent := FModes[FModes.Add(TPlugin.Create(HInstance,Self as IPlugin))];
end;

destructor TProgram.Destroy;
var
I : NativeInt;
P : TPlugin;
begin

{ Removing plug-ins. }
for I := FPlugins.Count-1 downto 0 do
begin
  P := FPlugins.Extract(I);
  FPlugins.Remove(P);
  P.Free;
end;

{ Setting current mode in zero position. }
FModes.Exchange(0,FModes.IndexOf(FCurrent));

{ Removing mode plug-ins. }
for I := FModes.Count-1 downto 0 do
  FModes.Extract(I).Free;

FCurrent := nil;
end;

function TProgram._AddRef:Integer;
begin
Result := -1;
end;

function TProgram._Release:Integer;
begin
Result := -1;
end;

function TProgram.GetScene(const FileName,Filter:WideString):WideString;
var
Extension : WideString;
begin
Extension := MIFileExtW(Filter);
if MISameText(MIFileExtW(FileName), Extension) then
  Result := FileName else
  Result := FileName + Extension;
end;

procedure TProgram.ChangeMode(const ID:TGUID);
var
M : TPlugin;
begin
if FCurrent.Guid <> ID then
  for M in FModes do
    if M.Guid = ID then
      if Modified(False) then
      begin
        Broadcast(NOTIFY_PROGRAM_MODE,Pointer(BOOL(False)));

        FScene := '';
        FSaveRequired := False;
        FCurrent := M;
        FHistory.Delete;
        FHolding.Clear;

        Broadcast(NOTIFY_PROGRAM_MODE,Pointer(BOOL(True)));
        Break;
      end
      else
        Break;
end;

procedure TProgram.LoadPlugin(const FileName,FuncName:WideString);
var
P      : TPlugin;
ID     : TGUID;
Module : HMODULE;
Plugin : IPlugin;
begin
Module := LoadLibrary(PWideChar(FileName));
if Module <> 0 then
try
  { Rapid approach. }
  if TPluginFunc(GetProcAddress(Module,PWideChar(FuncName))).QueryInterface(IPlugin,Plugin) <> S_OK then
  begin
    FreeLibrary(Module);
    Exit;
  end;

  P := TPlugin.Create(Module,Plugin);
  ID := P.Guid;

  { Looking for plug-in id duplicates. }
  if FIDs.Contains(ID) then
  begin
    MessageBox(Application.MainFormHandle,
      PWideChar('Plug-in '+MIGuidToStrW(ID,'<')+' from <'+MIFileNameW(FileName)+
      '> has duplicate plug-in ID: not loading.'),PWideChar('Error Loading Plug-In'),
      MB_OK or MB_ICONWARNING or MB_TOPMOST or MB_SETFOREGROUND);
    FreeLibrary(Module);
    Exit;
  end;

except
  MessageBox(GetDesktopWindow,
    PWideChar('Failed to load plug-in from <'+MIFileNameW(FileName)+'>.'),
    PWideChar('Error Loading Plug-In'),
    MB_OK or MB_ICONERROR or MB_TOPMOST or MB_SETFOREGROUND);
  if Module <> 0 then
    FreeLibrary(Module);
  Exit;
end else
begin
  MessageBox(GetDesktopWindow,
    PWideChar('Could not load module <'+MIFileNameW(FileName)+'>.'),
    PWideChar('Error Loading DLL'),
    MB_OK or MB_ICONERROR or MB_TOPMOST or MB_SETFOREGROUND);
  Exit;
end;

{ Registering. }
FIDs.Add(ID);

if MISupports(Plugin,IPluginMode) then
  FModes.Add(P) else
  FPlugins.Add(P);

Broadcast(NOTIFY_PROGRAM_PLUGIN,Pointer(Plugin));
end;

function TProgram.Handle:THandle;
begin
Result := Application.Handle;
end;

function TProgram.Mode:TGUID;
begin
Result := FCurrent.Guid;
end;

function TProgram.Modified(const Make:BOOL=True):BOOL;
begin
if Make then
begin
  Result := FSaveRequired;
  if not FSaveRequired then
  begin
    FSaveRequired := True;
    Broadcast(NOTIFY_PROGRAM_MODIFY,Pointer(BOOL(True)));
  end;
end else
begin
  if FSaveRequired then
    case Message('The scene has been modified.'+#13#10+'Do you want to save your changes?',MB_YESNOCANCEL or MB_ICONQUESTION or MB_TOPMOST) of
    ID_YES:
      begin
        Save;
        Result := not FSaveRequired;
      end;
    ID_NO:  Result := True;
    else    Result := False;
    end
  else Result := True;
end;
end;

procedure TProgram.Shutdown;
begin
Application.MainForm.Close;
end;

function TProgram.Hold(const Proc:TRestoreObjProc):IHold;
begin
Modified;
Result := FHolding[FHolding.Add(THold.Create(Proc,FHolding.Count) as IRestoreObj)] as IHold;
end;

procedure TProgram.New;
begin
if FSaveRequired then
begin
  case Message('The scene has been modified.'+#13#10+'Do you want to save your changes?',MB_YESNOCANCEL or MB_ICONQUESTION or MB_TOPMOST) of
  ID_YES:
    begin
      Save;
      if FSaveRequired then
        Exit;
    end;
  ID_NO:  FSaveDialog := False;
  else    Exit;
  end;
end;
FScene := '';
FSaveRequired := False;
Broadcast(NOTIFY_PROGRAM_NEW,nil);
end;

procedure TProgram.Open;
var
Dlg    : TProgramFile;
Stream : IStreamMemory;
begin

{ Save confirm. }
if FSaveRequired then
begin
  case Message('The scene has been modified.'+#13#10+'Do you want to save your changes?',MB_YESNOCANCEL or MB_ICONQUESTION or MB_TOPMOST) of
  ID_YES: Save;
  ID_NO:  FSaveDialog := False;
  else    Exit;
  end;
  if FSaveDialog then
    Exit;
end;

{ Requesting title and filter for open/save dialogs. }
Dlg.Title := 'File';
Broadcast(NOTIFY_PROGRAM_FILE,@Dlg);
with TOpenDialog.Create(nil) do
try

  { Building. }
  Options := Options + [ofPathMustExist, ofFileMustExist];
  Title := 'Open ' + Dlg.Title;
  Filter := Dlg.Filter;
  FileName := FScene;

  { Reexecute until break. }
  while True do
    if Execute(Application.MainFormHandle) and (FileName <> '') then
      if Succeeded(MIFileExists(FileName)) then
      begin
        New;
        FScene := FileName;
        Stream := _IStreamMemory;
        if Succeeded(Stream.Load(FileName)) then
          Broadcast(NOTIFY_PROGRAM_OPEN,Pointer(Stream)) else
          Message('Access denined.', MB_OK or MB_ICONERROR or MB_TOPMOST or MB_SETFOREGROUND);
        Break;
      end else Message('File '+FileName+' not found.'+#13#10+'Please verify that the correct file name was given.',MB_OK or MB_ICONERROR or MB_TOPMOST or MB_SETFOREGROUND)
    else Break;
finally
  Free;
end;

end;

procedure TProgram.Save;
begin
if (FScene <> '') and not MIDirExists(MIFilePathW(FScene)) then
begin
  FSaveDialog := True;
  MessageBox(Application.MainFormHandle,PWideChar('Path '+MIFilePathW(Scene)+' is missing.'),'Attention',MB_OK or MB_ICONERROR or MB_TOPMOST or MB_SETFOREGROUND);
end else
  FSaveDialog := Failed(MIFileExists(FScene));
if FSaveDialog then SaveAs;
end;

procedure TProgram.SaveAs;
var
Dlg     : TProgramFile;
Stream  : IStreamMemory;
Aborted : BOOL;
begin

Aborted := True;

{ Requesting title and filter for open/save dialogs. }
Dlg.Title := 'File';
Broadcast(NOTIFY_PROGRAM_FILE,@Dlg);

with TSaveDialog.Create(nil) do
try

  { Building. }
  Options := Options + [ofPathMustExist];
  Title := 'Save ' + Dlg.Title;
  Filter := Dlg.Filter;
  FileName := MIFileNameW(FScene);

  { Reexecute until break. }
  while True do
    if Execute(Application.MainFormHandle) and (FileName <> '') then
    begin
      if Succeeded(MIFileExists(FileName)) then
        case Message(FileName+' already exists.'+#13#10+'Do you want to replace it?',MB_YESNO or MB_ICONWARNING or MB_TOPMOST or MB_SETFOREGROUND) of
        ID_YES: ;
        ID_NO:
          begin
            FileName := MIFileNameW(FileName);
            Continue;
          end;
        else    Break;
        end;
      FScene := GetScene(FileName,Dlg.Filter);
      Aborted := False;
      Break;
    end else Break;

finally
  Free;
end;

if Aborted then
  Exit;

{ Saving. }
Stream := _IStreamMemory;
Broadcast(NOTIFY_PROGRAM_SAVE,Pointer(Stream));
if Succeeded(Stream.Save(FScene)) then
begin
  FSaveRequired := False;
  Broadcast(NOTIFY_PROGRAM_MODIFY,Pointer(BOOL(False)));
end else
begin
  FSaveRequired := True;
  Broadcast(NOTIFY_PROGRAM_MODIFY,Pointer(BOOL(True)));
  Message('Access denied.',MB_OK or MB_ICONERROR or MB_TOPMOST or MB_SETFOREGROUND);
end;
end;

function TProgram.Undo(const Count:Integer;const Execute:BOOL):BOOL;
begin
Result := (Count > 0) and (Count <= FHistory.Undo.Count);
if Result and Execute then
  Broadcast(NOTIFY_PROGRAM_UNDO,Pointer(Count));
end;

function TProgram.Redo(const Count:Integer;const Execute:BOOL):BOOL;
begin
Result := (Count > 0) and (Count <= FHistory.Redo.Count);
if Result and Execute then
  Broadcast(NOTIFY_PROGRAM_UNDO,Pointer(Count));
end;

function TProgram.Message(const Text:WideString;const Flags:DWORD):Integer;
begin Result := MessageBox(Application.MainFormHandle,PWideChar(Text), PWideChar(FCurrent.Name),Flags)end;

procedure TProgram.Broadcast(const Event:HEVENT;const CallParam:Pointer);
begin FNotify.Broadcast(Event,CallParam);end;

procedure TProgram.Register(const Event:HEVENT;const Param:NativeUInt;const Proc:TNotifyProc);
begin FNotify.Register(Event,Param,Proc);end;

procedure TProgram.Unregister(const Event:HEVENT;const Param:NativeUInt;const Proc:TNotifyProc);
begin FNotify.Unregister(Event,Param,Proc);end;

procedure TProgram.Subscribe(const Facility:Word;const Param:NativeUInt;const Proc:TNotifyProcEx);
begin FNotify.Subscribe(Facility,Param,Proc);end;

procedure TProgram.Unsubscribe(const Facility:Word;const Param:NativeUInt;const Proc:TNotifyProcEx);
begin FNotify.Unsubscribe(Facility,Param,Proc);end;
{$ENDREGION}

{$REGION 'TProgram.THold'}
constructor TProgram.THold.Create(const Proc:TRestoreObjProc;const Edit:Int64);
begin
FProc := Proc;
FEdit := Edit;
end;

procedure TProgram.THold.Accept(const Name:WideString;const Data:IInterface);
var
i : NativeInt;
r : IRestoreObj;
begin

{ Multiple accepting is not allowed. }
if FFlag > 0 then
  Exit;

{ Assigning information about this hold. }
FName := Name;
FData := Data;
FFlag := 1;

{ Super / Simple hold. }
if FEdit > 0 then
  Exit;

with Prog do
begin
  FPuts := IVector<IRestoreObj>(_IVector(_IT(TYPE_INTF)));
  for i := 0 to FHolding.Count-1 do
  begin
    r := FHolding[i];
    if r.Holding = 1 then
      FPuts.Add(r);
  end;
  FHistory.Store(Self as IRestoreObj);
  FHolding.Clear;
  Broadcast(NOTIFY_PROGRAM_HOLD,nil);
end;
end;

procedure TProgram.THold.Cancel;
var
i : NativeInt;
begin

{ Multiple canceling is not allowed. }
if FFlag = 2 then
  Exit;
with Prog do
  for i := FHolding.Count-1 downto FEdit do
    FHolding.Extract(i).Restore;
FFlag := 2;
end;

function TProgram.THold.Holding:Byte;
begin
Result := FFlag;
end;

function TProgram.THold.Name:WideString;
begin
Result := FName;
end;

procedure TProgram.THold.Restore;
var
I : NativeInt;
begin
{ Super hold. }
if Assigned(FPuts) then
  for I := 0 to FPuts.Count-1 do
    FPuts[I].Restore
else
{ Simple hold. }
if (FFlag = 1) and Assigned(FProc) then
  FProc(FData);
end;
{$ENDREGION}

{$REGION 'TProgram.TPlugin'}
constructor TProgram.TPlugin.Create(const Module:HMODULE;const Plugin:IPlugin);
begin
{ Module. }
FModule := Module;
SetLength(FModFull,MAX_PATH);
SetLength(FModFull,GetModuleFileName(Module,PWideChar(FModFull),MAX_PATH));
MIFilePathNtW(FModFull,FModPath);
MIFileNameNtW(FModFull,FModName);

{ Plugin. }
FPlugin := Plugin;
FName := FPlugin.ClassName;
FInfo := FPlugin.ClassInfo;
FGuid := FPlugin.ClassGuid;
if FPlugin.QueryInterface(IPluginMode,FMode) <> S_OK then FMode := nil;
end;

destructor TProgram.TPlugin.Destroy;
begin

end;

procedure TProgram.TPlugin.Place(const Handle:THandle);
begin
if Assigned(FMode) then
try
  FMode.Place(Handle);
except

end;
end;

procedure TProgram.TPlugin.Placement(const Left,Top,Height,Width:Integer);
begin
if Assigned(FMode) then
try
  FMode.Placement(Left,Top,Height,Width);
except

end;
end;

procedure TProgram.TPlugin.Activate;
begin
if Assigned(FMode) then
try
  FMode.Activate;
except

end;
end;

procedure TProgram.TPlugin.Deactivate;
begin
if Assigned(FMode) then
try
  FMode.Deactivate;
except

end;
end;

procedure TProgram.TPlugin.Notify(const Event:HEVENT;const CallParam:Pointer);
begin
if Assigned(FMode) then
try
  FMode.Notify(Event,CallParam);
except

end;
end;
{$ENDREGION}

function _IProgram:IProgram;
begin Result := Prog as IProgram;end;

exports
_IProgram;

Контроллер форма, этот 3 в 1 посылает сообщения только контроллеру, а он уже отправляет текущему режиму, заодно че-то делает. Чисто якобы WndProc:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
procedure TMain.WndProg(const Event:HEVENT;const Param:NativeUInt;const CallParam:Pointer);
var
M : TProgram.TPlugin;
H : WideString;
V : Boolean;
I : Integer;
begin
case Event of
NOTIFY_PROGRAM_MODE:
begin
  if BOOL(CallParam) then
  begin
    { In "PROGRAM" (standard) mode no quick access controls appear by design. Empty mode. }
    V := Prog.Mode <> IProgram;
    QATNew.Visible := V;
    QATOpen.Visible := V;
    QATSave.Visible := V;
    QATUndo.Visible := V;
    QATUndoArrow.Visible := V;
    QATRedo.Visible := V;
    QATRedoArrow.Visible := V;
    QATProj.Visible := V;
    QATMode.Visible := True;
    Menu.Visible := not V;
    Platform.Visible := V;
    if not V then
    begin
      QATL.Constraints.MaxWidth := 17;
      NcCaption := '';
      NcDocument := '';
    end else
    begin
      Prog.Current.Notify(NOTIFY_PROGRAM_HINT,@H);
      QATProj.Hint := H;
      QATL.Constraints.MaxWidth := 185;
      NcCaption := Prog.Current.Name;
      NcDocument := 'Untitled';
    end;
    Resize;
    Repaint;
  end else
  begin
    FUndo.Tree.Items.Clear;
    FRedo.Tree.Items.Clear;
    with Screen do
      for I := 0 to FormCount-1 do
        if Forms[I].InheritsFrom(TPopup) then
          Forms[I].Hide;
    Prog.Current.Place(0);
  end;
  Prog.Current.Notify(NOTIFY_PROGRAM_MODE,CallParam);
end;
NOTIFY_PROGRAM_PLUGIN:
  begin
    for M in Prog.Modes do
      M.Notify(NOTIFY_PROGRAM_PLUGIN,CallParam);
  end;
NOTIFY_PROGRAM_MODIFY:
  begin
    I := Length(NcDocument);
    if BOOL(CallParam) and ((I = 0) or (NcDocument[1] <> '*')) then
      NcDocument := '*' + NcDocument  else
    if not BOOL(CallParam) and (I > 0) and (NcDocument[1] = '*') then
      if I = 1 then
        NcDocument := '' else
        NcDocument := Copy(NcDocument,2,I-1);
    Resize;
  end;
NOTIFY_PROGRAM_NEW:
  begin
    NcDocument := 'Untitled';
    Resize;
    Prog.Current.Notify(NOTIFY_PROGRAM_NEW,CallParam);
  end;
NOTIFY_PROGRAM_OPEN:
  begin
    NcDocument := MIFileNameW(Prog.Scene);
    Resize;
    Prog.Current.Notify(NOTIFY_PROGRAM_OPEN,CallParam);
  end;
NOTIFY_PROGRAM_SAVE:
  begin
    NcDocument := MIFileNameW(Prog.Scene);
    Resize;
    Prog.Current.Notify(NOTIFY_PROGRAM_SAVE,CallParam);
  end;
NOTIFY_PROGRAM_UNDO:
  begin
    Revert(FUndo,Integer(CallParam));
  end;
NOTIFY_PROGRAM_REDO:
  begin
    Revert(FRedo,Integer(CallParam));
  end;
NOTIFY_PROGRAM_FILE:
  begin
    Prog.Current.Notify(NOTIFY_PROGRAM_FILE,CallParam);
  end;
end;
end;

Тут уже плагин WndProc:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
procedure TPlugin.Notify(const Event:HEVENT;const CallParam:Pointer);
var
PFile : PProgramFile absolute CallParam;
begin
case Event of
NOTIFY_PROGRAM_MODE:
  begin
    if BOOL(CallParam) then
    begin
      //Resume
    end else
    begin
      //Suspend
    end;
  end;
NOTIFY_PROGRAM_NEW:
  begin
    //Nothing to reset yet
  end;
NOTIFY_PROGRAM_OPEN:
  begin
    //Nothing to load yet
  end;
NOTIFY_PROGRAM_SAVE:
  begin
    //Nothing to save yet
  end;
NOTIFY_PROGRAM_PROJ:
  begin
    Prog.Modified(True); //just for fun, putting scene into modified state so
                         //user is asked about saving
  end;
NOTIFY_PROGRAM_FILE:
  begin
    PFile.Title := 'Sql scene';
    PFile.Filter := 'Sql scene|*.sqlscene';
  end;
NOTIFY_PROGRAM_HINT:
  begin
    PWideString(CallParam)^ := 'Sql Forum.';
  end;
end;
end;

А это у него меню:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
procedure TSql.ManagerExecute(Action: TBasicAction; var Handled: Boolean);
var
A : TAction;
begin
try
  Handled := Assigned(Action) and (Action is TAction);
  if not Handled then Exit;
  A := Action as TAction;
except
  Handled := False;
  Exit;
end;
if A = actNew then
  Prog.New else
if A = actOpen then
  Prog.Open else
if A = actSave then
  Prog.Save else
if A = actSaveAs then
  Prog.SaveAs else
if A = actExit then
  Prog.Shutdown else
if A = actUndo then
  Prog.Undo(1,True) else
if A = actRedo then
  Prog.Redo(1,True)
else
  Handled := False;
end;

procedure TSql.ManagerUpdate(Action: TBasicAction; var Handled: Boolean);
var
A : TAction;
begin
try
  Handled := Assigned(Action) and (Action is TAction);
  if not Handled then Exit;
  A := Action as TAction;
except
  Handled := False;
  Exit;
end;
if Action = actNew then
  A.Enabled := True else
if Action = actOpen then
  A.Enabled := True else
if Action = actSave then
  A.Enabled := True else
if Action = actSaveAs then
  A.Enabled := True else
if A = actExit then
  A.Enabled := True else
if A = actUndo then
  A.Enabled := Prog.Undo(1,False) else
if A = actRedo then
  A.Enabled := Prog.Redo(1,False)
else
  Handled := False;
end;





Победа. Победа? Я победил? Вариант, "эээ.. а, похерсайдет" меня устроит)

Крик, статьи статьи статьи, понаписали, теоретики, а реальной практики нет, кода нет. Без понятия че делать, когда теперь придется прикручивать листы, списки, инфу, реакции, сообщения, циклические сообщения, двусторонние.... Все мантайнить надо и натыкаться на проблемы.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39513825
Фотография krapotkin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ну, cудя по замаху, в конце и должна получиться копия VCL )))
ознакомьтесь например с такой реализацией отделения интерфейса от логики
YouTube Video
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39514491
Кар-Кар
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ссылку вообще не видел, пока постить не начал(( NoScript стоит. А 3 дня назад ютуб перестал работать, тырить с компа че-то начали видимо, а у меня SYSTEM-DENIED на доступ к истории, посещенным линкам и прочее, вот и блокируют мне)) <паранойа>

krapotkin, спасибо. Не уверен подходит ли мне, ну, чтобы кодить, а то вообще не знаком с DB и Unit-тестами.
Насчет разделения кажется есть теория, почему так пишут. Есть данные, загрузили на форму, все. Первый инстинкт: ну раз загрузили, вот они все данные на форме, то давай онклик, ончейндж и т.п. Тем более если форма знает как данные показывать, то ясень пень она знает как их сохранять, редактировать и вообще родственные души. Потом просто считаем с контролов и готово, а разделять это типа для run-time чисто.

У меня есть ~2000 файлов разных форматов, друг на друга ссылаются в разных ключах и секциях, зависимость у них колоссальная т.е. даже рефакторинг возможен, и класс возвращающий интерфейс для работы с каким-либо файлом:
Код: pascal
1.
2.
3.
4.
...
    function FileInterface(const Index:Int64;const IID:TGUID;out Intf):BOOL;
    function FilesCount:Int64;
...

Там и TInIReader, и TStream и всякое, только вот они никаких сообщений не посылают. Редактируй свободно. Единственный вариант, явно логгировать, что изменил и слать классу, чтобы в случае чего он там пофиксил или вдруг рефракторинг. Потом обратно пришлет это сообщение, да ещё может с увеличеным array of notification, якобы там ты значение, говоришь, изменил, так вот его пришлось ещё в 200 файлах подправить и пару новых добавить. Давай обновляй UI.

Для низкоуровневого редактирования, но оставаясь в гуще событий. И сделал бы просто notify_file_updated, только прога от перерисовок загнулась бы... если только форму не считать подобием кэша. If FValue <> Value then. Буду думать.
...
Рейтинг: 0 / 0
формошлёпство? а если заменить class(TForm) на class(TObject) ?
    #39514788
Кар-Кар
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вспомнил

Прилинкованный UInt64, хэндл, от 1 до 18446744073709551616. Меняется что-то (секция, ключ, нода) и становится новый хэндл для параметра или группы параметров, AtomicInc(FHandle). Сиди только в потоке и фильтруй "все ли на месте/что-то новенькое", отправляй на монитор.

Дольше, но не проблемы класса, зато свободно редактировать можно ни о чем не парясь, кому надо начнет фильтровать.
отличный конец недели
...
Рейтинг: 0 / 0
18 сообщений из 18, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / формошлёпство? а если заменить class(TForm) на class(TObject) ?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]