View - Axvser/MinimalisticWPF GitHub Wiki

假设我们有一个 MyControl : UIElement 作为 View

Ⅰ 主题切换

  • 如果你希望属性可以在主题发生切换时自动加载过渡效果……
    [Theme(nameof(Background), typeof(Dark), ["#1e1e1e"])]
    [Theme(nameof(Background), typeof(Light), ["White"])]
    public partial class MyControl : UserControl
    {

    }
  • Theme特性的第三个参数

    • 是一个对象数组
    • 用于构造值,例如 ["White"] 声明了 Background 在 Light 主题下应为 白色
    • TransitionSystem中6种支持自动过渡的目标均可构造,因此类似于 " 玻璃圆角 " 也可作为主题
    • Brush具备一些特殊优化
      • 可以传入 App.Xaml 中定义的静态资源的 Key ( 也就是说可以是任意画刷 ,而不局限于纯色画刷 )
      • 可以传入 public static w-r 字段/属性的名称
  • 依据需求,我们可以令程序 跟随系统主题 or 手动设定主题,只需在 App.cs 中执行下述语句

    • FollowSystem 在系统主题发生切换时,启动程序的主题切换,若读取系统主题失败,使用指定的默认值
    • StartWith 手动指定初始主题,并在需要时手动切换主题
    public partial class App : Application
    {
        public App()
        {
            DynamicTheme.FollowSystem(typeof(Dark));
            DynamicTheme.StartWith(typeof(Dark));
        }
    }
  • 手动切换主题
            var param = new TransitionParams() // 自定义参数
            {
                Duration = 0.5,
                FrameRate = 30,
            };

            TransitionParams.Theme.Duration = 0.2; // 全局共享参数 ( 默认参数 )

            DynamicTheme.Apply(typeof(Dark));
            DynamicTheme.Apply(typeof(Dark), param);
  • 自定义主题
    • IThemeAttribute接口必须实现
    • 构造器具备唯一参数 params object?[]
using MinimalisticWPF.StructuralDesign.Theme;

namespace DynamicTest_MinWpf
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public sealed class GlassAttribute(params object?[] param) : Attribute, IThemeAttribute
    {
        public object?[] Parameters { get; set; } = param;
    }
}
  • 最后,关于主题过渡的参数,需要注意一些问题
    • 若跟随系统主题,Duration不建议超过0.3,系统事件不应耗时过长
    • 不建议界面拥有超大量元素,如果一定有,应选取下述方案
        1. 降低 FrameRate ,极限情况下 ,直接设定为 0 ,仅加载1帧
        1. 切换Theme要求重启程序

Ⅱ 悬停交互

  • 如果你希望鼠标悬停时,属性可以加载过渡效果…… ( 悬停不同于主题,它的效果需要在Style内设定,参照 Ⅲ )
    [Hover([nameof(Background), nameof(Foreground)])]
    public partial class MyControl : UserControl
    {

    }

Ⅲ Style定义

总的来说, Ⅰ 和 Ⅱ 为主题过渡、悬停过渡功能快速生成了一个框架结构 ,能够设定默认值 ,而 Style 用于修改默认值以实现效果差异化

  • 有关于 Ⅰ 和 Ⅱ 所提供的功能扩展,都是基于 生成代理属性 + 生成效果控制属性 ,若要构建灵活且便于维护的项目 ,Style是必须的

    • 我们以Background属性为例
      • 如果仅使用Hover特性,没有使用Theme特性,则生成HoveredBackgroundNoHoveredBackground属性,修改他们即可直接改变悬停时加载的过渡
      • 如果同时拥有Hover与Theme特性,此处假设有Dark和Light主题
        • DarkHoveredBackground & DarkNoHoveredBackground 用于控制Dark主题下的Background属性
        • LightHoveredBackground & LightNoHoveredBackground 用于控制Light主题下的Background属性
  • 这比起传统实现,有哪些特点呢,不都是用Style吗 ?

      1. 无Trigger模式,减少XAML中的嵌套
      1. 无Animation模式,所有过渡均由库自动计算并加载,减少XAML中的动画定义
  • 有一些小技巧,我希望能派上用场

      1. 利用C#类继承,从UserControl派生一些Base类,然后将这些扩展用在Base类,这样,可以更少地使用特性
      1. 利用Style继承,我知道类似 LightHoveredBackground 这样的命名很长 ,不是所有人都喜欢 ,即便它传达的意思无比清晰

Ⅳ 循环帧

  • 如果你希望像一些游戏引擎所做的事情那样,以一定的帧率刷新……
    • CanMonoBehaviour 属性用于控制当前是否可以执行刷新
    • Awake 发生在一切开始之前
    • Start 发生在帧循环开始前
    • Update 发生在每一帧
    • LateUpdate 发生在每一帧执行完后
    • ExitMonoBehaviour 发生在帧循环被终结时
    [MonoBehaviour(16.6)]
    // 16.6ms 相当于 60FPS
    public partial class MyControl : UserControl
    {
        partial void Awake()
        {
            CanMonoBehaviour = false; // 默认是true,若你希望不要立刻开始帧循环,可以在这个阶段关闭
        }

        partial void Start()
        {
            throw new NotImplementedException();
        }

        partial void Update()
        {
            throw new NotImplementedException();
        }

        partial void LateUpdate()
        {
            throw new NotImplementedException();
        }

        partial void ExitMonoBehaviour()
        {
            throw new NotImplementedException();
        }
    }