【问题标题】:How can I create a fluent interface for defining Dialog Boxes?如何创建一个流畅的界面来定义对话框?
【发布时间】:2009-08-07 15:38:08
【问题描述】:

我正在寻找使用流畅界面定义简单对话框(和其他 UI 元素)的示例经验

(我可能需要在内部编程语言中添加对自定义对话框的支持,我认为流畅的界面可能是最好的方法)

如果这会影响您的答案,那么 UI 系统将基于 Winforms 或 WPF。


如果界面不流畅,我将问题更改为“一个简单易用(和阅读)的 API..”,不依赖于“拖放”UI 设计器的使用。

p>

我认为结果会在某种程度上流利,例如

文本框(“名称”)。标签(“人 姓名”)。栏目(1)

文本框(“笔记”)。标记(“注释”)。 多行(4)。列(1).ToColumn(3)

但是界面不必是单行


这个“How to make Databinding type safe and support refactoring” 为数据绑定的流畅界面提供了一个良好的起点。

【问题讨论】:

  • 能否提供一个流畅的界面示例?
  • rAyt,我试图发现的部分内容是流畅界面的使用应该是什么样子
  • 您为什么不告诉我们您对“对话框”的确切含义?您需要消息框、表格或其他东西吗?
  • 我的意思是一个带有简单字段的基本模型表单,这些字段是标签并以一种很好的方式布局。

标签: wpf winforms user-interface fluent-interface


【解决方案1】:

我为我的对话框构建了一个流畅的界面,类似于:

var result = Dialog
               .Buttons(buttons.Ok, buttons.Cancel)
               .Title("")
               .Text("")
               .Show();

if ( result == DialogResult.Ok) {
    //...
}

我也有一个用于接收类似这样的枚举:

var result = Dialog(of EnumName)
               .Text("")
               .Title("")
               .Show();

if ( result == EnumName.Value1 ) {
  //...
}

从枚举中生成按钮,并返回选定按钮的枚举值。

编辑:从 cmets 添加:

它显示的表单的宽度计算为适合一行中的所有按钮。 它有一种添加额外控件的方法。 布局由流布局面板组成(一个水平用于按钮。一个垂直用于文本和其他控件) 一般布局是标准消息框。 它有另一个自动加速按钮的选项。

方法总结:

.Buttons(paramarray of DialogResult)
.FromEnum<T>(enum)
.Title(text)
.Text(text)
.Control(control)
.AutoAccelerate
.Icon(image)
.Show() as T

【讨论】:

  • 我几乎不使用第一个示例,但我一直使用第二个示例。我几乎不再需要创建一个特殊的对话框,只需定义一个枚举(或使用现有的枚举)并使用该对话框。它唯一的缺点是如果你传递一个巨大的枚举,大约有 4 个以上的项目,但你可以添加范围过滤作为战斗的选项。
  • 在我看来,这个示例表明流畅的界面在某些方面并不适用。至少将它们更改为诸如“.WithTitle(”和“.ContainingText(”)之类的介词,如果您觉得必须将一堆属性设置串起来,中间有点。
  • 您是如何处理定义输入字段、标签和布局的?
  • 布局是相当固定的,因为它看起来像一个标准的消息框,除了你可以放入自己的按钮。因此计算了对话框的宽度。其中有一个允许插入其他控件的功能( .HasControl(new textbox) )还有一个自动添加加速键的功能
【解决方案2】:

到目前为止给出的示例并没有降低任务的复杂性;他们只用一种语法交换另一种(几乎同样冗长)的语法。如果您花时间创建流畅的界面,请利用它来实际提高 API 的表现力,而不仅仅是摇晃句法糖。将抽象级别从默认原语(按钮、模式等)提升到模板、可视化继承链和行为。

我还没有完全考虑清楚,但大致如下:

Dialog
 .WithStandardColors()
 .WithTitleOf("ChooseSomething")
 .WithButtonSet<OkCancel>()
 .Show();

Dialog
 .UseErrorFormatting
 .SetTitleTo("Uh Oh")
 .Show()

【讨论】:

    【解决方案3】:

    这个问题已经让我发疯了好几天。我认为您可能需要问的一个问题是“我为什么要为对话框制作流畅的 API?”

    当您查看流行的流畅 API 时,您会注意到它们的共同点,因为它可以帮助用户流畅地阅读一行代码。和一句话差不多。观察:

    来自忍者:

    Bind(typeof(IWeapon)).To(typeof(Sword));
    

    起订量:

    mock.Setup(foo => foo.Execute("ping"))
        .Returns(() => calls)
        .Callback(() => calls++);
    

    来自所有流畅 API 之母 Linq:

    var query = Products
        .Where(p => p.Name.Contains("foo")
        .OrderBy(p => p.Name);
    

    这些都是很好的 API,几乎可以提供一个句子结构来使用。

    再举一个例子,这是怎么回事:

    Dialog.Buttons(buttons.Ok, buttons.Cancel).Title("").Text("")
    

    更具可读性和实用性
    new Dialog()
    {
         Buttons = Buttons.OkCancel,
         Title = "",
         Text = ""
    };
    

    这只是一个简单的例子。我注意到你在问如何在一行代码中填充布局等内容。天哪,你的台词会很长。

    我认为您需要确定您是否真的认为流畅的 API 可以为您带来任何好处。我看到的只是在对话框上设置属性的方法,不提供任何可读性或价值。

    【讨论】:

      【解决方案4】:

      流畅接口的LINQ示例:

      var customerTurnover = allOrders
                             .Where (o.CustomerID == CustomerID)
                             .Sum (o => o.Amount);
      

      基本上,它是一种设计界面的方法,可以最大限度地减少冗长,并提供一种自然且易读的方式来组合操作,以便用很少的代码完成很多工作。

      对话框域的一个虚构示例:

      DialogBoxAPI
      .ModalDialogBox ()
      .RoundCornersStyle ()
      .BackgroundColor (RGB (200, 200, 200))
      .TextColor (0, 0, 0)
      .MessageText ("What shall we decide?")
      .OKButton ()
      .CancelButton ();
      

      这将生成一个具有所提供特征的对话框。这就是你要找的吗?

      【讨论】:

      • 我建议 .CornerStyle(CornerStyles.Round)
      • @Dykam:这是一个选项,是的。由作者决定他更喜欢哪种风格 - 有几个选项作为方法参数或几个没有参数的单独方法。
      • 是的。如果有多个选项,您的方法可能会出现问题,API 会变得混乱。
      • 我知道什么是流畅的界面。您的对话框示例未涵盖如何定义输入字段、标签和布局的难点
      【解决方案5】:

      我在扩展方法和流畅调用结合匿名方法的单一“上下文”方面有很好的经验。

      我希望例子会更清楚:

      using System;
      using System.Drawing;
      using System.Windows.Forms;
      
      namespace TcKs.FluentSample {
          class FluentSample {
              Form CreateDialogBox() {
                  var frm = new Form();
                  frm.AddTextField( "Simple text field:" )
                      .AddTextField( "Advanced text field:", null, txt => txt.BackColor = Color.Red )
                      .AddTextField( "Complex text field:", lbl => {
                          lbl.Click += ( _sender, _e ) => MessageBox.Show( lbl, "Some informative text.", "Help" );
                          lbl.Font = new Font( lbl.Font, FontStyle.Underline );
                          lbl.Cursor = Cursors.Hand;
                      },
                          txt => {
                              txt.TextChanged += ( _sender, _e ) => txt.BackColor = txt.TextLength > 0 ? SystemColors.Window : Color.Red;
                              txt.DoubleClick += ( _sender, _e ) => { /* TODO: show lookup dialog */ };
                              txt.AddErrorProvider();
                          } )
                      .AddButton( btn => btn.Click += ( _sender, _e ) => frm.Close() );
      
                  return frm;
              }
          }
      
          // contains standard extension methods for fluent creation of control
          static class StandardControlFluentExtensionMethods {
              // this extension method create button and add them to parent
              public static T AddButton<T>( this T parent ) where T : Control {
                  return AddButton<T>( parent, (Action<Button>)null );
              }
              // this extension method create button and add them to parent, then call initMethod
              public static T AddButton<T>( this T parent, Action<Button> initButton ) where T : Control {
                  var button = new Button();
                  parent.Controls.Add( button );
                  if ( null != initButton ) { initButton( button ); }
                  return parent;
              }
          }
      
          // contains specialized extension methods for fluent creation of control
          static class SpecializedControlFluentExtensionMethods {
              public static T AddCloseButton<T>( this T parent, Action<Button> initButton ) where T : Control {
                  return parent.AddButton( btn => {
                      var frm = btn.FindForm();
                      if ( null != frm ) { frm.Close(); }
      
                      if ( null != initButton ) { initButton( btn ); }
                  } );
              }
          }
      
          // contains data-driven extension methods for fluent creation of control
          static class DataDrivenControlFluentExtensionMethods {
              public static TParent AddTextField<TParent>( this TParent parent, string title ) where TParent : Control {
                  return AddTextField<TParent>( parent, title, (Action<Label>)null, (Action<TextBox>)null );
              }
              public static TParent AddTextField<TParent>( this TParent parent, string title, Action<Label> initTitle, Action<TextBox> initEditor ) where TParent : Control {
                  Label lblTitle = new Label();
                  // lblTitle .....
                  if ( null != initTitle ) { initTitle( lblTitle ); }
      
                  TextBox txtEditor = new TextBox();
                  // txtEditor ....
                  if ( null != initEditor ) { initEditor( txtEditor ); }
      
                  return parent;
              }
      
              public static TParent AddErrorProvider<TParent>( this TParent parent ) where TParent : Control {
                  return AddErrorProvider( parent, (Action<ErrorProvider>)null );
              }
              public static TParent AddErrorProvider<TParent>( this TParent parent, Action<ErrorProvider> initErrorProvider ) where TParent : Control {
                  // create and/or initilaize error provider
                  return parent;
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2010-11-01
        • 2014-09-21
        • 2022-08-19
        • 1970-01-01
        • 2010-09-19
        • 1970-01-01
        • 2018-11-29
        • 2010-10-21
        • 1970-01-01
        相关资源
        最近更新 更多