Форум: "Основная";
Текущий архив: 2002.12.30;
Скачать: [xml.tar.bz2];
ВнизСвойства не по умолчанию Найти похожие ветки
← →
Дмитрий К.К. (2002-12-17 09:30) [0]Здравствуйте!
Задача такова: необходимо вывести в текстовый файл названия всех компонентов на форме.
Делаю следующим образом:
var
I: Integer;
F: TextFile;
begin
AssignFile(F, "components.txt");
try
Rewrite(F);
for I := 0 to ComponentCount-1 do
Writeln(F, Components[I].Name);
finally
CloseFile(F);
end;
end;
Но это только треть задачи. Помимо этого, нужно записать в файл:
1) экземплярами каких классов являются эти компоненты;
2) (и самое главное) вывести свойства (и события) этих компонентов, которые не равняются тем, что выставлены по умолчанию (то есть приблизительно то, что записывается в dfm).
Подскажите, пожалуйста, как сие реализовать.
← →
MBo (2002-12-17 09:32) [1]2) Components[I].ClassName
← →
Дмитрий К.К. (2002-12-17 09:39) [2]Спасибо... а относительно свойств?
← →
stone (2002-12-17 10:07) [3]GetPropInfo
← →
Юрий Зотов (2002-12-17 10:23) [4]> Подскажите, пожалуйста, как сие реализовать.
Надо просто записать точно такой же DFM, который пишет сама Delphi, теми же самыми средствами, что и она.
const
FileName = "Form1.txt";
procedure TForm1.FormCreate(Sender: TObject);
var
i: integer;
begin
RegisterClass(TFormClass(ClassType));
for i := 0 to ComponentCount - 1 do
RegisterClass(TComponentClass(Components[i].ClassType))
end;
procedure TForm1.SaveButtonClick(Sender: TObject);
var
MemStream: TMemoryStream;
FileStream: TFileStream;
begin
MemStream := TMemoryStream.Create;
try
MemStream.WriteComponent(Self);
MemStream.Position := 0;
FileStream := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
try
ObjectBinaryToText(MemStream, FileStream);
finally
FileStream.Free
end
finally
MemStream.Free
end;
end;
procedure TForm1.LoadButtonClick(Sender: TObject);
var
FileStream: TFileStream;
MemStream: TMemoryStream;
Clone: TForm1;
begin
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
FileStream.Position := 0;
MemStream := TMemoryStream.Create;
try
ObjectTextToBinary(FileStream, MemStream);
MemStream.Position := 0;
Clone := TForm1.CreateNew(nil);
try
MemStream.ReadComponent(Clone);
with Clone do
begin
Hide;
Name := "Clone";
Caption := Name;
SetBounds(Left + 10, Top + 10, Width + 50, Height + 50);
ShowModal
end
finally
Clone.Free
end
finally
MemStream.Free
end
finally
FileStream.Free
end
end;
← →
han_malign (2002-12-17 10:27) [5]примерно так, в хелпе я ничего не нашел так-что открывай Delphi\Source\Vcl\TypeInfo.pas - и разбирайся
uses TypeInfo;
.........
var numProp: Integer;
PropList: PPropList;
.........
numProp:=GetPropList(Components[i].TypeInfo,[tkUnknown..tkDynArray],nil);
GetMem(PropList,sizeof(PPropInfo)*numPorp);
GetPropList(Components[i].TypeInfo,[tkUnknown..tkDynArray],PropList);
for i:=0 to numProp do with PropList[i]^ do begin //TPorpInfo
end;
З.Ы. А на самом деле выдираешь эту самую GetPropList из TypeInfo.pas, убираешь оттуда фильтрацию по типу - и вперед - это как раз то что тебе нужно.
← →
Юрий Зотов (2002-12-17 10:32) [6]Забыл добавить объявление формы:
TForm1 = class(TForm)
Edit1: TEdit;
SaveButton: TButton;
LoadButton: TButton;
procedure SaveButtonClick(Sender: TObject);
procedure LoadButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Меняя текст в Edit1 между нажатиями Save и Load, можно убедиться, что клон создается именно из файла.
← →
han_malign (2002-12-17 10:48) [7]тогда уж проще - RxLib.RX Tools.FormStorage
← →
Дмитрий К.К. (2002-12-17 11:13) [8]Юрий, спасибо... но немного не то...
Нужно не именно то, что пишет сама среда в dfm, а свой порядок записи:
AssignFile(F, SavFileName);
try
Rewrite(F);
for I := 0 to ComponentCount - 1 do
begin
Writeln(F, Components[I].Name + ": " + Components[I].ClassName);
...
Writeln(F, "-----");
end;
finally
CloseFile(F);
end;
Там, где многоточие, нужно записать то, что изменено в свойствах компонентов при редактировании в Инспекторе объектов (другими словами, что отличается от выставляемого по умолчанию).
Все это нужно для того, чтобы затем написать одну общую процедуру в редакторе кода (то есть Button1.Caption := "Save" и т.д.).
← →
Юрий Зотов (2002-12-17 14:52) [9]> нужно записать то, что изменено в свойствах компонентов...
> ...другими словами, что отличается от выставляемого по
> умолчанию.
Именно так и поступает Delphi. Именно так и работает пример.
← →
Дмитрий К.К. (2002-12-17 15:18) [10]Юрий, не смею спорить, что так и работает Ваш пример (поскольку сам его проверял).
Но... то, как переводит бинарник в строку метод ObjectBinaryToText, кажется немного не тем, что надо...
object Panel1: TPanel
Left = 0
Top = 374
Width = 611
Height = 41
Align = alBottom
BevelInner = bvLowered
и т.д.
Совсем другое дело - следующее представление:
Panel1.Left := 0;
Panel1.Top := 374;
Panel1.Width := 611;
Panel1.Height := 41;
Panel1.Align := alBottom;
Panel1.BevelInner := bvLowered;
Поэтому нет ли других альтернатив Вашему методу (например, тех, что предлагает han_malign)?
← →
Юрий Зотов (2002-12-17 17:42) [11]Ну, во-первых, это метод не мой, а, скорее, Борландовский. Я лишь повторил в явном виде то же самое, что делает сама Delphi, когда пишет свои DFM.
Во-вторых, альтернатива, конечно, есть - именно то, о чем говорил han_malign. Использование RTTI для "ручного" сохранения и чтения. Если Вам это кажется лучше - никто не запрещает. Но советую сначала посмотреть исходники TStream.WriteComponent (и ReadComponent). Вы убедитесь, что код там совсем не детский - а ведь Вам придется сделать примерно то же самое.
Так что выбор за Вами - либо тот формат, который Вам больше нравится, либо простота и надежность кода. Я бы выбрал второе - тем более, что родной Дельфишный формат не менее прост и понятен, чем тот, который Вы привели (а по объему текста еще и короче). К тому же, он универсален для любых компонентов.
← →
Vcoder (2002-12-17 19:14) [12]Может конечно это и не то, но мне вот что пришло на ум:
Записать файл по типу dfm (как предложил Юрий Зотов), а затем уже ЕГО приводить в читабельный вид.
← →
Дмитрий К.К. (2002-12-17 19:54) [13]
> Я бы выбрал второе - тем более, что родной Дельфишный формат
> не менее прост и понятен, чем тот, который Вы привели (а
> по объему текста еще и короче).
И здесь спорить не буду. Однако формат "текстовый dfm", записанный в редакторе коде, естественно, не примет к рассмотрению компилятор Delphi.
Моя задача - выжать из наполненной компонентами формы все, что можно (и нужно), а затем в пустой форме в обработчике события OnCreate усё ето воссоздать. Можно, конечно, загрузить все эти компоненты из внешнего файла, сохраненного при помощи Борландовского метода, но требуется реализовать другим способом.
← →
Бурундук (2002-12-17 20:16) [14]Вот пример перебора всех published св-в,
нашёл в каких-то своих извращениях:
(писать что-то конкретное по твоей теме мне облом -
хлопотно это больно)
Разобраться, думаю, можно.
procedure UpgradeComponent(OldCmp: TComponent; NewCmpClass: TComponentClass);
var NewCmp: TComponent;
CmpName: string;
Cls: TClass;
i: Integer;
PropCnt: Integer;
PropList: PPropList;
PropInfo: PPropInfo;
function IsField(PropProc: Pointer): Boolean;
type ba = array[0..3]of Byte;
begin
Result := ba(PropProc)[3] > $FE;
end;
begin
if not NewCmpClass.InheritsFrom(OldCmp.ClassType) then
raise Exception.CreateFmt("Cannot upgrade %s to %s",
[OldCmp.ClassName, NewCmpClass.ClassName]);
NewCmp := NewCmpClass.Create(OldCmp.Owner);
PropCnt := GetTypeData(OldCmp.ClassInfo)^.PropCount;
GetMem(PropList, PropCnt*SizeOf(Pointer));
try
GetPropInfos(OldCmp.ClassInfo, PropList);
for i := 0 to PropCnt-1 do
begin
PropInfo := PropList^[i];
if (PropInfo^.Name <> "Name") then
begin
if PropInfo^.PropType^^.Kind = tkMethod then
SetMethodProp(NewCmp, PropInfo,
GetMethodProp(OldCmp, PropInfo))
else if PropInfo^.PropType^^.Kind = tkClass then
begin
Cls := GetTypeData(PropInfo^.PropType^).ClassType;
if (PropInfo^.Name = "Constraints") then
// По непонятной причине свойство
// property Constraints: TSizeConstraints read FConstraints write FConstraints;
// производит запись прямо в поле, а не через Set-метод + Assign
// Поэтому приходится делать обходной маневр.
TPersistent(GetObjectProp(NewCmp, PropInfo)).Assign
(TPersistent(GetObjectProp(OldCmp, PropInfo)))
else
begin
SetObjectProp(NewCmp, PropInfo,
GetObjectProp(OldCmp, PropInfo));
end;
end
else
SetPropValue(NewCmp, PropInfo^.Name, GetPropValue(OldCmp, PropInfo^.Name));
end;
end;
finally
FreeMem(PropList, PropCnt*SizeOf(Pointer));
end;
CmpName := OldCmp.Name;
if OldCmp is TControl then
with TControl(OldCmp) do
begin
TControl(NewCmp).Parent := Parent;
TControl(NewCmp).Visible := Visible;
TControl(NewCmp).Enabled := Enabled;
Hide;
end;
OldCmp.Free;
NewCmp.Name := CmpName; // -> NewCmp.SetReference
end;
Но учти:
Св-ва типа ссылки на компоненты (не на объекты-потомки TPersistent, принадлежащие объекту) ты получаешь из RTTI
в виде указателя, а сохранять их надо как имена
(ну не получится иначе) - а потом, соответственно, по имени
находить.
И то же самое для tkMethod - обработчиков событий.
Если есть желание разбираться с десятком частных случаев -
что ж, флаг тебе в руки, typinfo.pas в зубы и вперёд.
Но, чесно говоря, я не понимаю, чем тебе стандартное сохранение/чтение не нравится.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2002.12.30;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.008 c