我必须合作一个软件项目,其中 1 名团队成员使用 Delphi 12.2,而其他一些团队成员仍使用 Delphi 10.4。
通过 Git repos 交换源代码现在产生了一些不兼容性,因为在使用旧版本的 Delphi 编译器打开源代码时发生了一些新错误:
ECustomStyleException 样式错误:未找到值
将不同版本 Delphi 编译器一起使用的最佳解决方案是什么?
是否有工具可以根据需求删除此新功能?
我必须合作一个软件项目,其中 1 名团队成员使用 Delphi 12.2,而其他一些团队成员仍使用 Delphi 10.4。
通过 Git repos 交换源代码现在产生了一些不兼容性,因为在使用旧版本的 Delphi 编译器打开源代码时发生了一些新错误:
ECustomStyleException 样式错误:未找到值
将不同版本 Delphi 编译器一起使用的最佳解决方案是什么?
是否有工具可以根据需求删除此新功能?
如果我有这样的枚举类型:
TOAuthSubjectTypes = (
[MappingAttr('public')]
ostPublic,
[MappingAttr('pairwise')]
ostPairwise
);
在特定时刻,我有一个保存的变量ostPairwise
,是否可以检索值的属性ostPairwise
?现在,我正在应用的解决方法是按枚举类型的顺序设置所有属性,然后通过GetAttributes
枚举类型的序数值到达它们,但我想知道是否有更惯用的方法。
抱歉,如果重复了。我在其他语言标签上找到了很多答案,但没有与 Delphi 相关的答案。
我正在我的 FMX 应用程序中开发类似于 Microsoft Teams 的标记功能。
它在 Teams 中的外观如下:
现在在我的版本中,我还想向弹出窗口添加编辑字段和其他内容。
我开始尝试使用TPopup
,但问题是,如果您单击弹出窗口TEdit
内的 ,弹出窗口将关闭。由于 中的WndProc
代码FMX.Platform.Win
:
WM_ACTIVATE:
begin
if not ((TFmxFormState.Recreating in LForm.FormState) or (LForm.FormStyle = TFormStyle.Popup) or
WindowHandleToPlatform(LForm.Handle).FDisableDeactivate) then
begin
if LoWord(wParam) <> 0 then
begin
if HiWord(wParam) = 0 then
LForm.Activate;
// If the window is minimized, then do nothing.
end
else
begin
PrepareClosePopups;
LForm.Deactivate;
ClosePopupList; // This is called when clicking on edit on popup
end;
end;
Result := 0;
end;
ClosePopupList
在单击弹出窗口内部时调用TEdit
,此函数关闭弹出窗口。
因此我改用TForm
并解决了该问题,但在显示带有我的备忘录的表格后Show
不再具有焦点。
我希望将焦点放在备忘录上,除非我们点击弹出窗口内的编辑,在这种情况下,编辑应该获得焦点,弹出窗口应该保持打开状态。如果我点击弹出窗口外,则弹出窗口应该关闭。
有办法解决这个问题吗?
这可能有点愚蠢,甚至在文档中解释过,但我找不到它。
很多 Delphi 类中似乎都重复出现一种模式,即TCustomSomething
在实际之前创建一个TSomething
。我想知道这样做是否有任何特殊原因,如果我想创建派生类,TSomething
我应该从继承TCustomSomething
还是直接从继承TSomething
?
我可以举几个例子:
TStringStream
自TByteStream
,继承自 ,TMemoryStream
最终继承TCustomMemoryStream
。TRESTClient
直接继承自TCustomRESTClient
。TButton
继承自TCustomButtom
。确实有一些情况我看到了基TCustom...
类的实际好处,比如TMemo
和TRichEdit
,但我不明白为什么TRichEdit
从继承TCustomRichEdit
而不是直接从继承TCustomMemo
。
TL;DR:Delphi 中的类有什么用TCustomFoo
?扩展现有类时我应该继承TCustomFoo
还是TFoo
直接从现有类继承?
TRichEdit 具有以下属性:EnableURLs
和ShowURLHint
。EnableURLs 自动将 Internet URL(例如http://www.example.com)转换为超链接。
使用下面的代码,我尝试使用电子邮件地址实现相同的功能。这仅在视觉上有效:
问题:但是,如何在单击电子邮件地址时自动实现[email protected]超链接功能(crHandPoint 光标和 OnURLClick 事件处理程序)?
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls;
type
TForm1 = class(TForm)
RichEdit1: TRichEdit;
Label1: TLabel;
procedure RichEdit1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
System.RegularExpressions;
procedure DetectAndLinkifyEmailsAndURLs(RichEdit: Vcl.ComCtrls.TRichEdit);
var
Match: System.RegularExpressions.TMatch;
EmailPattern, URLPattern: string;
LineIndex, LineStart, StartPos, SelLength: Integer;
LineText: string;
PrevSelStart, PrevSelLength: Integer;
begin
EmailPattern := '\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b';
URLPattern := '\b(http|https)://[^\s]+';
// Disable redraw to avoid flicker
SendMessage(RichEdit.Handle, WM_SETREDRAW, WPARAM(False), 0);
try
// Save user selection
PrevSelStart := RichEdit.SelStart;
PrevSelLength := RichEdit.SelLength;
// Reset all formatting to default for the entire text
RichEdit.SelectAll;
RichEdit.SelAttributes.Color := Vcl.Graphics.clWindowText;
RichEdit.SelAttributes.Style := [];
// Process line by line
for LineIndex := 0 to RichEdit.Lines.Count - 1 do
begin
LineText := RichEdit.Lines[LineIndex];
LineStart := RichEdit.Perform(EM_LINEINDEX, LineIndex, 0); // Get line start position
// Format email addresses in the line
Match := System.RegularExpressions.TRegEx.Match(LineText, EmailPattern, [System.RegularExpressions.roIgnoreCase]);
while Match.Success do
begin
StartPos := LineStart + Match.Index - 1;
SelLength := Match.Length;
RichEdit.SelStart := StartPos;
RichEdit.SelLength := SelLength;
RichEdit.SelAttributes.Color := Vcl.Graphics.clBlue;
RichEdit.SelAttributes.Style := [Vcl.Graphics.fsUnderline];
Match := Match.NextMatch;
end;
// Format URLs in the line
Match := System.RegularExpressions.TRegEx.Match(LineText, URLPattern, [System.RegularExpressions.roIgnoreCase]);
while Match.Success do
begin
StartPos := LineStart + Match.Index - 1;
SelLength := Match.Length;
RichEdit.SelStart := StartPos;
RichEdit.SelLength := SelLength;
RichEdit.SelAttributes.Color := Vcl.Graphics.clBlue;
RichEdit.SelAttributes.Style := [Vcl.Graphics.fsUnderline];
Match := Match.NextMatch;
end;
end;
// Restore user selection
RichEdit.SelStart := PrevSelStart;
RichEdit.SelLength := PrevSelLength;
// Ensure the current selection uses default formatting
if (PrevSelStart >= 0) and (PrevSelLength = 0) then
begin
RichEdit.SelAttributes.Color := Vcl.Graphics.clWindowText;
RichEdit.SelAttributes.Style := [];
end;
finally
// Re-enable redraw
SendMessage(RichEdit.Handle, WM_SETREDRAW, WPARAM(True), 0);
RichEdit.Invalidate; // Refresh to reflect changes
end;
end;
procedure TForm1.RichEdit1Click(Sender: TObject);
begin
DetectAndLinkifyEmailsAndURLs(RichEdit1);
end;
end.
我正在尝试使用records在 Delphi 中实现Builder 设计模式。我的目标是允许方法链接,类似于在 Kotlin、Java 或 Swift 中的做法。例如:
val alertDialog = AlertDialog.Builder(this)
.setTitle("Builder Example")
.setMessage("This is a builder pattern example")
.create()
在 Delphi 中,我使用记录编写了以下代码:
type
TAlertDialogBuilder = record
private
FTitle: string;
FMessage: string;
public
function SetTitle(const ATitle: string): TAlertDialogBuilder;
function SetMessage(const AMessage: string): TAlertDialogBuilder;
procedure CreateAndShow;
end;
implementation
function TAlertDialogBuilder.SetTitle(const ATitle: string): TAlertDialogBuilder;
begin
FTitle := ATitle;
Result := Self; // Creates a copy
end;
function TAlertDialogBuilder.SetMessage(const AMessage: string): TAlertDialogBuilder;
begin
FMessage := AMessage;
Result := Self; // Creates a copy
end;
procedure TAlertDialogBuilder.CreateAndShow;
begin
ShowMessage(Format('Title: %s%sMessage: %s', [FTitle, sLineBreak, FMessage]));
end;
我这样使用它:
procedure ShowDialogExample;
begin
TAlertDialogBuilder.Create
.SetTitle('Builder Example')
.SetMessage('This is a builder pattern example')
.CreateAndShow;
end;
问题:
由于记录在 Delphi 中是值类型,因此在和Self
等方法中返回的结果将复制该记录。这效率不高,尤其是当记录包含许多字段或大量数据时。SetTitle
SetMessage
我尝试过的:
返回指向记录的指针:我修改了方法,使其返回指向记录的指针(PAlertDialogBuilder
)而不是Self
。虽然这避免了复制,但它使链接变得不那么直观,因为我需要取消引用指针以^
进行后续调用。
使用absolute
:我考虑使用absolute
关键字来避免复制,但我无法找到让它用于链接方法调用的方法。
切换到类:我知道类可以避免这个问题,因为它们是引用类型,但如果可能的话,我宁愿坚持使用记录,因为它们具有轻量级的特性和值语义。
我的问题:
如何使用记录在 Delphi 中实现 Builder 模式,同时避免不必要的复制?有没有办法absolute
或其他方法可以有效地实现这一点?
我想下载 JSON 信息
idHTTP := TIdHTTP.Create(nil);
JSON:=idHTTP.get('https://login.microsoftonline.com/common/.well-known/openid-configuration');
并出现错误
10054 对端重置连接
. 在浏览器中下载有效。
我该如何着手发现该问题?
我创建了该对象的替代品TApplication
。
除其他功能(内存和 GUI 日志记录、将 GUI 状态保存到磁盘、单例实例、用户/应用程序路径相关实用程序等)外,它还实现了一个可以正确初始化 GUI 的地方(不再滥用 OnFormCreate!)通过PostMessage(MForm.Handle, MSG_LateFormInit)
应用程序发送到主窗体的已发布窗口消息。
但是,我发现了一个问题:如果我TrySetStyle()
在使主窗体可见之前调用,我的消息就会丢失。
我相信这是因为TrySetStyle()
内部重新创建了表单,这可能会影响消息循环(MSG_LateFormInit
消息丢失或未按预期发送?)。
DPR 如下所示:
program Stuff;
begin
AppData:= TAppData.Create('My App Name');
AppData.CreateMainForm(TMainForm, MainForm, False {Not visible}); // This calls PostMessage
// Warning:
// Don't call TrySetStyle until the main form is visible.
// Solution: Make the main form visible before you call TrySetStyle!
TStyleManager.TrySetStyle('Amakrits');
Application.Run;
end.
难题:
我不想将 POST 代码移到后面TrySetStyle()
。我不想强迫用户在主窗体/DPR 中调用比必要更多的代码,因为很容易忘记这样做。到目前为止,所有代码都是独立的(嗯……窗体中有拦截消息的方法MSG_LateFormInit
,但这是可选的)。所有更改都在 DPR 中完成。我想保持这种状态。
另一方面,我确实想TrySetStyle()
在表单仍然不可见时进行调用以防止闪烁。
PS:我不知道用户是否会打电话TrySetStyle()
。这对他来说是可选的。
因此,我需要找到一种可靠的方式来 POST 和接收MSG_LateFormInit
信号,最好是在主窗体仍然不可见时。
我正在将大量类从 Integer 迁移到 Int64,而我的 32 位应用程序中都是 32 位。
如果我将一个 32 位整数传递给一个类型参数明确定义为 Int64 的过程,但由于 Int32 与 Int64 兼容,因此我希望编译器抛出异常,而不会显示任何错误。
我想这样做是为了确保我不会错过任何参考。
我正在尝试使用 Delphi 12.2 Pro 检索实例化的通用类的 RTTI 信息。
Delphi 文档指出:
https://docwiki.embarcadero.com/RADStudio/Athens/en/Overview_of_Generics
运行时类型识别
在 Win32 中,泛型和方法没有运行时类型信息 (RTTI),但实例化类型有 RTTI。
我可以获取实例化泛型类的字段和属性的 RTTI 信息。但是,我无法获取方法的 RTTI。下面是示例代码。我遗漏了什么吗?还是这是一个错误?
program Project1;
{$M+}
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Rtti;
type
TTestObject<T> = class
private
FValue: T;
function GetValue: T;
public
constructor Create(AValue: T);
property Value: T read GetValue;
end;
TIntegerObject = class(TTestObject<Integer>)
end;
{ TTestObject }
constructor TTestObject<T>.Create(AValue: T);
begin
FValue := AValue;
end;
function TTestObject<T>.GetValue: T;
begin
Result := FValue;
end;
var
t: TRttiType;
m: TRttiMethod;
o: TIntegerObject;
n: string;
begin
try
o := nil;
n := '?';
with TRttiContext.Create do
try
o := TIntegerObject.Create(10);
t := GetType(o.ClassType);
for m in t.GetMethods do
begin
if (m.name = 'GetValue') then
begin
n := m.Name;
break;
end;
end;
WriteLn('Name: ' + n); //writes "Name: ?"
ReadLn;
finally
o.free;
Free;
end;
except
on E: Exception do
begin
Writeln(E.ClassName, ': ', E.Message);
ReadLn;
end;
end;
end.