【问题标题】:Globally-accessible data storage in a menu driven program?菜单驱动程序中的全局可访问数据存储?
【发布时间】:2011-01-13 03:13:24
【问题描述】:

我是 Stack Overflow 的新手,但我发现自己在这个网站上寻找一些最好的编程解决方案。所以我有一个问题要问。

我正在用 Delphi 编写一个程序,它是一个用于本地商业客户的 TUI 菜单驱动程序。他们要求我保持与旧程序中的用户界面相同(用 BASIC 为 MS-DOS 编写,日期为 1982 年),因此它是所有菜单驱动的,全局数据存储在文件中并由程序重新加载。每个子菜单本身就是一个由活动菜单运行的程序(也是一个程序)。

我编写了自己的 TUI 框架和 UI 管理器来显示菜单和子菜单。 UI 管理器包含一个名为“Draw”的覆盖方法来显示菜单和另一个名为“OnEvent”的覆盖方法,它处理 UI 中的键盘事件。 我的第一个问题是,您认为这是制作包含子菜单的菜单驱动程序的合适方法吗?这是如何工作的一个示例:

type
  TMenu1 = class(TExtendedUIManager)
  private
    procedure OnEvent (c: Char); override;
  end;

type
  TSubMenu1 = class(TExtendedUIManager)
  end;

procedure TMenu1.OnEvent (c: Char);
var
  Next: TExtendedUIManager;
begin
  if c = '2' then begin
    Next := TSubMenu1.Create;
    Self.Start(Next);
    Next.Free;
  end;
end;

我的另一个问题是在菜单之间共享数据的适当方式是什么?例如,如果我希望我的 TSubMenu1 类在调用方法时返回一个字符串,我将如何实现它可访问其他不与之交互的子菜单? (对不起,如果问题含糊不清)。我想到了单例模式,但我也想过让 UI 管理器存储对某个对象的引用以进行数据存储,并且每次运行新的子菜单时,将引用传递给新的子菜单(UI经理)。难题在于找出哪一个效果最好。或者即使我的菜单驱动框架很不错。

欢迎提出意见并感谢任何建议。感谢您的时间和帮助!

--托德

【问题讨论】:

  • 您能解释一下为什么您需要编写自己的框架和 UI 管理器,而不仅仅是使用标准的 Delphi 组件吗?什么是“TUI”?你是说图形用户界面吗??
  • TUI == 文本用户界面。如果是我,我真的很想使用 ncurses 之类的东西来使 TUI 实现可以接受,但我认为 Delphi 暗示了 Windows 环境——ncurses 可能不太适合。
  • 是的,很遗憾我必须使用 Windows,但我的 TUI 框架与 ncurses 的用途相似。我自己编写的,因为原始程序具有某些 UI 特征,例如所有包含空格句点的文本提示、每个 UI 多个提示等。Writeln 和 Readln 是不够的。
  • “我的另一个问题是......”通常意味着在 SO 上打开第二个问题
  • 如果您正在重写应用程序,请给他们升级:您可以保持相同的菜单组织,但使应用程序成为真正的 GUI 应用程序。我绝对肯定没有充分的理由保留基于文本的界面。示例:在表单上放置 3 个大按钮,您将获得一个 3 选项菜单。用户可以单击按钮(因此它的行为与所有 Windows 应用程序一样),用户可以左右切换并按 Enter,您可以注册加速键,以便他们可以根据需要使用一键访问。

标签: delphi menu global


【解决方案1】:

“我的另一个问题是在菜单之间共享数据的适当方式是什么?”

您可以使用类方法和属性来共享数据。通过使用这些,您甚至可以在不创建类实例的情况下访问它们。欲了解更多信息,请通过this Link

以下是共享List的示例代码。

type
  TForm1 = class(TForm)
    ---
    ---
  private
    { Private declarations }
    class var List: TStringList;
    ---
  end;
var
  Form1, Form2: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.List.Add('4');
  Form2.List.Add('5');

  ShowMessage(TForm1.List.Text);
end;

initialization
  StrList := TStringList.Create;
  TForm1.List := TStringList.Create;
  TForm1.List.Add('1');
  TForm1.List.Add('2');
  TForm1.List.Add('3');
  ShowMessage(TForm1.List.Text);


finalization
  FreeAndNil(TForm1.List);
end.

【讨论】:

  • 这听起来像是我的编程设计所追求的。我的 Delphi (7.0) 版本不支持静态实例字段,但我决定在根目录中初始化列表,然后通过引用菜单树中的子节点来传递列表。
  • 我最终决定采用这样的方式。我最终创建了一个 Context 对象来存储对活动数据的引用(例如配置信息、SQLite 数据库引用和菜单设置),并在每次调用新菜单或屏幕时传递上下文。我不确定它在编程社区中的接受程度如何,但我尝试构建类似于 Android API 功能(通过给定上下文调用对象的“活动”)的东西,因为我最近开始使用它。不管怎样,谢谢大家的帮助!
【解决方案2】:

我会坦率地承认,这对我来说是一个大声思考的领域,但首先引起我注意的是:

procedure TMenu1.OnEvent (c: Char);
var
  Next: TExtendedUIManager;
begin
  if c = '2' then begin
    Next := TSubMenu1.Create;
    Self.Start(Next);
    Next.Free;
  end;
end;

感觉这种编程的结束条件将是if c = '2' then ... else if c = '3' then ... else 等巨大的决策树。这确实使编写和维护变得乏味。如果可行,带有要执行的函数的数组映射输入字符通常更容易维护。

[['2', foo_create],
['3', foo_delete],
['4', foo_ship],
['d', foo_destroy_all],
['`', foo_return_to_previous_menu]]

当一个新角色进来时,你会在表中查找相应的函数并执行它。完成后,返回等待。

您可以进一步扩展此数组以跟踪所需的参数和每个函数返回的值。当您执行一个函数时,将其返回值存储在某个全局变量中,并跟踪返回的类型。 (也许 Delphi 的类型系统已经足够复杂以至于它是微不足道的,甚至不值得一提。)当您执行一个新函数时,检查“当前”返回结果的类型是否适合传递给所需的函数。 (如果类型错误,您甚至可以将菜单条目设为灰色,以向用户表明此组合不起作用。)

/* key function    arg   ret types */
[['2', foo_create, NULL, FOO],
['3', foo_delete, FOO, NULL],
['4', foo_ship, FOO, NULL],
['d', foo_destroy_all, NULL, NULL],
['`', foo_return_to_previous_menu, NULL, NULL]]

我希望这在某种程度上有用。 :)

【讨论】:

  • 我知道你是从哪里来的。程序的某些部分需要数字输入或单个字符输入,具体取决于使用的菜单。 Delphi(至少我的版本)除了创建记录并将它们存储在数组中之外没有任何形式的映射。我正在思考的问题是我的设计是使用树来存储正在运行的子菜单(主菜单是根节点),还是应该通过使用列表的某些实现将它们保持在同一级别。跨度>
猜你喜欢
  • 2012-05-14
  • 2017-01-30
  • 2011-08-09
  • 1970-01-01
  • 2020-06-13
  • 1970-01-01
  • 1970-01-01
  • 2015-05-17
  • 1970-01-01
相关资源
最近更新 更多