告别繁琐 XAML!从 XchyUI 看懂函数式组合编程的真正威力

0 阅读6分钟

一文读懂函数式组合编程:从 XchyUI 实战源码,看透现代 UI 开发新范式

在桌面端开发中,WPF 凭借强大的自定义能力占据主流,但繁琐的 XAML、复杂的数据绑定、高昂的学习成本也让开发者备受困扰。近年来,函数式组合编程凭借简洁、高效、统一的优势,成为 Jetpack Compose、SwiftUI、Flutter 等新一代 UI 框架的核心思想,也成为 C# 桌面开发的革新方向。

本文将从概念、优势、源码实战、统一 API四个维度,结合 XchyUI 的 Switch 开关IconButton 图标按钮 真实源码,带你彻底理解:函数式组合编程到底是什么、强在哪里


一、什么是函数式组合编程?

函数式组合编程,是一种以函数为基础单元、以组合为核心思想、以状态驱动 UI的声明式开发范式。

简单来说:

  • 一个函数 = 一个 UI 组件
  • 复杂界面 = 多个函数嵌套组合
  • UI 不再手动创建,而是状态自动渲染
  • 全程纯代码描述 “界面长什么样”,而非命令式操作控件

它的核心特征:纯代码构建 + 函数嵌套 + 链式调用 + 状态驱动 + 无副作用


二、函数式组合编程的 5 大核心优势

1. 纯 C# 一体化开发,告别 XAML 与逻辑割裂

传统 WPF 必须使用 XAML 布局 + C# 逻辑 双语言模式,而函数式 UI:布局、样式、动画、交互全部使用 C# 实现结构更内聚,维护更简单。

2. 积木式组件组合,复用成本极低

所有界面都由原子函数(Box、Row、Column、Icon、Text等等)拼接而成,小函数拼大组件,一次封装,随处使用

3. 状态驱动自动刷新,告别繁琐绑定

状态改变 → UI 自动刷新无需实现 INotifyPropertyChanged、无需依赖属性、无需处理绑定失效。

4. 大一统链式 API,所有组件样式写法完全统一

任何 UI 元素:容器、按钮、图标、文字等等都可以用同一套 API 设置:背景、边框、圆角、阴影、大小、边距、点击事件等。

三、XchyUI 核心能力:所有组件通用链式 API

在 XchyUI 中,所有 UI 组件都基于 XViewBuilder 构建,因此:

任何组件都能链式调用以下样式:

csharp

运行

.Background()      // 背景色
.Border()          // 边框
.Radius()          // 圆角
.Shadow()          // 阴影
.Size()            // 宽高
.Padding()         // 内边距
.Margin()          // 外边距
.Alpha()           // 透明度
.Click()           // 点击事件
... 还有很多常用的方法

一套 API,全框架通用这是传统 WPF 无法比拟的统一体验。


四、实战源码解析 1:Switch 开关组件(函数式典范)

我们直接看源码,逐点理解函数式组合如何实现开关。

Switch 功能

  • 带动画滑动效果
  • 状态选中 / 未选中
  • 支持禁用状态
  • 统一样式:背景、圆角、阴影、边框

Switch 完整源码

public static XViewBuilder Switch(bool isSelected, XFunction<bool>? onSelected = null, bool disable = false)
{
    // 1. 响应式状态:选中状态
    var selectedState = StateValueOf(isSelected);
    // 2. 动画状态
    var visibleState = StateValueOf(false);
    // 动画值
    var animiateValue = AnimateFloatOf(visibleState);
    var dist = 32; // 滑动的距离

    // 3. 函数嵌套构建UI:Box外壳 + Space圆形滑块
    return Box(() =>
    {
        Space(30)
        .Circle() // 绘制圆形
        // 4. 动画绑定:滑块自动滑动
        .Binding(animiateValue, (builder, value) =>
        {
            // 计算元素的margin
            var marginX = selectedState.Value ? (float)dist : 0f;
            if (visibleState.Value)
            {
                marginX = selectedState.Value ? value * dist : (1 - value) * dist;
            }
            builder.Margin(left: (int)marginX);
        }, needLayout: true) // 改变子元素布局参数需要触发布局
        // 5. 状态绑定:滑块颜色自动更新
        .Binding(selectedState, (builder, isSelected) =>
        {
            builder.Background(xTheme.Light.BlankFill);
        })
        .Shadow(xTheme.Shadows.MinCard); // 阴影(链式API)
    })
    .ContentAlignment(XAlignment.LeftCenter)
    // 6. 状态绑定:开关背景色自动切换
    .Binding(selectedState, (builder, isSelected)
        => builder.Background(isSelected ? xTheme.Colors.Primary : xTheme.Colors.BaseBorder))
    .Radius(33)          // 圆角(链式API)
    .Size(66, 33)        // 大小(链式API)
    .Padding(horizontal:2)
    .EnableEvent(!disable) // 是否禁用
    .Alpha(disable ? xTheme.Colors.DisabledAlpha : 1) // 禁用下设置透明度,也可以封装成一个Disable()扩展方法里面调用EnableEvent和Alpha就行
    // 7. 点击交互:只改状态,UI自动刷新
    .Click((builder, info) =>
    {
        selectedState.Value = !selectedState.Value; // 触发是否选中
        onSelected?.Invoke(selectedState.Value); //选中函数透出
        visibleState.Value = true; // 开始动画
    });
}

Switch 函数式设计亮点

  1. 无 XAML、无模板、无继承,纯函数实现
  2. 状态驱动:修改状态自动刷新 UI
  3. 动画自动播放:无需手动管理
  4. 链式样式 API:圆角、背景、阴影统一调用
  5. 组件结构清晰:Box 包含 Circle,一目了然

五、实战源码解析 2:IconButton 图标按钮(业务级组件)

IconButton 是最常用的业务组件,支持:图标 + 文字、横竖布局、Loading 状态。

IconButton 完整源码

public static XViewBuilder IconButton(int resId, string text, int? iconSize = null, int? fontSize = null, XColor? tint = null, XColor? fontColor = null, bool isVerticel = false, XState<bool>? loadingState = null)
{
    iconSize = iconSize ?? 20;
    fontSize = fontSize ?? xTheme.Sizes.Body;
    fontColor = fontColor ?? xTheme.Colors.RegularText;
    tint = tint ?? fontColor;

    // 1. 函数式UI结构
    XFunction function = () =>
    {
        if (loadingState != null)
        {
            // 2. 绑定加载状态
            Box(loadingState, loading =>
            {
                // 如果是加载状态显示颜色环形进度条,这个自带动画
                if (loading)
                {
                    ColorLoading(fontColor.Value, iconSize.Value, 2);
                }
                else
                {
                    // 否则显示一个图标
                    Icon(resId).Size(iconSize.Value).Color(tint.Value);
                }
            });
        }
        else
        {
            Icon(resId).Size(iconSize.Value).Color(tint.Value);
        }

        // 3. 文字组件
        Text(text)
            .FontSize(fontSize.Value)
            .FontColor(fontColor.Value);
    };

    // 4. 自动适配横向/纵向布局
    var builder = isVerticel ? Column(function) : Row(function);

    // 5. 统一链式样式API(所有组件通用)
    return builder
        .Background(xTheme.Colors.BlankFill)
        .Border(xTheme.Colors.BaseBorder, 1.5f)
        .Radius(xTheme.Radius.Middle)
        .HorizontalAlignment(XHorizontalAlignment.Center)
        .VerticalAlignment(XVerticalAlignment.Center)
        .Space(10) // 子元素之间的间距
        .Padding(horizontal: xTheme.Sizes.Space20 - 10, vertical: xTheme.Sizes.Space12);
}

// 加载动画组件(纯函数组合)
public static XViewBuilder ColorLoading(XColor color, int size, int borderSize)
{
    return Space(size)
        .Circle()
        .OnDraw(builder => { 
            // 添加自定义绘图,直接透出SKCanvas 中间没有任何链路,直连SKCanvas绘图,你想怎么画就怎么画
            var canvas = (SKCanvas)RenderImp.GetCanvas();
            var paint = PaintCache.GetBackground(builder.View.Style.Background);
            paint.Color = color.ToSKColor();
            paint.IsStroke = true;
            paint.StrokeCap = SKStrokeCap.Round;
            paint.StrokeWidth = borderSize.AsPx();
            var rect = builder.View.RenderRect;
            var skRect = rect.ToSKRect();
            // 设定渐变
            paint.Shader = DrawConverter.ToShader(rect, new XBrush()
            {
                StartColor = color,
                EndColor = color.Copy(0f),
                Direction = XGradientDirection.Round
            });
            // 绘制扇形
            canvas.DrawArc(skRect, 5, 350, false, paint);
            }).CircleProgress(); // 开始圆形360度旋转动画
}

使用实例

Column(() =>
{
    Switch(true);
    IconButton(SvgRes.Goods, "商品");
    var loadingState = StateValueOf(false);
    IconButton(SvgRes.Search, "查询", loadingState: loadingState).Click(()=>
    {
        loadingState.Value = !loadingState.Value;
    });
})
 .Size(500)
 .VerticalAlignment(XVerticalAlignment.Center)
 .Border(XColors.Red, 1, XDashType.Dash)
 .Space(10);

下面的是图 switch_iconbutton.png

点击查询后 查询图标自动变成加载动画

switch_loading.png

IconButton 函数式亮点

  1. 状态自动切换 Icon/Loading,无需手动改 UI
  2. Row/Column 函数一键切换横竖布局
  3. 所有样式均使用统一链式 API
  4. 一个函数就是一个组件,可在任何地方调用
  5. 无模板、无样式重写、无控件继承

六、函数式组合 UI vs WPF 直观对比

表格

维度函数式组合(XchyUI)传统 WPF
开发方式纯 C# 函数声明XAML + C# 分离
样式 API所有组件统一链式调用各控件独立属性,学习成本高
UI 刷新状态改变自动刷新绑定 + 通知,易失效
组件复用封装函数即可复杂控件模板 + 样式
代码量少、简洁、直观冗余代码多
Bug 概率低(无副作用)高(绑定 / 内存 / 样式)

七、总结

函数式组合编程,是新一代 UI 开发的主流范式,它用最简洁的方式解决了传统框架的核心痛点。

通过 XchyUI 的 SwitchIconButton 两大组件源码可以看出:

函数式组合的核心价值:

  1. 函数即组件,像搭积木一样构建 UI
  2. 状态驱动自动刷新,告别繁琐绑定
  3. 所有组件统一链式 API:背景、边框、圆角、阴影完全通用
  4. 纯 C# 开发,无 XAML,结构更清晰
  5. 低代码、低耦合、低 Bug、高复用

如果你厌倦了传统框架的沉重与繁琐,渴望一套 ** 轻量、现代、高效、纯 C#** 的桌面 UI 开发方案,函数式组合编程(如 XchyUI)绝对是未来的主流方向。