【问题标题】:How to display a table in ShowMessage?如何在 ShowMessage 中显示表格?
【发布时间】:2011-01-10 16:05:28
【问题描述】:

我正在尝试使用 ShowMessage 显示如下所示的表格:

short            | Description for "short"
verylongtext     | Description for "verylongtext"

如何在一个简单的消息对话框中获得两个正确对齐的列?

我尝试使用空格对齐列,但 ShowMessage 的字体是可变的。然后我尝试使用制表符对齐它们,但我不知道如何计算每行的正确制表符数。

有没有可靠的方法来计算标签数?

PS:我想避免为此编写自定义对话框。

【问题讨论】:

    标签: delphi fonts dialog delphi-2007


    【解决方案1】:

    您也可以在自定义对话框中使用列表视图。

    我的班级支持标准的 Windows 图标(和声音):信息、警告、错误、确认、无。这是无图标版本:

    使用方便:

    TTableDialog.ShowTable
      (
        Self,
        'Audio Properties',
        ['Duration', 'Samples per channel', 'Total data size', 'Channels', 'Bits per sample', 'Sample rate', 'Bitrate'],
        ['1 h 15 min 0 s', '216 000 000', '824 MB', '1', '32', '48 kHz', '1 536 kbit/sec'],
        mtInformation
      )
    

    它支持 DPI 缩放(高 DPI)和从 Windows XP(它可能也适用于 Windows 2000,我只是没有测试过)到 Windows 10 的所有 Windows 版本:

    表格是一个列表视图,因此您可以获得它的所有好处,例如滚动条、截断省略号和工具提示:

    您还可以指定对话框的大小以使其适合内容:

    TTableDialog.ShowTable
      (
        Self,
        'Audio Properties',
        ['Duration', 'Samples per channel', 'Total data size', 'Channels', 'Bits per sample', 'Sample rate', 'Bitrate', 'Maximum fractional sample value'],
        ['1 h 15 min 0 s', '216 000 000', '824 MB', '1', '32', '48 kHz', '1 536 kbit/sec', '0.1'],
        mtInformation,
        360,
        240
      )
    

    当然,OK按钮同时是DefaultCancel,所以你可以用EnterEscape关闭对话框.

    最后,按下 Ctrl+C 会将表格复制到剪贴板。

    完整源代码:

    uses
      ComCtrls, Math, Clipbrd;
    
    type
      TTableDialog = class
      strict private
        type TFormData = class(TComponent)
        public
          ListView: TListView;
          IconKind: PWideChar;
          Icon: HICON;
          LIWSD: Boolean;
        end;
        class function Scale(X: Integer): Integer;
        class procedure FormShow(Sender: TObject);
        class procedure FormDestroy(Sender: TObject);
        class procedure FormPaint(Sender: TObject);
        class procedure FormKeyPress(Sender: TObject; var Key: Char);
        class procedure LVToClipboard(AListView: TListView);
      public
        class procedure ShowTable(AOwner: TCustomForm; const ACaption: string;
          const ANames, AValues: array of string;
          ADialogType: TMsgDlgType = mtInformation;
          const AWidth: Integer = 360; const AHeight: Integer = 200);
      end;
    
    class procedure TTableDialog.FormShow(Sender: TObject);
    var
      FormData: TFormData;
      ComCtl: HMODULE;
      LoadIconWithScaleDown: function(hinst: HINST; pszName: LPCWSTR; cx: Integer;
        cy: Integer; var phico: HICON): HResult; stdcall;
    begin
      if not (Sender is TForm) then
        Exit;
      if not (TObject(TForm(Sender).Tag) is TFormData) then
        Exit;
      TForm(Sender).OnShow := nil;
      FormData := TFormData(TForm(Sender).Tag);
      if FormData.IconKind = nil then
        Exit;
      ComCtl := LoadLibrary('ComCtl32.dll');
      if ComCtl <> 0 then
      begin
        try
          LoadIconWithScaleDown := GetProcAddress(ComCtl, 'LoadIconWithScaleDown');
          if Assigned(LoadIconWithScaleDown) then
            FormData.LIWSD := Succeeded(LoadIconWithScaleDown(0, FormData.IconKind,
              Scale(32), Scale(32), FormData.Icon));
        finally
          FreeLibrary(ComCtl);
        end;
      end;
      if not FormData.LIWSD then
        FormData.Icon := LoadIcon(0, FormData.IconKind);
    end;
    
    class procedure TTableDialog.FormDestroy(Sender: TObject);
    var
      FormData: TFormData;
    begin
      if not (Sender is TForm) then
        Exit;
      if not (TObject(TForm(Sender).Tag) is TFormData) then
        Exit;
      FormData := TFormData(TForm(Sender).Tag);
      if (FormData.Icon <> 0) and FormData.LIWSD then
        DestroyIcon(FormData.Icon);
    end;
    
    class procedure TTableDialog.FormKeyPress(Sender: TObject; var Key: Char);
    var
      FormData: TFormData;
    begin
      if not (Sender is TForm) then
        Exit;
      if not (TObject(TForm(Sender).Tag) is TFormData) then
        Exit;
      FormData := TFormData(TForm(Sender).Tag);
      case Key of
        ^C:
          LVToClipboard(FormData.ListView);
      end;
    end;
    
    class procedure TTableDialog.FormPaint(Sender: TObject);
    var
      FormData: TFormData;
      Frm: TForm;
      Y: Integer;
    begin
    
      if not (Sender is TForm) then
        Exit;
    
      if not (TObject(TForm(Sender).Tag) is TFormData) then
        Exit;
    
      Frm := TForm(Sender);
      FormData := TFormData(TForm(Sender).Tag);
    
      Y := Frm.ClientHeight - Scale(25 + 8 + 8);
    
      Frm.Canvas.Brush.Color := clWhite;
      Frm.Canvas.FillRect(Rect(0, 0, Frm.ClientWidth, Y));
    
      Frm.Canvas.Pen.Color := $00DFDFDF;
      Frm.Canvas.MoveTo(0, Y);
      Frm.Canvas.LineTo(Frm.ClientWidth, Y);
    
      if FormData.Icon <> 0 then
        DrawIconEx(Frm.Canvas.Handle, Scale(8), Scale(8), FormData.Icon,
          Scale(32), Scale(32), 0, 0, DI_NORMAL);
    
    end;
    
    class procedure TTableDialog.LVToClipboard(AListView: TListView);
    
      function GetRow(AIndex: Integer): string;
      begin
        if InRange(AIndex, 0, AListView.Items.Count - 1) and (AListView.Items[AIndex].SubItems.Count = 1) then
          Result := AListView.Items[AIndex].Caption + #9 + AListView.Items[AIndex].SubItems[0]
        else
          Result := '';
      end;
    
    var
      S: string;
      i: Integer;
    begin
      if AListView = nil then
        Exit;
      S := GetRow(0);
      for i := 1 to AListView.Items.Count - 1 do
        S := S + sLineBreak + GetRow(i);
      Clipboard.AsText := S;
    end;
    
    class function TTableDialog.Scale(X: Integer): Integer;
    begin
      Result := MulDiv(X, Screen.PixelsPerInch, 96);
    end;
    
    class procedure TTableDialog.ShowTable(AOwner: TCustomForm; const ACaption: string;
      const ANames, AValues: array of string;
      ADialogType: TMsgDlgType = mtInformation;
      const AWidth: Integer = 360; const AHeight: Integer = 200);
    const
      Sounds: array[TMsgDlgType] of Integer =
        (MB_ICONWARNING, MB_ICONERROR, MB_ICONINFORMATION, MB_ICONQUESTION, 0);
      Icons: array[TMsgDlgType] of MakeIntResource =
        (IDI_WARNING, IDI_ERROR, IDI_INFORMATION, IDI_QUESTION, nil);
    var
      dlg: TForm;
      lv: TListView;
      btn: TButton;
      i: Integer;
      snd: Integer;
    begin
    
      if Length(ANames) <> Length(AValues) then
        raise Exception.Create('The lengths of the columns don''t match.');
    
      dlg := TForm.Create(AOwner);
      try
    
        dlg.BorderStyle := bsDialog;
        dlg.Caption := ACaption;
        dlg.Width := Scale(AWidth);
        dlg.Height := Scale(AHeight);
        dlg.Position := poOwnerFormCenter;
        dlg.Scaled := False;
        dlg.Font.Name := 'Segoe UI';
        dlg.Font.Size := 9;
        dlg.Tag := NativeInt(TFormData.Create(dlg));
        TFormData(dlg.Tag).IconKind := Icons[ADialogType];
        dlg.OnShow := FormShow;
        dlg.OnDestroy := FormDestroy;
        dlg.OnPaint := FormPaint;
        dlg.OnKeyPress := FormKeyPress;
        dlg.KeyPreview := True;
    
        btn := TButton.Create(dlg);
        btn.Parent := dlg;
        btn.Caption := 'OK';
        btn.Default := True;
        btn.Cancel := True;
        btn.ModalResult := mrOk;
        btn.Width:= Scale(75);
        btn.Height := Scale(25);
        btn.Left := dlg.ClientWidth - btn.Width - Scale(8);
        btn.Top := dlg.ClientHeight - btn.Height - Scale(8);
    
        lv := TListView.Create(dlg);
        TFormData(dlg.Tag).ListView := lv;
        lv.Parent := dlg;
        lv.DoubleBuffered := True;
        lv.ReadOnly := True;
        lv.BorderStyle := bsNone;
        lv.Left := Scale(8) + IfThen(Icons[ADialogType] <> nil, Scale(32 + 8));
        lv.Top := Scale(8);
        lv.Width := dlg.ClientWidth - Scale(16) - IfThen(Icons[ADialogType] <> nil, Scale(32 + 8));
        lv.Height := dlg.ClientHeight - Scale(16 + 8 + 4) - btn.Height;
        lv.ViewStyle := vsReport;
        lv.RowSelect := True;
        lv.ShowColumnHeaders := False;
    
        with lv.Columns.Add do
        begin
          Caption := 'Name';
          Width := Scale(150);
        end;
        with lv.Columns.Add do
        begin
          Caption := 'Value';
          Width := lv.ClientWidth - lv.Columns[0].Width -
            GetSystemMetricsForWindow(SM_CXVSCROLL, dlg.Handle) - scale(2);
        end;
    
        for i := 0 to High(ANames) do
          with lv.Items.Add do
          begin
            Caption := ANames[i];
            SubItems.Add(AValues[i]);
          end;
    
        snd := Sounds[ADialogType];
        if snd <> 0 then
          MessageBeep(snd);
    
        dlg.ShowModal;
    
      finally
        dlg.Free;
      end;
    
    end;
    

    【讨论】:

    • 太好了,它甚至像默认的 MsgBox 一样支持 CTRL+C(尽管它缺少原来的窗口标题和按钮标题)。
    【解决方案2】:

    如果您不为此编写自定义对话框,您什么时候会?这并不难。只需创建一个表单,在其上放置一个 TMemo 并将该备忘录设为只读。您可以设置像 Courier New 这样的等宽字体,您的问题就解决了。您也获得了滚动条和选择的优势,您可以选择使其成为非模态。

    我什至建议在网格(如 TStringGrid)而不是备忘录或标签中显示此类数据。

    计算如何在消息框中显示此文本将需要更多工作,而不仅仅是创建自定义对话框。

    【讨论】:

    • 等宽​​看起来很可怕,但自定义对话框是解决方案都一样
    • 好吧,你说服了我,我会采用 Adreas 的解决方案。
    【解决方案3】:

    刚刚创建了一些显示如下弹出窗口的内容:

    只需调用下面的过程,并添加一个 TStringList 作为参数。 当然你可以通过使用 TListView、图标、滚动条等来拉皮条。

    将它放在一个单独的单元中,您将始终能够轻松地展示此类内容。

    uses ..., StdCtrls, ExtCtrls;
    
    
    procedure ShowTablePopup(SL:TStringList);
    var
      LButtonOK: TButton;
      LMemo: TMemo;
      LPanel: TPanel;
      LForm: TForm;
    begin
      LForm := TForm.Create(Application);
      LMemo := TMemo.Create(LForm);
      LPanel := TPanel.Create(LForm);
      LButtonOK := TButton.Create(LForm);
    
      LForm.Left := 0;
      LForm.Top := 0;
      LForm.Caption := 'Values';
      LForm.ClientHeight := 250;
      LForm.ClientWidth := 400;
    
      LMemo.Parent := LForm;
      LMemo.AlignWithMargins := True;
      LMemo.Left := 3;
      LMemo.Top := 3;
      LMemo.Width := 295;
      LMemo.Height := 226;
      LMemo.Align := alClient;
      LMemo.Font.Name := 'Courier New';
      LMemo.Lines.Assign(SL);
    
      LPanel.Parent := LForm;
      LPanel.Caption := '';
      LPanel.Left := 0;
      LPanel.Top := 232;
      LPanel.Width := 301;
      LPanel.Height := 37;
      LPanel.Align := alBottom;
      LPanel.BevelOuter := bvNone;
    
      LButtonOK.Parent := LPanel;
      LButtonOK.AlignWithMargins := True;
      LButtonOK.Left := 223;
      LButtonOK.Top := 3;
      LButtonOK.Width := 75;
      LButtonOK.Height := 31;
      LButtonOK.Align := alRight;
      LButtonOK.Caption := '&OK';
      LButtonOK.ModalResult := mrOk;
      LButtonOK.Default := True;
    
      LForm.ShowModal;
    end;
    

    使用示例:

    var
      SL:TStringList;
    begin
      SL := TStringList.Create;
      try
        SL.Add('short            | Description for "short"');
        SL.Add('verylongtext     | Description for "verylongtext"');
        ShowTablePopup(SL);
      finally
        SL.Free;
      end;
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-12
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-25
      • 2019-04-06
      • 2011-11-04
      相关资源
      最近更新 更多