Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.02.12;
Скачать: CL | DM;

Вниз

TFontProperty.Edit;   Найти похожие ветки 

 
DimaBr   (2005-08-25 10:42) [0]

Здравствуйте !
Помогите получить значение свойства  - объекта.
Имеется компонент Test и свойстово Cell

type TAligment = (alLeft,alRight,alTop,alBottom,alValue);
type
 TCell = class(TPersistent)
   private
     fAligment: TAligment;
     fPosition: integer;
   protected
     procedure SetPosition(Value: integer);
   public
     constructor Create;
     destructor Destroy;override;
   published
     property Aligment: TAligment read fAligment write fAligment;
     property Position: integer read fPosition write SetPosition;
end;

type
 TTest = class(TComponent)
   private
     fCell : TCell;
   protected
   public
     constructor Create(AOwner:TComponent);override;
     destructor Destroy;override;
   published
     property Cell: TCell read fCell write fCell;
end;

А также его редактор

type TCellProperty = class(TClassProperty)
 public
   procedure Edit; override;
   function GetAttributes: TPropertyAttributes; override;
 end;

procedure TCellProperty.Edit;
begin
 with TCellEditorFrm.Create(Application) do
 try
   ACell := TCell(GetOrdValue);   <- ошибка
   if Execute then SetOrdValue(Longint(ACell));
 finally
   Free;
 end;
end;


Посмотрел на реализацию TFont и написал тоже самое. Только вот никак не могу понять, почему в TFont используется GetOrdValue, ведь свойтсво Font - НЕ перечислимого типа


procedure TFontProperty.Edit;
var
 FontDialog: TFontDialog;
begin
 FontDialog := TFontDialog.Create(Application);
 try
   FontDialog.Font := TFont(GetOrdValue);
   FontDialog.HelpContext := hcDFontEditor;
   FontDialog.HelpType := htContext;
   // TODO 4 -oCPJ: Figure out what these TFont dialog options mean for CLX
   // FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
   if FontDialog.Execute then SetOrdValue(Longint(FontDialog.Font));
 finally
   FontDialog.Free;
 end;
end;


 
Яся   (2005-08-25 11:23) [1]

Font - указатель, значит целое, а значит GetOrdValue самое то


 
DimaBr   (2005-08-25 11:43) [2]

У меня в строке

ACell := TCell(GetOrdValue);

вылетает AV, причём ACell к тому времени уже создан и проинициализирован.


 
Яся   (2005-08-25 12:55) [3]

Copies the contents of another, similar object.
procedure Assign(Source: TPersistent); virtual;

Note: The types of some properties are also objects. If these properties have write methods that use Assign to set the value of the property, then in these cases the statement "Destination := Source" is the same as "Destination.Assign(Source)".
Это как раз относиться к свойству Font


 
Юрий Зотов ©   (2005-08-25 13:15) [4]

1. > ACell к тому времени уже создан и проинициализирован.

Зачем? Вам же нужно редактировать именно тот экземпляр объекта, который создан компонентом, лежащим на форме (cсылку на него и дает GetOrdValue). Так зачем же создавать другой? Да еще потом и затирать ссылку на него, организуя тем самым утечку памяти.

Второй экземпляр объекта бывает нужен, если мы хотим в диалоге редактирования реализовать кнопку "Отмена". Тогда - да, тогда мы создаем копию и редактируем ее, а при нажатии "ОК" переносим свойства копии в свойства искомого объекта. Но если Вы хотели сделать именно это, то код неверен, а правильно было бы так:

procedure TCellProperty.Edit;
var
 Source: TCell;
begin
 Source := TCell(GetOrdValue); // Ссылка на искомый объект
 with TCellEditorFrm.Create(Application) do
 try
   ACell.Assign(Source); // Инициализация копии
   if Execute then
     Source.Assign(ACell) // Изменения вступают в силу
 finally
   Free
 end
end;

Здесь предполагатся, что форма редактора сама создает (а потом уничтожает) ACell и что в классе TCell перекрыт метод AssignTo.

2. TAlignment - очень советую назвать как-то иначе. Такой тип в VCL уже есть и поэтому программа, использующая Ваш компонент, окажется зависимой от порядка перечисления модулей в uses. Это может привести к багам, причем локализовать такие баги обычно бывает очень трудно.

3. Судя по приведенному коду, конструктор и деструктор в TCell не нужны, а вот перекрыть AssignTo было бы совсем не лишним (для копирования значений полей). Если же редактироваться будет копия объекта, а не он сам напрямую, то перекрытие AssignTo становится даже обязательным.

4. Еще было бы совсем не лишним указать для свойств Alignment и Position дефолтные значения. Если они отличаются от alLeft и нуля соответственно, то конструктор в TCell все же будет нужен (для инициализации полей).

5. > property Cell: TCell read fCell write fCell

Прямой доступ по записи даст утечку памяти и запросто может послужить источником Access Violation. Нужно write SetCell, а в SetCell вызвать Assign (вот еще для чего в TCell надо перекрыть AssignTo).

6. Есть ощущение, что после исправления п.п. 1-5 искомая проблема исчезнет сама собой.
:о)


 
DimaBr   (2005-08-25 14:31) [5]

Спасибо, буду побывать.


 
DimaBr   (2005-08-25 16:54) [6]

To Юрий Зотов.
Выполнил в точности все пять рекомендаций. Характер ошибки не изменился. Может быть неправильно переопределил AssignTo, но до него дело всё равно не доходит.

procedure TeeCell.AssignTo(Value: TPersistent);
begin
 TeeCell(Value).Aligment := Aligment;
 TeeCell(Value).fPosition:= Position;
end;

procedure TCellProperty.Edit;
var Source: TeeCell;
begin
 ShowMessage("Edit");
 Source := TeeCell(GetOrdValue);
 ShowMessage(inttostr(Source.Position));
 with TCellEditorFrm.Create(Application) do
 try
   ACell.Assign(Source);
   if Execute
     then Source.Assign(ACell);
 finally
   Free;
 end;
end;

При нажатии на "..." выскакивает окошечко "Edit", потои AV. То есть до второго Message дело не доходит.


 
Юрий Зотов ©   (2005-08-25 17:10) [7]

> DimaBr   (25.08.05 16:54) [6]

> Может быть неправильно переопределил AssignTo

procedure TeeCell.AssignTo(Dest: TPersistent); // override !!!
begin
 if Dest is TeeCell then
   with TeeCell(Dest) do
   begin
     Aligment := Self.Aligment;
     Position := Self.Position
   end
 else
   inherited
end;

> до второго Message дело не доходит.

Ошибка в коде компонента. То ли его внутренний объект Cell еще не создан, то ли уже уничтожен, то ли испорчена ссылка на него - в общем, надо видеть код.


 
DimaBr   (2005-08-25 17:35) [8]

unit CellEditor;

type
 TCellEditorFrm = class(TForm)
   ComboBox1: TComboBox;
   Edit1: TEdit;
   Button1: TButton;
   Button2: TButton;
   procedure FormCreate(Sender: TObject);
   procedure FormShow(Sender: TObject);
   procedure ComboBox1Change(Sender: TObject);
   procedure Edit1Change(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   ACell : TeeCell;
 end;

type TCellProperty = class(TClassProperty)
 public
   procedure Edit; override;
   function GetAttributes: TPropertyAttributes; override;
 end;

implementation
{$R *.dfm}
const StrAlig: array[0..4] of string = ("&#235;&#229;&#226;&#251;&#233;","&#239;&#240;&#224;&#226;&#251;&#233;","&#226;&#229;&#240;&#245;","&#237;&#232;&#231;" ,"&#231;&#237;&#224;&#247;&#229;&#237;&#232;&#229;");
const ValAlig: array[0..4] of TeeAligment = (alLeft,alRight,alTop,alBottom,alValue);

procedure TCellEditorFrm.FormCreate(Sender: TObject);
var i: integer;
begin
 ACell := TeeCell.Create;  // создали ACell при создании формы
 for i := 0 to 4 do
   ComboBox1.Items.Add(StrAlig[i]);
end;
procedure TCellEditorFrm.FormDestroy(Sender: TObject);
begin
 ACell.Free;
end;

procedure TCellEditorFrm.FormShow(Sender: TObject);
var i: integer;
begin
 for i := 0 to 4 do
   if ACell.Aligment = ValAlig[i] then ComboBox1.ItemIndex := i;
 Edit1.Text := Inttostr(ACell.Position);
end;

procedure TCellProperty.Edit;
var Source: TeeCell;
begin
 showmessage("Edit");
 Source := TeeCell(GetOrdValue);
 showmessage(inttostr(Source.Position));
 with TCellEditorFrm.Create(Application) do
 try
   ACell.Assign(Source);
   if ShowModal = 1
     then Source.Assign(ACell);
 finally
   Free;
 end;
end;

function TCellProperty.GetAttributes: TPropertyAttributes;
begin
 Result := [paDialog,paSubProperties];
end;

procedure TCellEditorFrm.ComboBox1Change(Sender: TObject);
begin
 ACell.Aligment := ValAlig[ComboBox1.ItemIndex];
end;

procedure TCellEditorFrm.Edit1Change(Sender: TObject);
begin
 ACell.Position := StrToInt(Edit1.Text);
end;
end.


unit Test;

type TeeAligment = (alLeft,alRight,alTop,alBottom,alValue);
type
 TeeCell = class(TPersistent)
   private
     fAligment: TeeAligment;
     fPosition: integer;
   protected
     procedure SetPosition(Value: integer);
   public
     constructor Create;
     destructor Destroy;override;
     procedure AssignTo(Value: TPersistent);override;
   published
     property Aligment: TeeAligment read fAligment write fAligment default alLeft;
     property Position: integer read fPosition write SetPosition default 0;
end;

type
 TTest = class(TComponent)
   private
     fCell : TeeCell;
   protected
     procedure SetCell(Value:TeeCell);
     function GetCell: TeeCell;
   public
     constructor Create(AOwner:TComponent);override;
     destructor Destroy;override;
   published
     property Cell: TeeCell read GetCell write SetCell;
end;

procedure Register;

implementation
uses CellEditor;

constructor TeeCell.Create;
begin
 inherited Create;
 fAligment := alValue;
 fPosition := 30;
end;

destructor TeeCell.Destroy;
begin
 inherited Destroy;
end;

constructor TTest.Create;
begin
 inherited Create(AOwner);
 fCell := TeeCell.Create;
end;

destructor TTest.Destroy;
begin
 fCell.Free;
 inherited Destroy;
end;

procedure TTest.SetCell(Value:TeeCell);
begin
 fCell.Assign(Value);
end;

function TTest.GetCell: TeeCell;
begin
 Result := fCell;
end;

procedure TeeCell.SetPosition(Value: integer);
begin
 if (Value = fPosition) or (Abs(Value) > 90) then Exit;
 fPosition := Value;
end;

procedure TeeCell.AssignTo(Value: TPersistent);
begin
if Value is TeeCell then
  with TeeCell(Value) do
  begin
    Aligment := Self.Aligment;
    Position := Self.Position
  end
else
  inherited
end;

procedure Register;
begin
 RegisterComponents("Standard", [TTest]);
 RegisterPropertyEditor(TypeInfo(TeeAligment), TeeCell, "Aligment",TCellProperty);
end;

end.


 
Юрий Зотов ©   (2005-08-25 19:14) [9]

Ну елы-палы!

Вы же сделали редактор для объектного свойства Cell - почему же тогда Вы регистрируете его для порядкового свойства Alignment, да еще совсем другого класса?

Ясное дело, что GetOrdValue будет давать число от 0 до 4, а это запрещенная область адресов - и здравствуй, Access Voilation! Еще хорошо, что хоть AV выскочило, при другом раскладе можно было бы просто втихую портить память и полжизни этот баг вылавливать.

RegisterPropertyEditor(TeeCell.ClassInfo, TTest, "Cell", TCellProperty);

P.S.
Погрешностей в коде очень много, в том числе и грубых. Шлифуйте.


 
Юрий Зотов ©   (2005-08-26 05:29) [10]

Чтобы было понятнее, где погрешности, причесал я код. Сравните построчно - увидите, в чем отличия. Если причины правки будет неясны - спрашивайте.

unit Test;

type
 TeeAlignment = (alLeft, alRight, alTop, alBottom, alValue);

 TeeCell = class(TPersistent)
 private
   FAligment: TeeAlignment;
   FPosition: integer;
   procedure SetPosition(const Value: integer);
 protected
   procedure AssignTo(Dest: TPersistent); override;
 public
   constructor Create;
 published
   property Aligment: TeeAlignment read FAligment write FAligment default alValue;
   property Position: integer read FPosition write SetPosition default 30;
 end;

 TTest = class(TComponent)
 private
   FCell: TeeCell;
   procedure SetCell(const Value:TeeCell);
 public
   constructor Create(AOwner:TComponent);override;
   destructor Destroy;override;
 published
   property Cell: TeeCell read FCell write SetCell;
 end;

procedure TeeCell.AssignTo(Dest: TPersistent);
begin
 if Dest is TeeCell then
   with TeeCell(Dest) do
   begin
     FAligment := Self.FAligment;
     FPosition := Self.FPosition
   end
 else
   inherited
end;

constructor TeeCell.Create;
begin
 inherited;
 FAligment := alValue;
 FPosition := 30
end;

procedure TeeCell.SetPosition(const Value: integer);
const
  MaxValue = 90;
begin
 if Abs(Value) > MaxValue then
   raise ERangeError.CreateFmt("Value must be between -%d and +%d", [MaxValue, MaxValue]);
 FPosition := Value
end;

{ TTest }

constructor TTest.Create(AOwner: TComponent);
begin
 inherited;
 FCell := TeeCell.Create
end;

destructor TTest.Destroy;
begin
 FCell.Free;
 inherited
end;

procedure TTest.SetCell(const Value: TeeCell);
begin
 FCell.Assign(Value);
end;

==============================================================

unit CellEditor;

type
 TCellEditorFrm = class(TForm)
   ComboBox1: TComboBox;
   Edit1: TEdit;
   Button1: TButton;
   Button2: TButton;
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
   procedure FormShow(Sender: TObject);
   procedure ComboBox1Change(Sender: TObject);
   procedure Edit1Change(Sender: TObject);
 private
   FCell: TeeCell;
 end;

 TCellProperty = class(TClassProperty)
 public
   procedure Edit; override;
   function GetAttributes: TPropertyAttributes; override;
 end;

procedure TCellEditorFrm.FormCreate(Sender: TObject);
var
 i: integer;
begin
 FCell := TeeCell.Create;
 for i := Ord(Low(TeeAlignment)) to Ord(High(TeeAlignment)) do
   ComboBox1.Items.Add(GetEnumName(TypeInfo(TeeAlignment), i))
end;

procedure TCellEditorFrm.FormDestroy(Sender: TObject);
begin
 FCell.Free
end;

procedure TCellEditorFrm.FormShow(Sender: TObject);
begin
 ComboBox1.ItemIndex := Ord(FCell.Aligment);
 Edit1.Text := IntToStr(FCell.Position)
end;

procedure TCellEditorFrm.ComboBox1Change(Sender: TObject);
begin
 FCell.Aligment := TeeAlignment(ComboBox1.ItemIndex)
end;

procedure TCellEditorFrm.Edit1Change(Sender: TObject);
begin
 try
   FCell.Position := StrToInt(Edit1.Text)
 except
   Edit1.Undo;
   raise
 end
end;

{ TCellProperty }

procedure TCellProperty.Edit;
var
 Source: TeeCell;
begin
 Source := TeeCell(GetOrdValue);
 with TCellEditorFrm.Create(nil) do
 try
   FCell.Assign(Source);
   if ShowModal = mrOK then
     Source.Assign(FCell)
 finally
   Free
 end
end;

function TCellProperty.GetAttributes: TPropertyAttributes;
begin
 Result := inherited GetAttributes + [paDialog]
end;


 
DimaBr   (2005-08-26 09:10) [11]

Огромное спасибо !!!   Всё замечательно работает.
Возникло несколько вопросов:

1) В чём различие между..., ведь в любом случае параметр изменить нельзя.

procedure SetPosition(const Value: integer);
procedure SetPosition(Value: integer);

2) В чём различие

constructor TeeCell.Create;
begin
inherited;
inherited Create;
end;

3) Не лучше ли написать вместо

Result := inherited GetAttributes + [paDialog];
Result := [paMultiSelect, paSubProperties, paReadOnly,paDialog];

4) Код

procedure TCellEditorFrm.Edit1Change(Sender: TObject);
begin
try
  FCell.Position := StrToInt(Edit1.Text)
except
  Edit1.Undo;
  raise
end
end;

я перенёс в Edit1Exit, поскольку при вводе числа 100, код отрабатываеся для 1, потом для 10 (10 - нормально), потом для 100 (100 - выход за предел и UNDO - 10)
5) Имеет ли смысл приравнивать в методе SetPosition() FPosition := Value, если они равны.


 
Юрий Зотов ©   (2005-08-26 12:58) [12]

1. В случае const параметр передается по ссылке. Поэтому, если его длина не более 4-х байт, то разницы нет (кроме очевидной, о которой вряд ли стоит здесь говорить). Но если длина параметра более 4-х байт, то смысл есть, и даже очень большой - мы даем возможность компилятору передать его через регистр, да еще и экономим место в стеке.

В данном случае длина Value - 4 байта и разницы нет, но лучше придерживаться грамотного стиля всегда, чтобы он стал просто привычкой и шел как бы сам по себе, на автопилоте.

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

3. Можно, но для этого надо лезть в VCL и копировать кусок оттуда - тратится время. А зачем его тратить, если нам нужно только добавить диалог? Кроме того, не исключено, что в следующих версиях Delphi что-то изменится (такое, кстати уже бывало) - а ведь вызов inherited автоматически это учтет и ничего не придется переделывать, код получится универсальным.

4. Правильно сделали. Лучше даже вообще убрать все обработчики OnChange или OnExit (чтобы не срабатывали при каждом чихе), а присвоение свойств объекта повесить на кнопку ОК (при ошибке не присваивать ModalResult, чтобы не закрывать форму).

5. С точки зрения логики алгоритма - без разницы. А с точки зрения оптимальности кода - смысл есть (присваивание - это одна команда move, код проверки - это еще несколько команд, без которых в данном случае вполне можно и обойтись).


 
DimaBr   (2005-08-26 15:02) [13]

Ещё раз ОГРОМНОЕ СПАСИБО !!!!!!



Страницы: 1 вся ветка

Текущий архив: 2006.02.12;
Скачать: CL | DM;

Наверх




Память: 0.51 MB
Время: 0.045 c
15-1137909887
begin...end
2006-01-22 09:04
2006.02.12
С Днём рождения! 22 января


15-1137841026
Andy BitOff
2006-01-21 13:57
2006.02.12
Borland Developer Studio


2-1138312913
Goryn
2006-01-27 01:01
2006.02.12
Окна нет


15-1138113647
X9
2006-01-24 17:40
2006.02.12
Проблемы с мат. платой


3-1134626810
DaNMaTeR
2005-12-15 09:06
2006.02.12
Запрос SQL к запросу созданному в ADO





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский