2

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

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

如果这会影响您的答案,那么 UI 系统将建立在 Winforms 或 WPF 之上。


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

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

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

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

但是接口不必是单行


这个“如何使数据绑定类型安全并支持重构”为数据绑定的流畅接口提供了一个很好的起点。

4

5 回答 5

3

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

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 ) {
  //...
}

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

编辑:从评论中添加:

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

方法总结:

.Buttons(paramarray of DialogResult)
.FromEnum<T>(enum)
.Title(text)
.Text(text)
.Control(control)
.AutoAccelerate
.Icon(image)
.Show() as T
于 2009-08-09T13:48:13.877 回答
2

这个问题让我发疯了几天。我认为您可能需要问的一个问题是“我为什么要为对话框制作流畅的 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 在这里可以为您带来任何好处。我所看到的只是在对话框上设置属性并且不提供任何可读性或价值的方法。

于 2009-08-12T03:41:06.800 回答
2

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

我还没有完全考虑到这一点,但大致如下:

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

或者

Dialog
 .UseErrorFormatting
 .SetTitleTo("Uh Oh")
 .Show()
于 2009-08-15T10:44:19.570 回答
1

流畅接口的 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 ();

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

于 2009-08-09T13:44:58.730 回答
1

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

我希望例子会更清楚:

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;
        }
    }
}
于 2009-08-17T09:37:20.283 回答