Как я могу сохранить все свойства TControlCanvas и восстановить их позже?

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

Я пытался создать свой собственный TControlCanvas и назначить ему единицу сетки, но я получаю исключение времени выполнения с сообщением:

Невозможно назначить TControlCanvas TControlCanvas

, что указывает на AssignTo метод не реализован для TControlCanvas ни для его предков. Итак, мои вопросы:

  1. Зачем TControlCanvas не имеет AssignTo метод? В чем проблема?

  2. Как я могу сохранить и восстановить все свойства TControlCanvas? И под этим я подразумеваю что-то более удобное, чем создание TPen, TBrush, TFont, так далее..

2 ответа

Решение

Не уверен, что это соответствует вашим ожиданиям, но есть TPenRecall, TBrushRecall а также TFontRecall сохранить и восстановить настройки этих трех свойств полуавтоматическим способом.

Обработка довольно проста: создайте экземпляр этих классов с соответствующими свойствами в качестве параметра и делайте все, что вы хотите с помощью Pen, Brush и Font. В конце освободите те экземпляры, которые восстановят настройки.

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

type
  TCanvasSaver = class(TInterfacedObject)
  private
    FStorage: TObjectList<TRecall>;
  public
    constructor Create(ACanvas: TCanvas);
    destructor Destroy; override;
    class function SaveCanvas(ACanvas: TCanvas): IInterface;
  end;

constructor TCanvasSaver.Create(ACanvas: TCanvas);
begin
  inherited Create;
  FStorage := TObjectList<TRecall>.Create(True);
  FStorage.Add(TFontRecall.Create(ACanvas.Font));
  FStorage.Add(TBrushRecall.Create(ACanvas.Brush));
  FStorage.Add(TPenRecall.Create(ACanvas.Pen));
end;

destructor TCanvasSaver.Destroy;
begin
  FStorage.Free;
  inherited;
end;

class function TCanvasSaver.SaveCanvas(ACanvas: TCanvas): IInterface;
begin
  Result := Self.Create(ACanvas);
end;

Использование:

procedure TForm274.DoYourDrawing(ACanvas: TCanvas);
begin
  TCanvasSaver.SaveCanvas(ACanvas);
  { Change Pen, Brush and Font of ACanvas and do whatever you need to do.
    Make sure that ACanvas is still valid and the same instance as at the entry of this method. }
end;

В то время как TCanvas на самом деле не инкапсулирует эти функции API, можно использовать SaveDC а также RestoreDC делать то, что вам нужно. Из MSDN:

Функция SaveDC сохраняет текущее состояние указанного контекста устройства (DC), копируя данные, описывающие выбранные объекты и графические режимы (такие как растровое изображение, кисть, палитра, шрифт, перо, область, режим рисования и режим отображения) в контекст стек.

[...]

Функция RestoreDC восстанавливает контекст устройства (DC) до указанного состояния. DC восстанавливается путем извлечения информации о состоянии из стека, созданного предыдущими вызовами функции SaveDC.

Возможный пример кода:

uses
  Winapi.Windows;
...
var
  SavedDC: Integer;
begin
  SavedDC := SaveDC(Canvas.Handle);
  try
   // Painting code
  finally
    RestoreDC(Canvas.Handle, SavedDC);
  end;
end;

Редактировать:
Я понял, что одно это, скорее всего, не будет ответом. Это будет обрабатывать контекст устройства на стороне Windows, которая представлена TCanvas /TControlCanvas объект на стороне VCL Delphi. Но это не изменит ни одного из TFont, TBrush или же TPen объекты, которые содержит VCL. Из тестов это выглядит так, например, всякий раз, когда Delphi использует Canvas Brush.GetHandle (FillRect, FrameRect), это все еще измененная кисть.

Так что лучше всего использовать SaveDC а также RestoreDC в сочетании с хранением и восстановлением Pen, Font а также Brush как в ответе Уве Раабе.

Другие вопросы по тегам