Советы по Delphi

         

Установка границ для вновь создаваемых элементов управления


Если вы создаете в среде Delphi новый элемент управления, и вам нужно контролировать или ограничить свойства Left, Top, Width и Height, то спешу вас обрадовать: есть одно простое решение сделать это. Тем не менее, в документации по Delphi я не увидел ни малейшего намека на данный способ (включая CWG).

Ключевой момент кроется в изменении Left, Top, Width, Height и каждого BoundsRect в Delphi методом SetBounds() (доступного в TControl и во всех его потомках). SetBounds() - виртуальная фунция, делающее установление позиции и размера элемента управления делом легким и приятным. Тем не менее, о чем умалчивается в документации, что TControl.SetBounds() вызывает методы TControl SetLeft(), SetTop(), SetWidth() и SetHeight() при каждом изменении значений свойств Left, Top, Width, Height и BoundsRect.

Таким образом, для того, чтобы ловить изменения этих свойств, просто перекройте метод SetBounds(), сделайте все, что вам нужно с новыми значениями, после чего передайте их унаследованному методу SetBounds().

В следующем примере мы имеем управление, которое автоматически изменяет свои пользовательские свойства X & Y, с той целью, чтобы они ссылались на центр элемента управления при изменении значений Left, Top, Width, Height или BoundsRect. И наоборот, Left и Top будут изменяться всякий раз при изменении свойств X & Y:

    type
TMyControl = class(TControl) private FX, FY : integer; {методы доступа к свойствам} procedure SetX(value : integer); procedure SetY(value : integer); ...

public procedure
SetBounds(aLeft, aTop, aWidth, aHeight : integer); override; ... property X : integer read FX write SetX; property Y : integer read FY write SetY; end; ...

procedure
TMyControl.SetX(value : integer);
begin
if
FX <> value then SetBounds(value - Width div 2, Top, Width, Height); end;

procedure TMyControl.SetY(value : integer);
begin
if
FY <> value then SetBounds(Left, value - Height div 2, Width, Height); end;

procedure TMyControl.SetBounds(aLeft, aTop, aWidth, aHeight : integer);
begin
{Продолжаем, и позволяем SetBounds() сделать свое дело...} inherited SetBounds(aLeft, aTop, aWidth, aHeight); {Теперь "регулируем" FX и FY согласно нашим новым границам.} FX := Width div 2; FY := Height div 2; end;

Также в документации не упоминается о том факте, что частные поля FLeft, FTop, FWidth и FHeight, которые TControl использует для хранения внутренних значений, используются в методах SetLeft(), SetTop() и пр. для сравнения с текущими границами прямоугольника, при совпадении которых он не обновляется. Фактически, эти переменные нигде, кроме как в методе TControl SetBounds(), не корректируются (как в случае с FX и FY в приведенном выше примере).

Так, чтобы ограничить изменения размеров вашего элемента управления, вы должны перекрыть SetBounds(), и проверять/изменять любые свойства перед передачей значений унаследованному методу SetBounds().

В следующем примере мы имеем управление, которое ограничивает свою ширину и высоту в 100 пикселей:

    type
TMyControl = class(TControl) ... public procedure SetBounds(aLeft, aTop, aWidth, aHeight : integer); override; ... end;
...
procedure
TMyControl.SetBounds(aLeft, aTop, aWidth, aHeight : integer);
begin
if
aWidth > 100 then aWidth  := 100; if aHeight > 100 then aHeight := 100; inherited SetBounds(aLeft, aTop, aWidth, aHeight); end;

[001594]



Содержание раздела