Delphi 更改包含已写入文本的区域的背景
(德尔福DX 10.3)Delphi 更改包含已写入文本的区域的背景,delphi,canvas,colors,background,Delphi,Canvas,Colors,Background,(德尔福DX 10.3) 我有一个大的空白(白色)画布(在Tpanel的后代上),在那里我绘制一些文本(使用Textout(),假设文本总是黑色)和图形(线条、矩形,没有那么复杂的东西)。 绘图后,我需要将某些特定区域的白色背景颜色从白色更改为另一种颜色。 我想要达到的效果是非常像带有彩色单元格的excel工作表。在所附示例中,所有列均为空白(白色),如“值”和“差”,然后黄色(价格)和红色(结果)列已上色。 如果我能在写文本之前填写区域,我会使用SetBkMode(透明)并获得最佳结果。不幸的
我有一个大的空白(白色)画布(在Tpanel的后代上),在那里我绘制一些文本(使用Textout(),假设文本总是黑色)和图形(线条、矩形,没有那么复杂的东西)。
绘图后,我需要将某些特定区域的白色背景颜色从白色更改为另一种颜色。
我想要达到的效果是非常像带有彩色单元格的excel工作表。在所附示例中,所有列均为空白(白色),如“值”和“差”,然后黄色(价格)和红色(结果)列已上色。
如果我能在写文本之前填写区域,我会使用SetBkMode(透明)并获得最佳结果。不幸的是,我需要在编写文本和图形后填写区域。 我想到的第一个解决方案是逐像素替换(使用Pixels[]函数),但它的速度非常慢,图形效果也不令人满意 因此,我的问题是:如何为包含已写入文本的区域的背景上色? 这里有一个最小可重复性示例
按钮1执行像素替换,速度非常慢,图形结果不令人满意。
按钮2使用SetBkMode(透明)在写入文本后填充区域。完美的结果,但我做不到
program BK_mode;
uses
Vcl.Forms,
main in 'main.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
////////////////////////////////
unit main;
interface
uses Windows, Forms, SysUtils, Vcl.StdCtrls, UiTypes, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics, Dialogs;
type
TForm1 = class(TForm)
btn_01: TButton;
btn_02: TButton;
procedure btn_02Click(Sender: TObject);
procedure btn_01Click(Sender: TObject);
private
procedure write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
procedure switch_color(canvas : TCanvas;color_source, color_target : TColor);
procedure prepare_example(bo_transparent : boolean);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
BASE_COLOR = clWhite;
ALTERNATIVE_COLOR = clRed;
procedure TForm1.write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
begin
var lo_old_BK_color : TColor := canvas.Brush.Color;
var lo_old_BK_mode := GetBKMode(canvas.Handle);
if bo_transparent then SetBKMode(canvas.Handle, TRANSPARENT)
else begin
SetBKMode(canvas.Handle, OPAQUE);
canvas.Brush.Color := BASE_COLOR
end;
canvas.Font.Color := clBlack;canvas.Font.Size := i_fontsize;canvas.Font.Style := style;
TextOut(canvas.Handle, x, y, PChar(str_text), Length(str_text));
if NOT bo_transparent then canvas.Brush.Color := lo_old_BK_color;
SetBKMode(canvas.Handle, lo_old_BK_mode)
end;
procedure TForm1.switch_color(canvas : TCanvas;color_source, color_target : TColor);
begin
for var x := 0 to clientWidth-1 do
for var y := 0 to clientHeight - 1 do
if (canvas.Pixels[x, y] = color_source) then canvas.Pixels[x, y] := color_target
end;
procedure TForm1.prepare_example(bo_transparent : boolean);
begin
if bo_transparent then Color := ALTERNATIVE_COLOR else Color := BASE_COLOR;
invalidate;
application.MessageBox('Click me', 'Test');
canvas.Rectangle(10, 10, 200, 100);
canvas.MoveTo(10, 110);canvas.LineTo(200, 140);
canvas.MoveTo(10, 140);canvas.LineTo(200, 110);
write_text(canvas, 30, 30, 14, 'This is a text!', [], bo_transparent);
write_text(canvas, 30, 60, 11, 'This is another text!', [fsBold, fsItalic], bo_transparent)
end;
procedure TForm1.btn_01Click(Sender: TObject);
begin
prepare_example(FALSE);
switch_color(canvas, BASE_COLOR, ALTERNATIVE_COLOR)
end;
procedure TForm1.btn_02Click(Sender: TObject);
begin
prepare_example(TRUE)
end;
end.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 283
ClientWidth = 208
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
208
283)
PixelsPerInch = 96
TextHeight = 13
object btn_01: TButton
Left = 17
Top = 161
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '1. write text and fill area'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 0
WordWrap = True
OnClick = btn_01Click
end
object btn_02: TButton
Left = 17
Top = 220
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '2. fill area then write text'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 1
WordWrap = True
OnClick = btn_02Click
end
end
/////////////////////////////////////////////////////////
unit main;
interface
uses Windows, Forms, SysUtils, Vcl.StdCtrls, UiTypes, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics, Dialogs;
type
TForm1 = class(TForm)
btn_01: TButton;
btn_02: TButton;
procedure btn_02Click(Sender: TObject);
procedure btn_01Click(Sender: TObject);
private
procedure write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
procedure switch_color(canvas : TCanvas;color_source, color_target : TColor);
procedure prepare_example(bo_transparent : boolean);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
BASE_COLOR = clWhite;
ALTERNATIVE_COLOR = clRed;
procedure TForm1.write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
begin
var lo_old_BK_color : TColor := canvas.Brush.Color;
var lo_old_BK_mode := GetBKMode(canvas.Handle);
if bo_transparent then SetBKMode(canvas.Handle, TRANSPARENT)
else begin
SetBKMode(canvas.Handle, OPAQUE);
canvas.Brush.Color := BASE_COLOR
end;
canvas.Font.Color := clBlack;canvas.Font.Size := i_fontsize;canvas.Font.Style := style;
TextOut(canvas.Handle, x, y, PChar(str_text), Length(str_text));
if NOT bo_transparent then canvas.Brush.Color := lo_old_BK_color;
SetBKMode(canvas.Handle, lo_old_BK_mode)
end;
procedure TForm1.switch_color(canvas : TCanvas;color_source, color_target : TColor);
begin
for var x := 0 to clientWidth-1 do
for var y := 0 to clientHeight - 1 do
if (canvas.Pixels[x, y] = color_source) then canvas.Pixels[x, y] := color_target
end;
procedure TForm1.prepare_example(bo_transparent : boolean);
begin
if bo_transparent then Color := ALTERNATIVE_COLOR else Color := BASE_COLOR;
invalidate;
application.MessageBox('Click me', 'Test');
canvas.Rectangle(10, 10, 200, 100);
canvas.MoveTo(10, 110);canvas.LineTo(200, 140);
canvas.MoveTo(10, 140);canvas.LineTo(200, 110);
write_text(canvas, 30, 30, 14, 'This is a text!', [], bo_transparent);
write_text(canvas, 30, 60, 11, 'This is another text!', [fsBold, fsItalic], bo_transparent)
end;
procedure TForm1.btn_01Click(Sender: TObject);
begin
prepare_example(FALSE);
switch_color(canvas, BASE_COLOR, ALTERNATIVE_COLOR)
end;
procedure TForm1.btn_02Click(Sender: TObject);
begin
prepare_example(TRUE)
end;
end.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 283
ClientWidth = 208
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
208
283)
PixelsPerInch = 96
TextHeight = 13
object btn_01: TButton
Left = 17
Top = 161
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '1. write text and fill area'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 0
WordWrap = True
OnClick = btn_01Click
end
object btn_02: TButton
Left = 17
Top = 220
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '2. fill area then write text'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 1
WordWrap = True
OnClick = btn_02Click
end
end
你所要求的是根本无法通过这种方式实现的。您需要重新绘制整个画布,在顶部绘制线条+文本之前先绘制背景。一旦你画好了,你需要重新画一遍。请勿从绘制事件外部在画布上绘制 您的示例在
TForm.Canvas
上绘制,因此使用TForm.OnPaint
事件。对于TPanel
子体,替代虚拟Paint()
方法。无论哪种方式,都要保留一些具有所需设置的变量,在绘制时使用这些变量,并在更新变量并希望触发重新绘制后调用Invalidate()
例如:
unitmain;
接口
使用Windows、窗体、SysUtils、Vcl.StdCtrls、UiTypes、System.class、Vcl.Controls、Vcl.ExtCtrls、图形、对话框;
类型
TForm1=类(TForm)
btn_01:t按钮;
btn_02:t按钮;
程序btn_02Click(发送方:TObject);
程序btn_01单击(发送方:TObject);
过程表单创建(发送方:ToObject);
程序FormPaint(发送方:TObject);
私有的
FDrawTransparent:布尔值;
FDrawColor:TColor;
过程写入文本(ACanvas:TCanvas;x,y:integer;i_fontsize:smallint;const str_text:String;style:TFontStyles;bo_transparent:boolean);
过程准备示例(bo_透明:布尔;颜色目标:TColor);
结束;
变量
表1:TForm1;
实施
{$R*.dfm}
常数
基色=白色;
可选颜色=clRed;
过程TForm1.FormCreate(发送方:TObject);
开始
FDrawTransparent:=假;
FDrawColor:=基础颜色;
结束;
程序TForm1.FormPaint(发送方:TObject);
开始
Canvas.Brush.Color:=Self.Color;
画布。矩形(10,10,200,100);
Canvas.MoveTo(10110);
Canvas.LineTo(200140);
Canvas.MoveTo(10140);
Canvas.LineTo(200110);
编写文本(Canvas,30,30,14,'这是一个文本!',[],FDrawTransparent);
书写文本(Canvas,30,60,11,'这是另一个文本!',[fsBold,fsItalic],FDrawTransparent);
结束;
过程TForm1.write_text(ACanvas:TCanvas;x,y:integer;i_fontsize:smallint;const str_text:String;style:TFontStyles;bo_transparent:boolean);
开始
var lo_old_BK_color:=ACanvas.Brush.color;
var lo_old_BK_mode:=GetBKMode(ACanvas.Handle);
如果bo_透明的话
SetBKMode(ACanvas.Handle,透明)
否则开始
SetBKMode(ACanvas.Handle,不透明);
ACanvas.Brush.Color:=FDrawColor;
结束;
ACanvas.Font.Color:=clBlack;
ACanvas.Font.Size:=i_fontsize;
ACanvas.Font.Style:=样式;
TextOut(ACanvas.Handle、x、y、PChar(str_-text)、Length(str_-text));
如果不是透明的,则ACanvas.Brush.Color:=lo_old_BK_Color;
SetBKMode(ACanvas.Handle,低旧模式);
结束;
步骤TForm1.准备示例(透明:布尔;颜色目标:TColor);
开始
FDrawTransparent:=bo_transparent;
FDrawColor:=目标颜色;
如果透明,则颜色:=可选颜色,否则颜色:=基础颜色;
使无效
Application.MessageBox('单击我','测试');
结束;
程序TForm1.btn_01单击(发送方:TObject);
开始
准备_示例(错误,可选_颜色);
结束;
程序TForm1.btn_02Click(发送方:TObject);
开始
准备示例(真实、基本颜色);
结束;
结束。
谢谢@Remy,你当然是对的。我总是用颜料在画布上画画,但在这种情况下,直接画画会节省我很多工作。我会改变我的策略,我会更加努力,但我会做得更好。