当前位置: 首页 > news >正文

最简单的代码,让 WPF 支持响应式布局

响应式布局在各种现代的 UI 框架中不是什么新鲜的概念,基本都是内置支持。然而在古老的 WPF 框架中却并没有原生支持,后来虽然通过 Blend 自带的 Interactions 库实现了响应式布局,但生成的代码量太大了,而且需要引入额外的库。

如果只是希望临时局部地方使用响应式布局,那么其实可以直接使用 WPF 内置的绑定机制来完成响应式布局。本文介绍如何使用。


思路是在控件尺寸发生变更的时候更新控件的样式。而能容易实现这个的只有 TriggerSetter 那一套。直接在控件上使用的 Trigger 只能使用 EventTrigger,因此我们需要编写能写更多种类 TriggerStyle

<Style x:Key="Style.Foo.WalterlvDemo">
    <Setter Property="Grid.Row" Value="0" />
    <Setter Property="Grid.Column" Value="0" />
    <Style.Triggers>
        <DataTrigger Value="True"
                     Binding="{Binding ActualHeight, ElementName=DemoWindow,
                              Converter={StaticResource GreaterOrEqualsConverter},
                              ConverterParameter=640}">
            <Setter Property="Grid.Row" Value="1" />
            <Setter Property="Grid.Column" Value="1" />
        </DataTrigger>
    </Style.Triggers>
</Style>

定义了一个样式,默认情况下,行列是 (0, 0),当窗口宽度大于或等于 640 之后,行列换到 (1, 1)。

这里我们需要一个大于或等于,以及小于的转换器。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace Cvte.EasiNote.UI.Styles.Converters
{
    public class GreaterOrEqualsConverter : IValueConverter
    {
        public double Than { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double d)
            {
                return d >= Than;
            }
            else if (value is float f)
            {
                return f >= Than;
            }
            else if (value is ulong ul)
            {
                return ul >= Than;
            }
            else if (value is long l)
            {
                return l >= Than;
            }
            else if (value is uint ui)
            {
                return ui >= Than;
            }
            else if (value is int i)
            {
                return i >= Than;
            }
            else if (value is ushort us)
            {
                return us >= Than;
            }
            else if (value is short s)
            {
                return s >= Than;
            }
            else
            {
                return false;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    public class LessConverter : IValueConverter
    {
        public double Than { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double d)
            {
                return d < Than;
            }
            else if (value is float f)
            {
                return f < Than;
            }
            else if (value is ulong ul)
            {
                return ul < Than;
            }
            else if (value is long l)
            {
                return l < Than;
            }
            else if (value is uint ui)
            {
                return ui < Than;
            }
            else if (value is int i)
            {
                return i < Than;
            }
            else if (value is ushort us)
            {
                return us < Than;
            }
            else if (value is short s)
            {
                return s < Than;
            }
            else
            {
                return false;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}

如果你本身是写的基础控件的样式,那么绑定当然就跟本文一开始说的写法非常类似了。

如果你需要写的是一般控件,可以考虑直接在控件里写 <Framework.Style /> 把样式内联进去。

如果你写的是 DataTemplate,也一样是使用 DataTrigger 绑定。

你也可以不绑定到窗口上,而绑定到控件本身上,使用 TemplatedParent 作为绑定的源即可。

<DataTemplate>
    <DataTemplate.Resources>
        <local:LessConverter x:Key="LessThan60" Than="60" />
    </DataTemplate.Resources>
    <Grid>
        <Image x:Name="Icon" />
        <Rectangle x:Name="Mask" Fill="Red" />
    </Grid>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource LessThan60}}" Value="True">
            <Setter TargetName="Icon" Property="Grid.ColumnSpan" Value="3" />
            <Setter TargetName="Mask" Property="Visibility" Value="Collapsed" />
        </DataTrigger>
</DataTemplate>

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 当无边框窗口被子窗口遮挡导致难以调节窗口大小时,可通过处理 NCHITTEST 消息重新支持调节窗口大小
  • 如何给 GitHub Pages 配置多个域名?
  • 通过子类化窗口(SubClass)来为现有的某个窗口添加新的窗口处理程序(或者叫钩子,Hook)
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • git 乱改你的换行符?一句话设置让 git 不再碰你某个文件的换行符
  • Linux Shell 中需要转义的字符
  • Unity3D 入门:Unity Editor 编辑器常用快捷键
  • Unity3D 入门:Unity 项目版本管理建议使用的 .gitignore 忽略文件和 .gitattributes 文件(2020年4月更新)
  • Unity3D 入门:让 C# 脚本公开可在 Unity 编辑器中设置的属性
  • Unity3D 入门:如何管理 Unity 项目中的 NuGet 包?使用第三方 NuGet 包管理器——NuGetForUnity
  • Unity3D 入门:如何在脚本中找到游戏对象的父子级 祖孙级对象和它们的组件
  • Unity3D 入门:如何制作天空效果?天空盒的使用
  • Unity3D 入门:使用 Visual Studio 开发 Unity C# 脚本,说说根目录的那些 sln 和 csproj 文件
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • E-HPC支持多队列管理和自动伸缩
  • JavaScript对象详解
  • Java编程基础24——递归练习
  • JAVA多线程机制解析-volatilesynchronized
  • JSDuck 与 AngularJS 融合技巧
  • Meteor的表单提交:Form
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • SpiderData 2019年2月25日 DApp数据排行榜
  • SQLServer之创建数据库快照
  • 阿里研究院入选中国企业智库系统影响力榜
  • 实习面试笔记
  • 使用API自动生成工具优化前端工作流
  • 手写一个CommonJS打包工具(一)
  • 想写好前端,先练好内功
  • 项目管理碎碎念系列之一:干系人管理
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • Semaphore
  • 阿里云移动端播放器高级功能介绍
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • #14vue3生成表单并跳转到外部地址的方式
  • #Linux(Source Insight安装及工程建立)
  • #微信小程序(布局、渲染层基础知识)
  • $().each和$.each的区别
  • (1)(1.13) SiK无线电高级配置(六)
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (学习日记)2024.01.09
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • .net CHARTING图表控件下载地址
  • .net core webapi 大文件上传到wwwroot文件夹
  • .Net mvc总结
  • .NET企业级应用架构设计系列之开场白
  • .NET中GET与SET的用法
  • .NET中两种OCR方式对比
  • ;号自动换行
  • [20161214]如何确定dbid.txt
  • [2023年]-hadoop面试真题(一)