【问题标题】:Better approach for storing, retrieving, saving and loading data in a TListBox?在 TListBox 中存储、检索、保存和加载数据的更好方法?
【发布时间】:2021-08-08 16:10:22
【问题描述】:

在 Windows 10 中的 Delphi 10.4.2 win-32 VCL 应用程序中,我使用简单的 TListBox 将记录存储在 ListBox 项中。当用户单击一个项目时,将检索相关记录的数据:

type
  PResizeSettingsRec = ^TResizeSettingsRec;
  TResizeSettingsRec = record
    TopValue:    Integer;
    LeftValue:   Integer;
    RightValue:  Integer;
    BottomValue: Integer;
    Color:       TColor;
    Opacity:     Integer;
  end;

procedure TformMain.btnAddCurrentSettingsClick(Sender: TObject);
var
  P: PResizeSettingsRec;
begin
  New(P);
  P.TopValue    := 5;
  P.LeftValue   := 5;
  P.RightValue  := 5;
  P.BottomValue := 5;
  P.Color       := clRed;
  P.Opacity     := 255;
  listboxMultiResize.AddItem('Step 1', TObject(P));

  New(P);
  P.TopValue    := 9;
  P.LeftValue   := 9;
  P.RightValue  := 9;
  P.BottomValue := 9;
  P.Color       := clBlue;
  P.Opacity     := 127;
  listboxMultiResize.AddItem('Step 2', TObject(P));
end;

procedure TformMain.FormDestroy(Sender: TObject);
begin
  for var i := 0 to listboxMultiResize.Items.Count - 1 do
    Dispose(PResizeSettingsRec(listboxMultiResize.Items.Objects[i]));
end;

procedure TformMain.listboxMultiResizeClick(Sender: TObject);
var
  P: PResizeSettingsRec;
begin
  if listboxMultiResize.ItemIndex < 0 then EXIT;
  P := PResizeSettingsRec(listboxMultiResize.Items.Objects[listboxMultiResize.ItemIndex]);
  CodeSite.Send('TformMain.listboxMultiResizeClick: P.TopValue', P.TopValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: P.LeftValue', P.LeftValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: P.RightValue', P.RightValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: P.BottomValue', P.BottomValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: ColorToString(P.Color)', ColorToString(P.Color));
  CodeSite.Send('TformMain.listboxMultiResizeClick: P.Opacity', P.Opacity);
end;

这种方法故意简单,以使其稳定且万无一失。还是有更好、更现代、更简单的方法来实现这一目标?

有没有一种简单的方法可以将 ListBox 项连同它们的数据一起存储到一个文件中,然后再从文件中重新加载它们? (目前,我使用的是 INI 文件)。

编辑:遵循 Andreas 的建议,我现在使用以下代码:

type
  TResizeSettings = class
    TopValue:    Integer;
    LeftValue:   Integer;
    RightValue:  Integer;
    BottomValue: Integer;
    Color:       TColor;
    Opacity:     Integer;
  end;

procedure TformMain.btnAddCurrentSettingsClick(Sender: TObject);
var
  ThisResizeSettings: TResizeSettings;
begin
  ThisResizeSettings := TResizeSettings.Create;
  try
    ThisResizeSettings.TopValue    := 5;
    ThisResizeSettings.LeftValue   := 5;
    ThisResizeSettings.RightValue  := 5;
    ThisResizeSettings.BottomValue := 5;
    ThisResizeSettings.Color       := clRed;
    ThisResizeSettings.Opacity     := 255;
    listboxMultiResize.AddItem('Step 1', ThisResizeSettings);
  finally
    ThisResizeSettings.Free;
  end;

  ThisResizeSettings := TResizeSettings.Create;
  try
    ThisResizeSettings.TopValue    := 9;
    ThisResizeSettings.LeftValue   := 9;
    ThisResizeSettings.RightValue  := 9;
    ThisResizeSettings.BottomValue := 9;
    ThisResizeSettings.Color       := clBlue;
    ThisResizeSettings.Opacity     := 127;
    listboxMultiResize.AddItem('Step 2', ThisResizeSettings);
  finally
    ThisResizeSettings.Free;
  end;
end;

procedure TformMain.FormDestroy(Sender: TObject);
begin
  for var i := 0 to listboxMultiResize.Items.Count - 1 do
  begin
    TResizeSettings(listboxMultiResize.Items.Objects[i]).Free;
  end;
end;

procedure TformMain.listboxMultiResizeClick(Sender: TObject);
var
  ThisResizeSettings: TResizeSettings;
begin
  if listboxMultiResize.ItemIndex < 0 then EXIT;
  ThisResizeSettings := TResizeSettings(listboxMultiResize.Items.Objects[listboxMultiResize.ItemIndex]);
  CodeSite.Send('TformMain.listboxMultiResizeClick: TopValue', ThisResizeSettings.TopValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: LeftValue', ThisResizeSettings.LeftValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: RightValue', ThisResizeSettings.RightValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: BottomValue', ThisResizeSettings.BottomValue);
  CodeSite.Send('TformMain.listboxMultiResizeClick: ColorToString(P.Color)', ColorToString(ThisResizeSettings.Color));
  CodeSite.Send('TformMain.listboxMultiResizeClick: Opacity', ThisResizeSettings.Opacity);
end;

但是,单击列表框项时,我得到随机结果!

EDIT2:在 Andreas 的回答之后,我现在不再释放创建的对象:

procedure TformMain.btnAddCurrentSettingsClick(Sender: TObject);
// https://stackoverflow.com/questions/68702539/better-approach-for-storing-retrieving-saving-and-loading-data-in-a-tlistbox
var
  ThisResizeSettings: TResizeSettings;
begin
  ThisResizeSettings := TResizeSettings.Create;  
    ThisResizeSettings.TopValue    := 5;
    ThisResizeSettings.LeftValue   := 5;
    ThisResizeSettings.RightValue  := 5;
    ThisResizeSettings.BottomValue := 5;
    ThisResizeSettings.Color       := clRed;
    ThisResizeSettings.Opacity     := 255;
    listboxMultiResize.AddItem('Step 1', ThisResizeSettings);   

  ThisResizeSettings := TResizeSettings.Create;  
    ThisResizeSettings.TopValue    := 9;
    ThisResizeSettings.LeftValue   := 9;
    ThisResizeSettings.RightValue  := 9;
    ThisResizeSettings.BottomValue := 9;
    ThisResizeSettings.Color       := clBlue;
    ThisResizeSettings.Opacity     := 127;
    listboxMultiResize.AddItem('Step 2', ThisResizeSettings);  
end;

我还更改了FormDestroy中ListBox对象的释放:

procedure TformMain.FormDestroy(Sender: TObject);
begin
  // https://stackoverflow.com/questions/68702539/better-approach-for-storing-retrieving-saving-and-loading-data-in-a-tlistbox
  for var i := listboxMultiResize.Items.Count - 1 downto 0 do
  begin
    listboxMultiResize.Items.Objects[i].Free;
    listboxMultiResize.Items.Objects[i] := nil;
  end;

现在它似乎工作了。谢谢你,安德烈亚斯!

【问题讨论】:

  • 使用对象代替记录。然后不需要丑陋的TObject 演员表,因为您实际上是在提供一个对象!这就是它(主要)应该被使用的方式。
  • @AndreasRejbrand 你的意思是,我应该声明一个带有 setter 和 getter 的类而不是记录?记录不是更容易处理吗?一个类不会增加更多不必要的复杂性吗?
  • 一个类不需要有getter和setter。而不是type TTest = record A, B: Integer; c: string; end;,只需执行type TTest = class A, B: Integer; c: string; end;。默认情况下,ABc 将是公开的。如果您不需要它们,则不需要带有 getter 和 setter 的私有字段和公共属性。是的,记录是值类型,而对象是引用类型(您需要创建和释放它们),但在您的情况下,使用引用类型更方便。此外,您已经在进行分配和释放,尽管是以古老的方式!
  • @AndreasRejbrand 我听从了您的建议,使用类而不是记录。但是,当我单击 ListBox 中的一个项目时,我得到的代码大多是随机值:ThisResizeSettings := TResizeSettings(listboxMultiResize.Items.Objects[listboxMultiResize.ItemIndex]);CodeSite.Send('TformMain.listboxMultiResizeClick: TopValue', ThisResizeSettings.TopValue);
  • 那你做错了什么。一开始是不要使用不安全的演员表,而是安全的as演员表。

标签: delphi listbox delphi-10.4-sydney


【解决方案1】:

使用对象代替记录。然后不需要丑陋的TObject 演员表,因为您实际上是在提供一个对象!这就是它(主要)应该被使用的方式。

记住:类不需要有 getter 和 setter,默认情况下,基于TObject 的类具有成员可见性public。如果您不需要它们,则不需要带有 getter 和 setter 的私有字段和公共属性。此外,记录是值类型,而对象是引用类型(您需要创建和释放它们),但在您的情况下,使用引用类型更方便。此外,您已经在进行分配和释放,尽管是以古老的方式!

这是一个简单的例子:

type
  TPerson = class
    Name: string;
    Age: Integer;
  end;

  TForm1 = class(TForm)
    lbPersons: TListBox;
    eName: TEdit;
    lblName: TLabel;
    eAge: TEdit;
    lblAge: TLabel;
    btnAddUpd: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure btnAddUpdClick(Sender: TObject);
    procedure lbPersonsClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

procedure TForm1.btnAddUpdClick(Sender: TObject);
begin
  var idx := lbPersons.Items.IndexOf(eName.Text);
  var Person: TPerson;
  if idx <> -1 then
    Person := lbPersons.Items.Objects[idx] as TPerson
  else
    Person := TPerson.Create;
  Person.Name := eName.Text;
  Person.Age := StrToInt(eAge.Text);
  if idx = -1 then
    lbPersons.Items.AddObject(Person.Name, Person);
  if eName.CanFocus then
    eName.SetFocus;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  for var i := lbPersons.Items.Count - 1 downto 0 do
  begin
    lbPersons.Items.Objects[i].Free;
    lbPersons.Items.Objects[i] := nil;
  end;
end;

procedure TForm1.lbPersonsClick(Sender: TObject);
begin
  if lbPersons.ItemIndex = -1 then
  begin
    eName.Text := '';
    eAge.Text := '';
  end
  else
  begin
    var Person := lbPersons.Items.Objects[lbPersons.ItemIndex] as TPerson;
    eName.Text := Person.Name;
    eAge.Text := Person.Age.ToString;
  end;
end;

【讨论】:

  • 默认可见度不是发布了吗!?
  • @DelphiCoder: 不,它是public,除非在类声明之前使用了{$M+} 指令,或者祖先类默认具有published 成员。所以TPersistent 的后代,包括所有组件(以及所有控件)都将published 作为默认可见性。但是TObject 没有这种状态,所以我的说法“基于TObject 的类具有公开的成员可见性”是正确的。见docwiki.embarcadero.com/RADStudio/Sydney/en/…
猜你喜欢
  • 2019-11-10
  • 1970-01-01
  • 2018-05-15
  • 2021-03-29
  • 2012-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多