编写自定义属性检查器 - 验证值时如何处理就地编辑器焦点?

2024-04-10

Overview

我正在尝试编写自己的简单属性检查器,但我面临着一个困难且相当令人困惑的问题。首先,我要说的是,我的组件并不是要使用或处理组件属性,而是允许向其添加自定义值。我的组件的完整源代码位于问题的更下方,一旦将其安装在包中并从新的空项目运行它,它应该看起来像这样:

问题(简要)

问题与就地编辑器的使用和验证属性值有关。这个想法是,如果属性值无效,则向用户显示一条消息,通知他们该值不能被接受,然后将焦点返回到最初关注的行和就地编辑器。

我们实际上可以使用 Delphi 自己的对象检查器来说明我正在寻找的行为,例如尝试在Name无法接受的属性,然后单击远离对象检查器。将显示一条消息,关闭它后,它将返回到Name row.

源代码

如果没有任何代码,问题就变得太模糊了,但由于我试图编写的组件的性质,它也很大。为了问题和示例的目的,我已尽可能地剥离它。我确信会有一些评论问我为什么不这样做或那样做,但重要的是要知道我不是 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;
    FNamesFont: TFont;
    FValuesFont: TFont;

    FSelectedRow: Integer;

    procedure SetNamesFont(const AValue: TFont);
    procedure SetValuesFont(const AValue: TFont);

    procedure CalculateInspectorHeight;
    function GetMousePosition: TPoint;
    function MousePositionToRowIndex: Integer;
    function RowIndexToMousePosition(ARowIndex: Integer): Integer;
    function GetRowHeight: Integer;
    function GetValueRowWidth: Integer;
    function RowExists(ARowIndex: Integer): Boolean;
    function IsRowSelected: Boolean;

  protected
    procedure Loaded; override;
    procedure Paint; override;
    procedure WMKeyDown(var Message: TMessage); message WM_KEYDOWN;
    procedure WMMouseDown(var Message: TMessage); message WM_LBUTTONDOWN;
    procedure WMMouseMove(var Message: TMessage); message WM_MOUSEMOVE;
    procedure WMMouseUp(var Message: TMessage); message WM_LBUTTONUP;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    function RowCount: Integer;

    property Items: TMyInspectorItems read FInspectorItems write FInspectorItems;
    property OnMouseMove: TOnMouseMoveEvent read FOnMouseMove write FOnMouseMove;
    property OnSelectRow: TOnSelectRowEvent read FOnSelectRow write FOnSelectRow;
  published
    property Align;
  end;

  TMyPropertyInspector = class(TScrollBox)
  private
    FInspector: TMyCustomInspector;
    FInplaceStringEditor: TEdit;

    FSelectedRowName: string;
    FLastSelectedRowName: string;
    FLastSelectedRow: Integer;

    function SetPropertyValue(RevertToPreviousValueOnFail: Boolean): Boolean;

    procedure InplaceStringEditorEnter(Sender: TObject);
    procedure InplaceStringEditorExit(Sender: TObject);
    procedure InplaceStringEditorKeyPress(Sender: TObject; var Key: Char);
    procedure SelectRow(Sender: TObject; PropName, PropValue: string; RowIndex: Integer);
    function ValidateStringValue(Value: string): Boolean;
  protected
    procedure Loaded; override;
    procedure WMSize(var Message: TMessage); message WM_SIZE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure AddItem(APropName, APropValue: string);
    function GetSelectedPropertyName: string;
    function GetSelectedPropertyValue: string;
    function RowCount: Integer;
  end;

var
  FCanSelect: Boolean;

implementation

{ TMyInspectorItems }

constructor TMyInspectorItems.Create;
begin
  inherited Create;
  FPropertyNames  := TStringList.Create;
  FPropertyValues := TStringList.Create;
end;

destructor TMyInspectorItems.Destroy;
begin
  FPropertyNames.Free;
  FPropertyValues.Free;
  inherited Destroy;
end;

procedure TMyInspectorItems.AddItem(APropName, APropValue: string);
begin
  FPropertyNames.Add(APropName);
  FPropertyValues.Add(APropValue);
end;

procedure TMyInspectorItems.Clear;
begin
  FPropertyNames.Clear;
  FPropertyValues.Clear;
end;

{ TMyCustomInspector }

constructor TMyCustomInspector.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FInspectorItems     := TMyInspectorItems.Create;

  FNamesFont          := TFont.Create;
  FNamesFont.Color    := clWindowText;
  FNamesFont.Name     := 'Segoe UI';
  FNamesFont.Size     := 9;
  FNamesFont.Style    := [];

  FValuesFont         := TFont.Create;
  FValuesFont.Color   := clNavy;
  FValuesFont.Name    := 'Segoe UI';
  FValuesFont.Size    := 9;
  FValuesFont.Style   := [];
end;

destructor TMyCustomInspector.Destroy;
begin
  FInspectorItems.Free;
  FNamesFont.Free;
  FValuesFont.Free;
  inherited Destroy;
end;

procedure TMyCustomInspector.Loaded;
begin
  inherited Loaded;
end;

procedure TMyCustomInspector.Paint;

  procedure DrawBackground;
  begin
    Canvas.Brush.Color := clWindow;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(Rect(0, 0, Self.Width, Self.Height));
  end;

  procedure DrawNamesBackground;
  begin
    Canvas.Brush.Color := clWindow;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(Rect(0, 0, Self.Width div 2, Self.Height));
  end;

  procedure DrawNamesSelection;
  begin
    if (FRowCount > -1) and (RowExists(MousePositionToRowIndex)) then
    begin
      Canvas.Brush.Color := $00E0E0E0;
      Canvas.Brush.Style := bsSolid;
      Canvas.FillRect(Rect(0, RowIndexToMousePosition(FSelectedRow),
        Self.Width div 2, RowIndexToMousePosition(FSelectedRow) + GetRowHeight));
    end;
  end;

  procedure DrawNamesText;
  var
    I: Integer;
    Y: Integer;
  begin
    FRowCount := FInspectorItems.FPropertyNames.Count;

    Canvas.Brush.Style  := bsClear;
    Canvas.Font.Color   := FNamesFont.Color;
    Canvas.Font.Name    := FNamesFont.Name;
    Canvas.Font.Size    := FNamesFont.Size;

    Y := 0;
    for I := 0 to FInspectorItems.FPropertyNames.Count -1 do
    begin
      Canvas.TextOut(2, Y, FInspectorItems.FPropertyNames.Strings[I]);
      Inc(Y, GetRowHeight);
    end;
  end;

  procedure DrawValuesBackground;
  begin
    Canvas.Brush.Color := clWindow;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(Rect(Self.Width div 2, 0, Self.Width, Self.Height));
  end;

  procedure DrawValuesSelection;
  begin
    if (FRowCount > -1) and (RowExists(MousePositionToRowIndex)) then
    begin
      Canvas.DrawFocusRect(Rect(Self.Width div 2, RowIndexToMousePosition(FSelectedRow),
        Self.Width, RowIndexToMousePosition(FSelectedRow) + GetRowHeight));
    end;
  end;

  procedure DrawValues;
  var
    I, Y: Integer;
  begin
    FRowCount := FInspectorItems.FPropertyValues.Count;

    Y := 0;
    for I := 0 to FInspectorItems.FPropertyValues.Count -1 do
    begin
      Canvas.Brush.Style  := bsClear;
      Canvas.Font.Color   := FValuesFont.Color;
      Canvas.Font.Name    := FValuesFont.Name;
      Canvas.Font.Size    := FValuesFont.Size;

      Canvas.TextOut(Self.Width div 2 + 2, Y + 1, FInspectorItems.FPropertyValues.Strings[I]);
      Inc(Y, GetRowHeight);
    end;
  end;

begin
  DrawNamesBackground;
  DrawNamesSelection;
  DrawNamesText;
  DrawValuesBackground;
  DrawValuesSelection;
  DrawValues;
end;

procedure TMyCustomInspector.WMKeyDown(var Message: TMessage);
begin
  inherited;

  case Message.WParam of
    VK_DOWN:
    begin

    end;
  end;
end;

procedure TMyCustomInspector.WMMouseDown(var Message: TMessage);
begin
  inherited;

  Parent.SetFocus;

  FSelectedRow := MousePositionToRowIndex;

  if FSelectedRow <> -1 then
  begin
    if Assigned(FOnSelectRow) then
    begin
      FOnSelectRow(Self, FInspectorItems.FPropertyNames.Strings[FSelectedRow],
        FInspectorItems.FPropertyValues.Strings[FSelectedRow], FSelectedRow);
    end;
  end;

  Invalidate;
end;

procedure TMyCustomInspector.WMMouseMove(var Message: TMessage);
begin
  inherited;

  if Assigned(FOnMouseMove) then
  begin
    FOnMouseMove(Self, GetMousePosition.X, GetMousePosition.Y);
  end;
end;

procedure TMyCustomInspector.WMMouseUp(var Message: TMessage);
begin
  inherited;
end;

procedure TMyCustomInspector.SetNamesFont(const AValue: TFont);
begin
  FNamesFont.Assign(AValue);
  Invalidate;
end;

procedure TMyCustomInspector.SetValuesFont(const AValue: TFont);
begin
  FValuesFont.Assign(AValue);
  Invalidate;
end;

procedure TMyCustomInspector.CalculateInspectorHeight;
var
  I, Y: Integer;
begin
  FRowCount := FInspectorItems.FPropertyNames.Count;

  Y := GetRowHeight;
  for I := 0 to FRowCount -1 do
  begin
    Inc(Y, GetRowHeight);
  end;

  if Self.Height <> Y then
    Self.Height := Y;
end;

function TMyCustomInspector.GetMousePosition: TPoint;
var
  Pt: TPoint;
begin
  Pt := Mouse.CursorPos;
  Pt := ScreenToClient(Pt);
  Result := Pt;
end;

function TMyCustomInspector.MousePositionToRowIndex: Integer;
begin
  Result := GetMousePosition.Y div GetRowHeight;
end;

function TMyCustomInspector.RowIndexToMousePosition(
  ARowIndex: Integer): Integer;
begin
  Result := ARowIndex * GetRowHeight;
end;

function TMyCustomInspector.GetRowHeight: Integer;
begin
  Result := FNamesFont.Size * 2 + 1;
end;

function TMyCustomInspector.GetValueRowWidth: Integer;
begin
  Result := Self.Width div 2;
end;

function TMyCustomInspector.RowCount: Integer;
begin
  Result := FRowCount;
end;

function TMyCustomInspector.RowExists(ARowIndex: Integer): Boolean;
begin
  Result := MousePositionToRowIndex < RowCount;
end;

function TMyCustomInspector.IsRowSelected: Boolean;
begin
  Result := FSelectedRow <> -1;
end;

{ TMyPropertyInspector }

constructor TMyPropertyInspector.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Self.DoubleBuffered               := True;
  Self.Height                       := 150;
  Self.HorzScrollBar.Visible        := False;
  Self.TabStop                      := True; // needed to receive focus
  Self.Width                        := 250;

  FInspector                        := TMyCustomInspector.Create(Self);
  FInspector.Parent                 := Self;
  FInspector.Align                  := alTop;
  FInspector.Height                 := 0;
  FInspector.OnSelectRow            := SelectRow;

  FInplaceStringEditor              := TEdit.Create(Self);
  FInplaceStringEditor.Parent       := Self;
  FInplaceStringEditor.BorderStyle  := bsNone;
  FInplaceStringEditor.Color        := clWindow;
  FInplaceStringEditor.Height       := 0;
  FInplaceStringEditor.Left         := 0;
  FInplaceStringEditor.Name         := 'MyPropInspectorInplaceStringEditor';
  FInplaceStringEditor.Top          := 0;
  FInplaceStringEditor.Visible      := False;
  FInplaceStringEditor.Width        := 0;
  FInplaceStringEditor.Font.Assign(FInspector.FValuesFont);

  FInplaceStringEditor.OnEnter      := InplaceStringEditorEnter;
  FInplaceStringEditor.OnExit       := InplaceStringEditorExit;
  FInplaceStringEditor.OnKeyPress   := InplaceStringEditorKeyPress;

  FCanSelect                        := True;
end;

destructor TMyPropertyInspector.Destroy;
begin
  FInspector.Free;
  FInplaceStringEditor.Free;
  inherited Destroy;
end;

procedure TMyPropertyInspector.Loaded;
begin
  inherited Loaded;
end;

procedure TMyPropertyInspector.WMSize(var Message: TMessage);
begin
  FInspector.Width := Self.Width;
  Invalidate;
end;


procedure TMyPropertyInspector.AddItem(APropName, APropValue: string);
begin
  FInspector.CalculateInspectorHeight;
  FInspector.Items.AddItem(APropName, APropValue);
  FInspector.Invalidate;
  Self.Invalidate;
end;

function TMyPropertyInspector.GetSelectedPropertyName: string;
begin
  Result := '';

  if FInspector.FSelectedRow <> -1 then
  begin
    Result := FInspector.FInspectorItems.FPropertyNames.Strings[FInspector.FSelectedRow];
  end;
end;

function TMyPropertyInspector.GetSelectedPropertyValue: string;
begin
  Result := '';

  if FInspector.FSelectedRow <> -1 then
  begin
    Result := FInspector.FInspectorItems.FPropertyValues.Strings[FInspector.FSelectedRow];
  end;
end;

function TMyPropertyInspector.RowCount: Integer;
begin
  Result := FInspector.RowCount;
end;

procedure TMyPropertyInspector.InplaceStringEditorEnter(Sender: TObject);
begin
  FCanSelect := False;
  FLastSelectedRow := FInplaceStringEditor.Tag;
end;

procedure TMyPropertyInspector.InplaceStringEditorExit(Sender: TObject);
begin
  if SetPropertyValue(True) then
  begin
    FCanSelect := True;
  end;
end;

procedure TMyPropertyInspector.InplaceStringEditorKeyPress(Sender: TObject;
  var Key: Char);
begin
  if Key = Chr(VK_RETURN) then
  begin
    Key := #0;
    FInplaceStringEditor.SelectAll;
  end;
end;

procedure TMyPropertyInspector.SelectRow(Sender: TObject; PropName, PropValue: string; RowIndex: Integer);
begin
  FSelectedRowName     := PropName;
  FLastSelectedRowName := PropName;

  FInplaceStringEditor.Height   := FInspector.GetRowHeight - 2;
  FInplaceStringEditor.Left     := Self.Width div 2;
  FInplaceStringEditor.Tag      := RowIndex;
  FInplaceStringEditor.Text     := GetSelectedPropertyValue;
  FInplaceStringEditor.Top      := FInspector.RowIndexToMousePosition(FInspector.FSelectedRow) + 1 - Self.VertScrollBar.Position;
  FInplaceStringEditor.Visible  := True;
  FInplaceStringEditor.Width    := FInspector.GetValueRowWidth - 3;
  FInplaceStringEditor.SetFocus;
  FInplaceStringEditor.SelectAll;
end;

function TMyPropertyInspector.SetPropertyValue(
  RevertToPreviousValueOnFail: Boolean): Boolean;
var
  S: string;
begin
  Result := False;

  S := FInplaceStringEditor.Text;

  if ValidateStringValue(S) then
  begin
    Result := True;
  end
  else
  begin
    ShowMessage('"' + S + '"' + 'is not a valid value.');
    Result := False;
  end;
end;

function TMyPropertyInspector.ValidateStringValue(Value: string): Boolean;
begin
  // a quick and dirty way of testing for a valid string value, here we just
  // look for strings that are not zero length.
  Result := Length(Value) > 0;
end;

end.

问题(详细)

我的困惑都归结为谁首先受到关注以及如何正确处理和响应它。因为我自定义绘制行,所以我确定单击检查器控件时鼠标所在的位置,然后绘制所选行以显示这一点。然而,在处理就地编辑器时,尤其是OnEnter and OnExit我一直面临着各种奇怪的问题,在某些情况下,我陷入了重复显示验证错误消息的循环中(因为焦点从我的检查器切换到就地编辑器并来回切换)。

要在运行时填充我的检查器,您可以执行以下操作:

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyPropertyInspector1.AddItem('A', 'Some Text');
  MyPropertyInspector1.AddItem('B', 'Hello World');
  MyPropertyInspector1.AddItem('C', 'Blah Blah');
  MyPropertyInspector1.AddItem('D', 'The Sky is Blue');
  MyPropertyInspector1.AddItem('E', 'Another String');
end;

您可以尝试一些事情:

  • 单击一行
  • 从就地编辑器中删除内容
  • 选择另一行
  • 出现验证错误消息框(先不要关闭它)
  • 在消息框仍然可见的情况下,将鼠标移到另一行上
  • 现在按 Enter 键关闭消息框
  • 您会注意到所选行现在已移动到鼠标所在的位置

我需要的是在验证消息框显示并关闭后,我需要将焦点设置回首先验证的行。它变得令人困惑,因为似乎(或者我认为)就地编辑器OnExit在之后被调用WMMouseDown(var Message: TMessage);我的检查员的代码。

如果问题仍然不清楚,我可以尽可能简单地说,Delphi 对象检查器的行为就是我试图在我的组件中实现的。您在就地编辑器中输入一个值,如果验证失败,则显示一个消息框,然后将焦点返回到最后选择的行。一旦焦点从就地编辑器移开,就地编辑器验证就应该发生。


我似乎无法弄清楚首先调用的是什么以及触发的阻塞事件是什么,这变得令人困惑,因为我绘制所选行的方式是由单击检查器控件时鼠标所在的位置决定的。

这是您的事件流程:

  • TMyCustomInspector.WMMouseDown is called
    1. Therein, Parent.SetFocus is called
      • 焦点将从编辑控件中移除,并且TMyPropertyInspector.InplaceStringEditorExit叫做
      • 消息对话框显示为SetPropertyValue
    2. FSelectedRow正在重置
    3. TMyPropertyInspector.SelectRow被称为(通过TMyCustomInspector.FOnSelectRow) 将焦点重置到替换的编辑控件。

你需要做的是防止FSelectedRow在验证失败的情况下重置。所有需要的成分都已经存在,只需添加以下一个条件:

  if FCanSelect then
    FSelectedRow := MousePositionToRowIndex;

几点说明:

  • Make FCanSelect受保护或私有领域TMyCustomInspector,
  • 您需要检查以下限制TMyCustomInspector.MousePositionToRowIndex为了返回-1.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写自定义属性检查器 - 验证值时如何处理就地编辑器焦点? 的相关文章

  • 在 Pascal 中将文本文件中的字符串读入数组

    使用这个程序 我试图读取一个文件并将其随机打印到控制台 我想知道是否必须使用数组 例如 我可以将字符串分配到一个数组中 然后从数组中随机打印 但是 我不确定如何解决这个问题 另一个问题是 我当前的程序没有从我的文件中读取第一行 我有一个文本
  • Move() 从动态字符串数组中插入/删除项目

    使用 System Move 从字符串数组中插入 删除项目并不像从其他简单数据类型数组中插入 删除项目那么容易 问题是 字符串在 Delphi 中是引用计数的 在引用计数数据类型上使用 Move 需要对内部编译器行为有更深入的了解 这里有人
  • 桌面 Delphi 应用程序是否可以通过 Windows 8 认证(使用 Windows 应用程序认证套件)?

    显然 Delphi 任何版本 不支持安全异常处理程序 https forums embarcadero com thread jspa messageID 473469 Visual Studio 中的 SAFESEH 开关 在 Windo
  • 可以在delphi数据集中创建一个假数据字段吗?

    我想在 DataSet 不是 ClientDataSet 中创建一个 假 数据字段 该字段不应存储在数据库中 它不是计算字段 应允许用户输入输入数据 该字段具有业务逻辑含义 因此用户更新其值后应该更新其他字段 使用 OnFieldChang
  • 如何仅在某些列中设置带有复选框的 TListView?

    我正在使用 Delphi 2010 并且我试图允许用户在 TListView 中的每行 2 个选项之间进行选择 使用 TListView 我可以将样式设置为 vsReport 并启用复选框 但这只会让我每行有 1 个复选框 我需要的是每行
  • TRESTRequest:是否可以在 POST 请求中使用自定义媒体类型?

    例如 我们有一个 API 需要我们自己的供应商特定内容类型application vnd xxxx custom custom data json但查看 REST Client 的源代码 它似乎总是默认为 REST Types 中的 Con
  • TControlState.csDesignerHide 与 TControlStyle.csNoDesignVisible

    VCL 似乎提供了两种向表单设计者隐藏控件的机制 TControlState csDesignerHide and TControlStyle csNoDesignVisible 就 IDE 而言 它们之间有什么区别 什么时候应该使用哪个
  • 能否从 Vista Shell 获取 48x48 或 64x64 图标?

    如果 Vista Shell 中存在 48x48 或 64x64 图标 如何使用 SHGetFileInfo 获取在 TImage 中显示图标的句柄 我想从图像列表中选择一个代表文件夹路径的图标 并在 Timage 中显示 48x48 或
  • Delphi - 相当于C#的三元运算符? [复制]

    这个问题在这里已经有答案了 可能的重复 Delphi 中是否存在或者将来是否存在条件运算符 https stackoverflow com questions 2108609 is there or is there ever going
  • Delphi如何使用其他窗体中的类型?

    抱歉 这是一个非常新手的问题 我正在对这个庞大的应用程序进行维护 它有5种不同的形式 我们将全局变量放在一个单元 uGlobal 中 但我似乎无法从数据单元 uData 访问它 我有这个 Unit uGlobal type TmyType
  • 在 SQLite 数据库中存储日期时间值的最佳方式(Delphi)

    我将把日期时间值存储在 SQLite 数据库中 使用 Delphi 和 DISqlite 库 数据库的本质是它永远不需要在计算机或系统之间传输 因此互操作性不是一个限制 相反 我的重点是阅读速度 日期时间字段将被索引 我将对其进行大量搜索
  • Delphi XE2 Firemonkey 示例应用程序未在 MAC 上运行

    我正在尝试在 Mac 上运行示例 Firemonkey 应用程序 但我在 Mac 中收到以下消息 dyld Library not loaded rpath libcgunwind 1 0 dylib Referenced from Use
  • 如何读取注册表项的默认值

    我有一个 Delphi XE2 项目来使用注册表项进行某些操作 所以我定义了以下代码 procedure TMainForm BitBtn01Click Sender TObject var RegistryEntry TRegistry
  • 如何追踪手柄泄漏?

    在我的一个应用程序中 我观察到句柄数量不断增加 在不使用应用程序的情况下 该数字大约每秒增加一次 因此后台处理代码的某些部分一定存在句柄泄漏 我如何追踪此类泄漏 有什么工具可以帮助解决这个问题吗 跟踪句柄泄漏时要寻找哪些模式 导致手柄泄漏的
  • 以 png 格式剪辑幻灯片 (Delphi 2010)

    I have a filmstrip of images in png format like this 我想知道如何剪辑每个图像并将这些图像放入 TImageList 控件中 并始终保留透明度 EDIT 是的 在设计时 RRUZ 提到的技
  • Delphi中的抽象类

    我正在使用一个具有许多抽象类的组件套件 现在我想应用多态性 但在创建对象时收到错误抽象类 即使我不需要 我是否应该重写所有虚拟方法 有什么解决方法或解决方案吗 为了创建类的实例 您需要重写所有声明为虚拟抽象的方法 即使您不使用它们 如果您确
  • 在 Delphi 2009 上安装最新版本的 Indy 10 [重复]

    这个问题在这里已经有答案了 是否有更新 Delphi 2009 中的 Indy 10 组件的分步指南 我读过正在卸载线程 https stackoverflow com questions 486210 what is the proper
  • 在 Delphi 2007 中将具有透明度的位图保存为 PNG

    我有一个包含透明度信息的 Delphi 位图 32 位 我需要将其转换并保存为 PNG 文件 同时保留透明度 我目前拥有的工具是graphics32 Library GR32 PNG 由Christian Budde 提供 和PNGImag
  • 使用 TStringList 的分隔符解析字符串,似乎也解析空格(Delphi)

    我有一个简单的字符串 由某个字符分隔 比如说逗号 我应该能够创建一个 TStringList 并将其分隔符设置为逗号 然后将 DelimitedText 设置为我想要解析的文本 并且应该自动解析它 问题是 当我查看输出时 它还包含空格作为分
  • 从 Delphi 调用 C# dll

    我用单一方法编写了 Net 3 5 dll 由Delphi exe调用 不幸的是它不起作用 步骤 1 使用以下代码创建 C 3 5 dll public class MyDllClass public static int MyDllMet

随机推荐