2016-08-02 51 views
8

所以这是我写过的最湿润的代码之一。但它很有用,这很烦人。所有重复的原因是因为我想保持界面流畅。如果我增加了基类(这恰好是View在这种情况下),它只会给后面的View一个实例,这将阻止我做这样的事情如何干掉这个F#代码? (流畅接口)

let label = theme.CreateLabel().WithMargin(new Thickness(5.0)).WithText("Hello") 

因为Label.Text财产不被执行基类为View

所以这里是我流利的界面。做好准备。这是丑陋的,重复的。但它也可以工作,而且使用起来很方便。

我错过了一个明显的方法来干掉它吗?

module ViewExtensions = 
    let private withTwoWayBinding<'TElement, 'TProperty, 'TViewModel, 'TView when 'TView :> IViewFor<'TViewModel>>(viewModel: 'TViewModel, view: 'TView, viewModelProperty: Expr<'TViewModel -> 'TProperty>, viewProperty: Expr<'TView -> 'TProperty>) (element: 'TElement) = 
     view.Bind(viewModel, ExpressionConversion.toLinq viewModelProperty, ExpressionConversion.toLinq viewProperty) |> ignore 
     element 
    let private withHorizontalOptions<'TElement when 'TElement :> View> options (element: 'TElement) = 
     element.HorizontalOptions <- options 
     element 
    let private withVerticalOptions<'TElement when 'TElement :> View> options (element: 'TElement) = 
     element.VerticalOptions <- options 
     element 
    let private withAlignment<'TElement when 'TElement :> View> horizontalOptions verticalOptions (control: 'TElement) = 
     control |> withHorizontalOptions horizontalOptions |> withVerticalOptions verticalOptions 
    let private withMargin<'TElement when 'TElement :> View> margin (element: 'TElement) = 
     element.Margin <- margin 
     element 
    let private withActions<'TElement> (actions: ('TElement -> unit)[]) (element: 'TElement) = 
     for action in actions do action(element) 
     element 
    type Xamarin.Forms.Entry with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) = withTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Entry -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Grid with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Grid -> unit) = this.With([|action|]) 
    type Xamarin.Forms.StackLayout with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: StackLayout -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Button with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.WithText(text) = this.Text <- text; this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Button -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Switch with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) = withTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Switch -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Label with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.WithText(text) = this.Text <- text; this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Label -> unit) = this.With([|action|]) 

UPDATE

所以,感谢你的帮助,答案是肯定的,我失去了一些东西明显。作为TheQuickBrownFox解释说,如果我通过

module ViewExtensions = 
    let withTwoWayBinding<'TElement, 'TProperty, 'TViewModel, 'TView when 'TView :> IViewFor<'TViewModel>>(viewModel: 'TViewModel, view: 'TView, viewModelProperty: Expr<'TViewModel -> 'TProperty>, viewProperty: Expr<'TView -> 'TProperty>) (element: 'TElement) = 
     view.Bind(viewModel, ExpressionConversion.toLinq viewModelProperty, ExpressionConversion.toLinq viewProperty) |> ignore 
     element 
    let withHorizontalOptions options (element: #View) = element.HorizontalOptions <- options; element 
    let withVerticalOptions options (element: #View) = element.VerticalOptions <- options; element 
    let withAlignment horizontalOptions verticalOptions element = element |> withHorizontalOptions horizontalOptions |> withVerticalOptions verticalOptions 
    let withMargin margin (element: #View) = element.Margin <- margin; element 
    let withCaption text (element: #Button) = element.Text <- text; element 
    let withText text (element: #Entry) = element.Text <- text; element 
    let withContent text (element: #Label) = element.Text <- text; element 
    let withSetUpActions<'TElement> (actions: ('TElement -> unit)[]) (element: 'TElement) = (for action in actions do action(element)); element 
    let withSetUpAction<'TElement> (action: 'TElement -> unit) = withSetUpActions([|action|]) 

改变流畅的界面形式

let label = theme.CreateLabel() |> withMargin(new Thickness(5.0)) |> withContent("Hello") 

,那么你在上面看到的怪物可以整体更换的东西此代码删除是非常讨人喜欢确实如此。

+2

你能不能做后缀应用程序(又名“管道转发”),而不是扩展方法?这样你可以使这些函数具有通用约束。 –

+0

我不得不承认,我不太清楚你的意思,直到我阅读TheQuickBrownFox的答案。它现在非常有意义。它的作用非常好。正在更新。 –

回答

11

惯用F#的方法来流畅的接口仅仅是使用管道正向操作|>

module ViewHelpers 
    let withMargin margin element = ... 
    let withText text element = ... 

open ViewHelpers 

let label = theme.CreateLabel() |> withMargin (new Thickness(5.0)) |> withText "Hello" 

我想你也可以使用flexible types缩短您的函数签名:

let withMargin margin (element: #View) = ... 
+1

这听起来不错。我会尽快尝试,如果一切顺利,请将其标记为答案。正如F#经常发生的那样,我总是在学习。灵活的类型是一个绝妙的想法,我从来没有听说过它们。 –