我的问题来自于过去几天我一直在处理的一个相当有趣的问题.我最近问了一个关于编写自定义属性检查器的问题 - 如何在验证值时处理内部编辑器焦点?
我已经在我的控件上取得了一些不错的进步,比如在中间添加一个分隔符来分隔Name和Value行,重要的是分隔符可以用来调整两列的大小.
这是我的问题开始的地方,让inplace编辑器可见,同时调整分隔符的大小导致我的控制稍微减慢.所以我进一步改变了代码,只显示了inplace编辑器,如果没有调整分隔符的大小.基本上,我习惯Canvas.TextOut将我的值绘制为字符串,如果选择了一行,则上面显示了Inplace编辑器.如果已调整分隔符大小,则inplace编辑器将隐藏,一旦调整大小操作完成,就地编辑器将再次可见.
虽然这解决了我提到的轻微减速问题,但我遇到了一个新问题,因为来自内部编辑器(基本上是TEdit)的文本与我正在使用的文本略有不同 Canvas.TextOut
差异非常微妙,但如果你看得足够近,你可以看到它:
图1 Canvas.TextOut
图2 DrawText
您可能需要使用屏幕放大镜看起来更近,但与SomeText排它是更明显的间距之间Some和Text也的T和e在Text略有不同.
稍微好一点的例子或许之间的比较Canvas.TextOut,并DrawText在内置的编辑器(TEDIT)文本:
图3比较
正如你所看到的,这里的区别更为突出.字符串True清晰地显示了文本字符在使用时间距更大Canvas.TextOut,其中DrawText和inplace editor文本字符完全相同.
当我使用时,Canvas.TextOut我在调整检查器分隔符的大小以及显示和隐藏内部编辑器之间遇到了各种可怕的文本不匹配.如果我没有尝试并尝试替代文本绘制方法,我认为我不会意识到差异并找到了解决方案.重要的是要知道我在将文本绘制到画布时使用完全相同的字体设置作为我为内置编辑器定义的字体.
现在我正在使用DrawText而不是Canvas.TextOut一切正在与inplace编辑器一致工作,以及我想要它的确切方式.
我的问题是什么使Canvas.TextOut渲染文本变得如此不同DrawText?从我的例子和处理我当前的问题,很明显,Canvas.TextOut不会以与具有相同Font设置的TEdit相同的方式DrawText呈现文本,但是渲染文本看起来似乎是正确的方式.
这让我质疑使用Canvas.TextOut,如果它没有正确呈现文本,我应该总是使用DrawText吗?
您可以使用以下代码自行测试:
type
TForm1 = class(TForm)
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure …Run Code Online (Sandbox Code Playgroud) 以下函数获取Richedit控件中的选定文本,写入TMemoryStream回调函数内部,然后以纯文本字符串形式返回原始rtf代码.
var
MS: TMemoryStream; // declared globally and works.
implementation
function GetSelectedRTFCode(RichEdit: TRichedit): string;
function RichEditCallBack(dwCookie: Longint; pbBuff: PByte;
CB: Longint; var pCB: Pointer): Longint; stdcall;
begin
MS.WriteBuffer(pbBuff^, CB);
Result := CB;
end;
var
EditStream: TEditStream;
SL: TStringList;
begin
MS := TMemoryStream.Create;
try
EditStream.dwCookie := SF_RTF or SFF_SELECTION;
EditStream.dwError := 0;
EditStream.pfnCallback := @RichEditCallBack;
Richedit.Perform(EM_StreamOut, SF_RTF or SFF_SELECTION, DWord(@EditStream));
MS.Seek(0, soBeginning);
SL := TStringList.Create;
try
SL.LoadFromStream(MS);
Result := SL.Text;
finally
SL.Free;
end;
finally
MS.Free;
end;
end; …Run Code Online (Sandbox Code Playgroud) 我需要从字符串中提取数字并将它们放入列表中,但是有一些规则,例如识别提取的数字是整数还是浮点数.
这项任务听起来很简单,但随着时间的推移,我发现自己越来越困惑,并且可以真正做到一些指导.
以下面的测试字符串为例:
There are test values: P7 45.826.53.91.7, .5, 66.. 4 and 5.40.3.
Run Code Online (Sandbox Code Playgroud)
解析字符串时要遵循的规则如下:
数字不能以字母开头.
如果找到一个数字并且后面没有小数点,则该数字为整数.
如果它发现了一些和被后跟一个小数点然后数为浮点数,例如5.
〜如果更多数字跟随小数点,那么数字仍然是一个浮点数,例如5.40
〜进一步发现小数点应该分解数字,例如5.40.3变为(5.40 Float)和(3 Float)
如果一个字母例如在小数点后面,例如3.H然后仍然3.作为Float 添加到列表中(即使从技术上讲它是无效的)
例1
为了使这一点更清晰,在上面引用所需输出的测试字符串应该如下:
从上图中可以看出,浅蓝色表示浮点数,浅红色表示单个整数(但请注意浮点数如何连接在一起分成单独的浮点数).
- 45.826(浮动)
- 53.91(浮法)
- 7(整数)
- 5(整数)
- 66.(浮动)
- 4(整数)
- 5.40(浮动)
- 3.(浮动)
请注意,66之间有故意的空格.和3.以上是由于数字的格式化方式.
例2:
Anoth3r Te5.t string .4 abc 8.1Q 123.45.67.8.9
- 4(整数)
- 8.1(浮动)
- 123.45(浮动)
- 67.8(浮动)
- 9(整数)
为了给出更好的想法,我在测试时创建了一个新项目,如下所示:
现在进入实际任务.我想也许我可以从字符串中读取每个字符,并根据上面的规则识别有效数字,然后将它们拉入列表.
以我的能力,这是我能管理的最好的:
代码如下:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
type …Run Code Online (Sandbox Code Playgroud) 我一直在使用Windows API和Parts and States获得一些乐趣,并了解如何将控件绘制到画布上.
经过大量的试验和错误,我设法提出了这个程序,它会在画布上绘制一个按钮:
type
TButtonState = (bsDefault, bsDisabled, bsHot, bsNormal, bsPressed);
procedure DrawButton(ACanvas: TCanvas; X, Y, AWidth, AHeight: Integer;
AFont: TFont; Caption: string; ButtonState: TButtonState);
var
Size: TSize;
R: TRect;
H: HTHEME;
begin
Size.cx := AWidth;
Size.cy := AHeight;
R := Rect(X, Y, X + AWidth, Y + AHeight);
if Winapi.uxTheme.UseThemes then
begin
H := OpenThemeData(0, 'BUTTON');
if H <> 0 then
try
ACanvas.Brush.Style := bsClear;
if AFont <> nil then
begin
ACanvas.Font.Assign(AFont);
end;
case …Run Code Online (Sandbox Code Playgroud) 我正在使用Zarko Gajic将更多(自定义)数据存储到树视图的树节点中为每个节点项添加其他字符串,但我发现如果我的应用程序已经闲置了很长时间,那么我拥有的值存储在自定义树节点中的节点消失.
这就是我昨天离开时的定制treenode

这就是今天早上的样子(注意fMyProperty值现在为空)

我已经确认我的计算机从未休眠或睡眠,但系统会在锁定1分钟后关闭显示器以节省电量.然而,计算机需要闲置一段时间才能发生此问题.它在夜间闲置时最明显,但如果只闲置30分钟则不太可能发生.
我能想到的唯一可能导致这种情况的是操作系统将应用程序内存交换到磁盘,当您重新激活计算机时,它会被换回内存.正如您所看到的,FItemId正在发生变化,因此它似乎正在"重建"Treeview,从而失去与自定义Tree节点的关联.
我用一个非常简单的应用程序重现了这个问题,其代码如下.
我知道我可以使用另一种方法来存储额外的数据,通过使用Treenode中的数据字段,但是能够这样做会很好,因为我不必担心在删除节点时释放额外的内存块.
我该怎么做才能防止这种数据丢失?
unit Test04Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls;
type
TMyTreeNode = class(TTreeNode)
private
fMyProperty : string;
public
property MyProperty : string read fMyProperty write fMyProperty;
end;
TForm1 = class(TForm)
TreeView1: TTreeView;
StatusBar1: TStatusBar;
procedure FormCreate(Sender: TObject);
procedure TreeView1CreateNodeClass(Sender: TCustomTreeView; var NodeClass: TTreeNodeClass);
procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
private
fTreeView1_Selected: TMyTreeNode;
property TreeView1_Selected : TMyTreeNode read fTreeView1_Selected;
{ Private declarations }
public
{ …Run Code Online (Sandbox Code Playgroud) 我很感激之前已经提出过几个类似的问题:
但是,我没有进一步了解如何将RTTI用于我的需求.
我也花了很多时间和精力来写这个问题所以我希望它不会被关闭:)
我在下面有几个过程可以输出到TStrings列表中的组件的属性名称,值和类型.原始来源不是我的,我只是做了一些小改动,整理了代码并将它们放入一些整洁的可重用程序:
下面将输出属性名称,例如:
- 颜色
- DoubleBuffered
- 启用
- 高度
- 宽度
procedure GetComponentPropertyNames(Component: TComponent; OutList: TStrings);
var
I: Integer;
Count, Size: Integer;
PropList: PPropList;
PropInfo: PPropInfo;
begin
OutList.BeginUpdate;
try
OutList.Clear;
Count := GetPropList(Component.ClassInfo, tkAny, nil);
Size := Count * SizeOf(Pointer);
GetMem(PropList, Size);
try
Count := GetPropList(Component.ClassInfo, tkAny, PropList);
for I := 0 to Count -1 do
begin
PropInfo := PropList^[I];
if not (PropInfo^.PropType^.Kind = tkMethod) then
begin
OutList.Add(PropInfo^.Name);
end;
end;
finally
FreeMem(PropList);
end;
finally
OutList.EndUpdate; …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写自己的简单属性检查器,但是我面临着一个困难且相当混乱的问题。首先,尽管让我说我的组件不是要使用或处理组件属性,而是允许向其添加自定义值。我组件的完整源代码在问题的后面,一旦将其安装在软件包中并从一个新的空项目中运行,它应该看起来像这样:
问题在于使用就地编辑器并验证属性值。想法是,如果属性值无效,则向用户显示一条消息,通知他们该值不能被接受,然后将焦点放回到最初集中在的行和就位编辑器上。
实际上,我们可以使用Delphi自己的Object Inspector来说明我要寻找的行为,例如,尝试在Name无法接受的属性中编写字符串,然后单击Object Inspector。显示一条消息,关闭该消息后,它将聚焦回到该Name行。
没有任何代码,问题变得太模糊了,但是由于我要编写的组件的性质,这个问题也很大。为了问题和示例的目的,我已尽可能将其简化。我敢肯定会有一些评论问我为什么不这样做或不这样做,但是重要的是要知道我不是Delphi专家,并且经常做出错误的决定和选择,但是我总是愿意学习,所以所有评论表示欢迎,特别是如果它有助于寻找我的解决方案。
unit MyInspector;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.Classes,
System.SysUtils,
Vcl.Controls,
Vcl.Dialogs,
Vcl.StdCtrls,
Vcl.Graphics,
Vcl.Forms;
type
TMyInspectorItems = class(TObject)
private
FPropertyNames: TStringList;
FPropertyValues: TStringList;
procedure AddItem(APropName, APropValue: string);
procedure Clear;
public
constructor Create;
destructor Destroy; override;
end;
TOnMouseMoveEvent = procedure(Sender: TObject; X, Y: Integer) of object;
TOnSelectRowEvent = procedure(Sender: TObject; PropName, PropValue: string; RowIndex: Integer) of object;
TMyCustomInspector = class(TGraphicControl)
private
FInspectorItems: TMyInspectorItems;
FOnMouseMove: TOnMouseMoveEvent;
FOnSelectRow: TOnSelectRowEvent;
FRowCount: Integer; …Run Code Online (Sandbox Code Playgroud) 我正在尝试从我的应用程序中自定义文件夹图标,但由于 Windows 缓存图标和缩略图的方式,我正在努力获取更改后的文件夹以更新它的新图标。
在 Windows 中,我知道有两种自定义文件夹图标的方法,最简单的方法是从 Windows 资源管理器中右键单击文件夹,属性,然后选择自定义选项卡并更改其图标,但这种方法不好,因为它需要手动更改文件夹属性。
我从我的程序做的第二种方法是创建一个desktop.ini文件并将其放在需要更改图标的文件夹中,desktop.ini可以在此处找到有关文档:http : //hwiegman.home.xs4all.nl/desktopini.html和这里有一篇文章:http : //helpdeskgeek.com/how-to/customize-folder-icons-desktop-ini/
如下所示,虽然您可以从文件夹属性中看到图标已更改,但 Windows 中的文件夹图标并未更新,只是 Windows 未显示更新的文件夹图标:
我完全了解 Windows 缓存图标和缩略图,我搜索并阅读了几页关于重建图标缓存的方法,从而让 Windows 显示新更改的图标而不是缓存的图标,但是我无法得到这个在不重新启动 Windows 或 Explorer.exe 进程的情况下从我的应用程序工作,这需要在不中断 Windows 的情况下完成,但因此关闭 Explorer.exe 或要求重新启动绝对不是一种选择。
有趣的是,当从我之前描述的第一种方式更改文件夹图标时,Windows 以某种方式设法刷新和重建图标缓存,因为更改的文件夹会立即更新以显示新图标,但是我看不到 Windows 为实现这一目标做了什么技巧.
我尝试了一些失败的事情,(没有特别的顺序):
ie4uinit.exe -ClearIconCacheSHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil);IconCache.db从删除文件AppData\Local"Always show icons, never thumbnails"从注册表中切换Explorer 选项,位于此处:HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced存储在IconsOnly值中HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics存储在Shell Icon Size值中我正在尝试使用目录中的所有文件夹名称填充字符串列表.
以下是我如何能够做到这一点的摘录:
var
SL: TStringList;
SearchAttr: LongInt;
SR: TSearchRec;
begin
SL := TStringList.Create;
try
SearchAttr := (faDirectory);
if FindFirst(Directory + '\*.', SearchAttr, SR) = 0 then
begin
try
repeat
if (SR.Attr and faDirectory) <> 0 then
begin
if (SR.Name <> '.') and (SR.Name <> '..') then
begin
SL.Add(Directory + SR.Name);
end;
end;
until
FindNext(Sr) <> 0;
finally
FindClose(SR);
end;
end;
end;
// do something with string list folder names
finally
SL.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
我访问的父文件夹包含220个子文件夹,但例程只添加了216个文件夹名称.经过一些比较和调试后,我注意到未添加的4个文件夹名称在名称中包含点.
为了测试,我创建了一个名为"Test Folder"的文件夹,在里面我添加了9个名为的新文件夹:
- 文件夹1
- 文件夹2
- 文件夹3
- 文件夹4 …
我正在用Delphi XE2编写程序.在我的程序中的某一点上,将向用户呈现输出文本,我通过简单的"showmessage('感兴趣的文本')"对话框显示该文本.
我希望用户能够复制并粘贴此文本,如果他们愿意的话.它根本不是该计划的关键部分,但我知道能够这样做可能会很好.
目前,据我所知,不可能从这种类型的消息框中选择全部或部分文本,这使我产生了一个问题:是否有一些方法可以使消息框文本可供选择用户,或者如果我想要这个功能,我还需要继续使用其他方式显示文本吗?任何帮助将不胜感激.