model, Cmd.none let view (model: Model) dispatch = View.ContentPage( content = View.StackLayout( children = [ View.Label(text = model.Message, horizontalOptions = LayoutOptions.Center, verticalOptions = LayoutOptions.CenterAndExpand) ])) // Note, this declaration is needed if you enable LiveUpdate let program = Program.mkProgram init update viewtype App () as app = inherit Application () let runner = App.program#if DEBUG |> Program.withConsoleTrace#endif |> XamarinFormsProgram.run appYes, this is all the code you would usually have in your blank C# app. We will not go into too much detail on how all the functions work. At the bottom, you can see the type App, which translates to the App.xaml.cs class, i.e. the entry point of any Xamarin Forms app. Our analogue to the MainPage is the module App. The three components of the MVU pattern are present with the Model (an F# record, if your new to F# think of it as a POCO, not quite the same but close enough for now) and the view and update functions.更新函数是处理视图的所有更改的位置。仅显示文本此功能无关。由于我们以后将重点关注UI,因此我将为您的普通应用程序中的更新功能提供短介绍。想象一下你所有的UI更改和背景任务事件必须走sequentially通过这一点。定义所有状态更改。您可以重现应用程序的每个状态 - 哦,没有种族条件The view function contains the ContentPage, which includes a StackLayout and a Label. At first, you might not think much about it. But look how terse it is written. For example, the StackLayout children , that is a simple list in F#. So adding another element to the grid would be simply adding a new UI element.The functions get invoked by Fabulous and do not interact with Xamarin Forms directly. This is important to understand because this means that all of the code you write can be 100% unit tested. All the dependencies to the view are resolved within the Fabulous framework. The view function returns the instructions on how to create the UI, but it does not create it. If you change a value such as the welcome message, the Fabulous Framework checks what parts have changed and updates the view accordingly. The React.JS framework uses the same technique with a shadow DOM (Document Object Model) that is then taken to update the actual UI.原子设计和编码UIWriting your UI with code comes with a few perks. While you could write all of your UI in the view function. It might get a bit hard to view at a glance over time. But being only code, you can split up the code into different functions. This also allows you to reuse parts of the UI in different places. And reusability reusing/combining components is at the heart of Atomic Design.虽然独特的设计,可重复使用的组件听起来很棒,让我们看看我们如何设计这样的应用程序。我们希望从基本元素(原子)开始,然后我们将在一起更加重要的UI组件,并在页面结束时。当我们查看应用程序的设计时,我们可以看到大多数标题标签似乎具有相同的字体。另一个快速接受我的眼睛的UI组件是持有目的地描述和要做的事物的表现:现在我们可以看到的是标题具有相同的字体,粗体,除了“待办事项”部分中的卡片分开具有相同的字体大小。因此,让我们创建一个允许我们使用参数文本和字体大小创建标题标签的函数:let titleLabel text fontSize = View.Label(text = text, fontSize = fontSize, textColor = textColor, verticalOptions = LayoutOptions.Center, fontAttributes = FontAttributes.Bold)目的地由图片(如果我可能这么说的话,那么GORGES图片!)和镇上的简短描述,乡村,评级和最喜欢的似乎应该是最喜欢的,所以让我们假设这是一个按钮。可能类似于右上角的搜索按钮。考虑到可访问性,我更喜欢通常使用按钮或平台交互式控制,如果用户需要交互。这样,使用屏幕阅读器优化经验更容易。所以我们想要一个带图标的按钮 - 或文本。由于XAMARIN表单允许我们使用自定义字体,我们可以使用诸如Font Awesome为我们提供可扩展的图标。务必退房James’ post关于如何使用您的Xamarin表单应用程序使用字体令人敬畏。因此,让我们创建一个函数,给出一个图标,颜色,背景颜色和命令函数将我们返回给我们按钮:let materialFont = (match Device.RuntimePlatform with | Device.iOS -> "Material Design Icons" | Device.Android -> "materialdesignicons-webfont.ttf#Material Design Icons" | _ -> null)let materialButton materialIcon backgroundColor textColor command = View.Button(text = materialIcon, command = command, fontFamily = materialFont, fontSize = 20., backgroundColor = backgroundColor, widthRequest = 42., textColor = textColor)所以现在到了描述文本I.。这个国家。让我们再次创建一个函数,它将创建一个标签给出了文本:let descriptionLabel text = View.Label(text = text, textColor = secondaryTextColor, fontSize = descriptionFontSize )您是否注意到标题和描述模式在页面的“待办事项”部分重复。到目前为止,我们已经创建了什么原子设计调用原子。现在让我们将一些原子包装成相干块(分子):let titleAndDescription title titleFontSize description = View.StackLayout(margin = 0., children=[ titleLabel title titleFontSize descriptionLabel description |> fun(label) -> label.Margin (Thickness(0.,-8.,0.,0.))]这将使我们能够重用标题& Description duo further. Also, note that we had to adjust the margin a bit. You can think of the |> as a pipe forward. Since we have a View type, we can pipe it forward to a lambda function where we change the margin. Calling the margin function will again return a View type. If you are using LINQ, you most probably have joined multiple calls to where select et al. - we are doing the exact same thing here.Now looking back at the short description of the destination, we can also see a rating of the city with stars. So let’s create a function that given the icon and text colour returns a Label based on font awesome.let materialIcon materialIcon color = View.Label(text = materialIcon, textColor = color, fontFamily = materialFont, fontSize = 18., verticalOptions = LayoutOptions.Center, fontAttributes = FontAttributes.Bold)评级栏 - 我假设它是一个只读指示器,向我展示零到五之间的整体评级。鉴于4.5的评级,我们想要四颗满恒星和一个覆盖的一半。所以让我们把这个控件分开,让我们说我们想要一个只绘制明星的一个函数:let ratingStar percentage = let star = materialIcon star starColor let boxViewWidth = 16. - (16. * percentage) View.Grid( padding = 0., margin = Thickness(0.,-4.,0.,0.), children = [ star View.BoxView(color = backgroundColor, widthRequest = boxViewWidth, isVisible = (if percentage > 0. then true else false), horizontalOptions = LayoutOptions.End) ])Aka Star Factory的函数被另一个函数调用,鉴于评级:let ratingControl (rating:decimal) = let fullNumber = Math.Ceiling(rating) let fraction = (rating - Math.Truncate(rating)) View.StackLayout(orientation = StackOrientation.Horizontal, children = [ for i in 1m .. fullNumber -> if i = fullNumber then ratingStar (float fraction) else ratingStar 1. ])Now we have all of our building blocks together for the description, but we still have the image with rounded corners left. A quick look at the ImageView from Xamarin Forms tells us: “No rounded edges.” But when putting the image in a Frame, we can create the rounded edges effect. So let’s create a function that gives us an image with round corners:let roundedCornerImage imagePath = View.Frame(cornerRadius = cornerRadius, padding = 0., isClippedToBounds = true, hasShadow = true, content = View.Image( source = imagePath, aspect = Aspect.AspectFill) )这些部件全部现在使让我们组装它们,以便我们通过简短描述使用圆角覆盖的图像:let cityDescriptionFrame city dispatch = View.StackLayout( margin = Thickness(16.,0.,16.,0.), children = [ (roundedCornerImage city.Image |> fun(img) -> img.HeightRequest 320.) View.Frame( heightRequest = 70., margin = Thickness(24.,-64.,24.,0.), padding = Thickness(20.,12.,16.,12.), backgroundColor = Color.White, cornerRadius = cornerRadius, content = View.Grid( rowdefs=["auto"; "auto" ], coldefs=["*";"auto"], children=[ (titleAndDescription city.Name titleFontSize city.Country) (favoriteIcon city dispatch).GridColumn(2) (ratingControl city.Rating).GridRow(1).GridColumnSpan(2) ] ), hasShadow = true) ])同样,我们可以实施“要做的事情”部分。伟大的事情是我们可以重复使用我们已经创建的大量组件。然后,我们可以将所有零件放在视图方法中,将我们带有以下UI:您可以找到整个样本GitHub. Side notes: No, we are not required to have all the code in one file. But since this is a one-pager application, I left it together, so it is easier to navigate the code in a browser. Further note that the CarouselView did not work correctly when I was working with the view. I hope I will be soon able to get it working and have a sample which will allow switching between cities as intended by design.Conclusion将原子设计模式应用于UI,真正让您的应用更容易维护和创建。鉴于允许在代码中编写UI的很棒,在没有多少样品板代码的情况下创建自定义和一致的UI相对简单。进一步的神奇提供了一个现场更新功能,允许您在调试会话期间实现代码。不仅执行UI适应,还执行逻辑。您可以阅读更多信息Live Update功能在官方网站上。在最近的2019年期间,它似乎用代码写入UI。与Apple等公司一起在Swift UI工作。如果你是一个顽固的C#爱人,你应该看看Post by Ryan Davis用C#写UIS for Xamarin。您可以了解有关原子设计模式的更多信息Brad Frosts website.">

使用原子设计,f#和fabulous创建美丽的Xamarin表格应用程序

在阳光灿烂的日子上展示Wodden同行和海洋

极好允许在功能样式中用F#写入Xamarin表单应用程序。很棒的灵感来自榆树 事实证明,通过使用模型视图更新(MVU for Short)模式功能语言非常适合编写UI代码。虽然功能代码挤压了围绕无效和竞争条件的多种潜在虫子 - 在这篇文章中,我们不会专注于美妙的那个方面。相反,让我们看看如何创造在未来保持可维护的美丽UIS。

这个博客帖子是七月宣传ui的一部分史蒂文·克森。一定要查看我的所有美丽帖子同事.

特色#Xamarinuijuly徽章

受到一些史蒂文斯以前的帖子的启发 - 我喜欢打电话的东西 - Lickable Ui。我想在代码中展示为什么写UI允许您编写UI,这不仅是漂亮但易于维护和扩展。所以对于这篇文章来说,我将实施我在发现的设计理念德布布尔经过Apptaste..

App Design如Dribbble所示

虽然这个博客帖子将专注于美妙,但您可以使用C#和XAML编写应用程序时应用相同的原则。但你最终会有一堆文件,它会感到更复杂。 F#要写入的话语首先且非常允许用较少的代码行编写应用程序而不是您通常需要的C#和XAML。我不是说这是你应该理由看看很棒的原因。但事实上,这是一个......如果你是新的,而且整个下一个很短的介绍。如果这是所有旧的新闻,您都可以自由跳过介绍。

一个简短的介绍

Let’s start with the good old Welcome to Xamarin Forms blank app:

module App = 
    type Model = 
      { Message : string } // your apps state, we could do without...

    type Msg = 
        | SomeStateChange // just for the demo, we do not need this...

    let initModel = { Message = "Welcome to Xamarin.Forms!" }

    let init () = initModel, Cmd.none

    let update msg model =
        match msg with
        | SomeStateChange -> model, Cmd.none

    let view (model: Model) dispatch =
        View.ContentPage(
          content = View.StackLayout(
            children = [ 
                View.Label(text = model.Message, horizontalOptions = LayoutOptions.Center, verticalOptions = LayoutOptions.CenterAndExpand)
            ]))

    // Note, this declaration is needed if you enable LiveUpdate
    let program = Program.mkProgram init update view

type App () as app = 
    inherit Application ()

    let runner = 
        App.program
#if DEBUG
        |> Program.withConsoleTrace
#endif
        |> XamarinFormsProgram.run app

Yes, this is all the code you would usually have in your blank C# app. We will not go into too much detail on how all the functions work. At the bottom, you can see the type App, which translates to the App.xaml.cs class, i.e. the entry point of any Xamarin Forms app. Our analogue to the MainPage is the module App. The three components of the MVU pattern are present with the Model (an F# record, if your new to F# think of it as a POCO, not quite the same but close enough for now) and the view and update functions.

更新函数是处理视图的所有更改的位置。仅显示文本此功能无关。由于我们以后将重点关注UI,因此我将为您的普通应用程序中的更新功能提供短介绍。想象一下你所有的UI更改和背景任务事件必须走顺序通过这一点。定义所有状态更改。您可以重现应用程序的每个状态 - 哦,没有种族条件

The view function contains the ContentPage, which includes a StackLayout and a Label. At first, you might not think much about it. But look how terse it is written. For example, the StackLayout children , that is a simple list in F#. So adding another element to the grid would be simply adding a new UI element.

The functions get invoked by Fabulous and do not interact with Xamarin Forms directly. This is important to understand because this means that all of the code you write can be 100% unit tested. All the dependencies to the view are resolved within the Fabulous framework. The view function returns the instructions on how to create the UI, but it does not create it. If you change a value such as the welcome message, the Fabulous Framework checks what parts have changed and updates the view accordingly. The React.JS framework uses the same technique with a shadow DOM (Document Object Model) that is then taken to update the actual UI.

原子设计和编码UI

Writing your UI with code comes with a few perks. While you could write all of your UI in the view function. It might get a bit hard to view at a glance over time. But being only code, you can split up the code into different functions. This also allows you to reuse parts of the UI in different places. And reusability reusing/combining components is at the heart of Atomic Design.

原子设计

虽然独特的设计,可重复使用的组件听起来很棒,让我们看看我们如何设计这样的应用程序。我们希望从基本元素(原子)开始,然后我们将在一起更加重要的UI组件,并在页面结束时。

当我们查看应用程序的设计时,我们可以看到大多数标题标签似乎具有相同的字体。另一个快速接受我的眼睛的UI组件是持有目的地描述和要做的事物的表现:

目的地录取

现在我们可以看到的是标题具有相同的字体,粗体,除了“待办事项”部分中的卡片分开具有相同的字体大小。因此,让我们创建一个允许我们使用参数文本和字体大小创建标题标签的函数:

let titleLabel text fontSize =
    View.Label(text = text,
        fontSize = fontSize,
        textColor = textColor,
        verticalOptions = LayoutOptions.Center,
        fontAttributes = FontAttributes.Bold)

目的地由图片(如果我可能这么说的话,那么GORGES图片!)和镇上的简短描述,乡村,评级和最喜欢的似乎应该是最喜欢的,所以让我们假设这是一个按钮。可能类似于右上角的搜索按钮。考虑到可访问性,我更喜欢通常使用按钮或平台交互式控制,如果用户需要交互。这样,使用屏幕阅读器优化经验更容易。所以我们想要一个带图标的按钮 - 或文本。由于XAMARIN表单允许我们使用自定义字体,我们可以使用诸如字体很棒为我们提供可扩展的图标。务必退房詹姆斯的帖子关于如何使用您的Xamarin表单应用程序使用字体令人敬畏。因此,让我们创建一个函数,给出一个图标,颜色,背景颜色和命令函数将我们返回给我们按钮:

let materialFont =
    (match Device.RuntimePlatform with
                             | Device.iOS -> "Material Design Icons"
                             | Device.Android -> "materialdesignicons-webfont.ttf#Material Design Icons"
                             | _ -> null)

let materialButton materialIcon backgroundColor textColor command =
    View.Button(text = materialIcon,
        command = command,
        fontFamily = materialFont,
        fontSize = 20.,
        backgroundColor = backgroundColor,
        widthRequest = 42.,
        textColor = textColor)

所以现在到了描述文本I.。这个国家。让我们再次创建一个函数,它将创建一个标签给出了文本:

let descriptionLabel text =
    View.Label(text = text,
        textColor = secondaryTextColor,
        fontSize = descriptionFontSize
        )

您是否注意到标题和描述模式在页面的“待办事项”部分重复。到目前为止,我们已经创建了什么原子设计调用原子。现在让我们将一些原子包装成相干块(分子):

let titleAndDescription title titleFontSize description =
    View.StackLayout(margin = 0.,
        children=[
            titleLabel title titleFontSize
            descriptionLabel description |> fun(label) -> label.Margin (Thickness(0.,-8.,0.,0.))]

这将使我们能够重用标题& Description duo further. Also, note that we had to adjust the margin a bit. You can think of the |> as a pipe forward. Since we have a View type, we can pipe it forward to a lambda function where we change the margin. Calling the margin function will again return a View type. If you are using LINQ, you most probably have joined multiple calls to where select et al. - we are doing the exact same thing here.

Now looking back at the short description of the destination, we can also see a rating of the city with stars. So let’s create a function that given the icon and text colour returns a Label based on font awesome.

let materialIcon materialIcon color =
    View.Label(text = materialIcon,
        textColor = color,
        fontFamily = materialFont,
        fontSize = 18.,
        verticalOptions = LayoutOptions.Center,
        fontAttributes = FontAttributes.Bold)

评级栏 - 我假设它是一个只读指示器,向我展示零到五之间的整体评级。鉴于4.5的评级,我们想要四颗满恒星和一个覆盖的一半。所以让我们把这个控件分开,让我们说我们想要一个只绘制明星的一个函数:

let ratingStar percentage =
    let star = materialIcon star starColor
    let boxViewWidth = 16. - (16. * percentage)
    View.Grid(
        padding = 0.,
        margin = Thickness(0.,-4.,0.,0.),
        children = [
            star
            View.BoxView(color = backgroundColor, 
                widthRequest = boxViewWidth,
                isVisible = (if percentage > 0. then true else false),
                horizontalOptions = LayoutOptions.End)
            ])

Aka Star Factory的函数被另一个函数调用,鉴于评级:

let ratingControl (rating:decimal) =
    let fullNumber = Math.Ceiling(rating)
    let fraction = (rating - Math.Truncate(rating))
    View.StackLayout(orientation = StackOrientation.Horizontal,
        children = [
            for i in 1m .. fullNumber -> if i = fullNumber then ratingStar (float fraction) else ratingStar 1.
        ])

Now we have all of our building blocks together for the description, but we still have the image with rounded corners left. A quick look at the ImageView from Xamarin Forms tells us: “No rounded edges.” But when putting the image in a Frame, we can create the rounded edges effect. So let’s create a function that gives us an image with round corners:

let roundedCornerImage imagePath =
    View.Frame(cornerRadius = cornerRadius,
        padding = 0.,
        isClippedToBounds = true,
        hasShadow = true,
        content = View.Image(
            source = imagePath,
            aspect = Aspect.AspectFill)
    )

这些部件全部现在使让我们组装它们,以便我们通过简短描述使用圆角覆盖的图像:

let cityDescriptionFrame city dispatch =
    View.StackLayout(
        margin = Thickness(16.,0.,16.,0.),
        children = [
            (roundedCornerImage city.Image |> fun(img) -> img.HeightRequest 320.)
            View.Frame(
                heightRequest = 70.,
                margin = Thickness(24.,-64.,24.,0.),
                padding = Thickness(20.,12.,16.,12.),
                backgroundColor = Color.White,
                cornerRadius = cornerRadius,
                content = View.Grid(
                    rowdefs=["auto"; "auto" ],
                    coldefs=["*";"auto"],
                    children=[
                        (titleAndDescription city.Name titleFontSize city.Country)
                        (favoriteIcon city dispatch).GridColumn(2)
                        (ratingControl city.Rating).GridRow(1).GridColumnSpan(2)
                        ]
                ),
                hasShadow = true)
        ])

同样,我们可以实施“要做的事情”部分。伟大的事情是我们可以重复使用我们已经创建的大量组件。然后,我们可以将所有零件放在视图方法中,将我们带有以下UI:

appscreenshot.

您可以找到整个样本GitHub..

Side notes: No, we are not required to have all the code in one file. But since this is a one-pager application, I left it together, so it is easier to navigate the code in a browser. Further note that the CarouselView did not work correctly when I was working with the view. I hope I will be soon able to get it working and have a sample which will allow switching between cities as intended by design.

结论

将原子设计模式应用于UI,真正让您的应用更容易维护和创建。鉴于允许在代码中编写UI的很棒,在没有多少样品板代码的情况下创建自定义和一致的UI相对简单。进一步的神奇提供了一个现场更新功能,允许您在调试会话期间实现代码。不仅执行UI适应,还执行逻辑。您可以阅读更多信息Live Update功能在官方网站上。

在最近的2019年期间,它似乎用代码写入UI。与Apple等公司一起在Swift UI工作。如果你是一个顽固的C#爱人,你应该看看ryan戴维斯的帖子用C#写UIS for Xamarin。

您可以了解有关原子设计模式的更多信息布拉德霜网网站.

更新: