Avalonia开发实践(二)——开发带边框的Grid
一、开发背景
在实际开发工作中,常常会用到Grid进行布局。为了美观考虑,会给每个格子加上边框,如下图:

原生的Grid虽然有ShowGridLines属性可以控制显示格子之间的线,但线的样式不能定义,可以说此功能非常鸡肋。接下来我们自己动手实现Grid中的网格线!
二、设计思路
虽然Grid自带的格子线非常拉胯,但它的实现方式为我们提供了宝贵的思路。
首先,Grid继承自Panel,而在Panel中,Render方法已经密封了,所以想在Grid中利用Render方法进行边框绘制这条路就走不通了。
官方的做法是,定义一个GridLinesRenderer,继承自Control。将GridLinesRenderer添加到Grid的VisualChildren中,在GridLinesRenderer的Render方法中实现对Grid的边框绘制。
三、实现过程
1、定义边框绘制类
/// <summary>
/// GridLineStyle
/// </summary>
internal class GridLinesRenderer : Control
{private Pen _borderPen;private Size _lastArrangeSize;public override void Render(DrawingContext context){base.Render(context);var grid = this.GetVisualParent<Grid>();if (grid == null || !grid.ShowGridLines)return;if (_borderPen == null){_borderPen = new Pen(grid.GridLineBrush, grid.GridLineWidth, lineCap: PenLineCap.Round);}else{_borderPen.Brush = grid.GridLineBrush;_borderPen.Thickness = grid.GridLineWidth;}// 获取行高、列宽数据var rowHeightArr = new double[Math.Max(grid.RowDefinitions.Count, 1)];var colWidthArr = new double[Math.Max(grid.ColumnDefinitions.Count, 1)];if (grid.RowDefinitions.Count == 0){rowHeightArr[0] = _lastArrangeSize.Height;}else{for (int i = 0; i < grid.RowDefinitions.Count; i++){rowHeightArr[i] = grid.RowDefinitions[i].ActualHeight;}}if (grid.ColumnDefinitions.Count == 0){colWidthArr[0] = _lastArrangeSize.Width;}else{for (int i = 0; i < grid.ColumnDefinitions.Count; i++){colWidthArr[i] = grid.ColumnDefinitions[i].ActualWidth;}}// 绘制内边框var _lastOffsetX = 0d;var _currentOffsetX = colWidthArr[0];for (int i = 1; i < colWidthArr.Length; ++i){if (_lastOffsetX != _currentOffsetX){DrawGridLine(context,_currentOffsetX, 0.0,_currentOffsetX, _lastArrangeSize.Height);_lastOffsetX = _currentOffsetX;}_currentOffsetX += colWidthArr[i];}var _lastOffsetY = 0d;var _currentOffsetY = rowHeightArr[0];for (int i = 1; i < rowHeightArr.Length; ++i){if (_lastOffsetY != _currentOffsetY){DrawGridLine(context,0.0, _currentOffsetY,_lastArrangeSize.Width, _currentOffsetY);}_currentOffsetY += rowHeightArr[i];}// 绘制外边框double radiusX = grid.CornerRadius;double radiusY = grid.CornerRadius;Rect rect = new Rect(_lastArrangeSize).Deflate(grid.GridLineWidth / 2);if (radiusX == 0.0 && radiusY == 0.0){var rectangleGeometry = new RectangleGeometry(rect);context.DrawGeometry(null, _borderPen, rectangleGeometry);}else{StreamGeometry streamGeometry = new StreamGeometry();using (StreamGeometryContext streamGeometryContext = streamGeometry.Open()){GeometryHelper.DrawRoundedCornersRectangle(streamGeometryContext, rect, radiusX, radiusY);}context.DrawGeometry(null, _borderPen, streamGeometry);}}private void DrawGridLine(DrawingContext drawingContext,double startX,double startY,double endX,double endY){var start = new Point(startX, startY);var end = new Point(endX, endY);drawingContext.DrawGeometry(null, _borderPen, new RectangleGeometry(new Rect(start, end).Deflate(0.5)));}internal void UpdateRenderBounds(Size arrangeSize){_lastArrangeSize = arrangeSize;InvalidateVisual();}
}
2、自定义Grid,重写ArrangeOverride方法,每次布局变化时触发边框重绘方法
/// <summary>
/// Grid
/// </summary>
public class Grid : Avalonia.Controls.Grid
{private GridLinesRenderer _gridLinesRenderer;/// <summary>/// Defines the <see cref="ShowGridLines"/> property./// </summary>public new bool ShowGridLines{get { return (bool)GetValue(ShowGridLinesProperty); }set { SetValue(ShowGridLinesProperty, value); }}/// <summary>/// Defines the <see cref="ShowGridLinesProperty"/> property./// </summary>public static readonly new StyledProperty<bool> ShowGridLinesProperty = AvaloniaProperty.Register<Grid, bool>("ShowGridLines");/// <summary>/// Defines the <see cref="GridLineBrush"/> property./// </summary>public IBrush GridLineBrush{get { return (IBrush)GetValue(GridLineBrushProperty); }set { SetValue(GridLineBrushProperty, value); }}/// <summary>/// Defines the <see cref="GridLineBrushProperty"/> property./// </summary>public static readonly StyledProperty<IBrush> GridLineBrushProperty = AvaloniaProperty.Register<Grid, IBrush>("GridLineBrush");/// <summary>/// Defines the <see cref="GridLineWidth"/> property./// </summary>public double GridLineWidth{get { return (double)GetValue(GridLineWidthProperty); }set { SetValue(GridLineWidthProperty, value); }}/// <summary>/// Defines the <see cref="GridLineWidthProperty"/> property./// </summary>public static readonly StyledProperty<double> GridLineWidthProperty = AvaloniaProperty.Register<Grid, double>("GridLineWidth", 1d);/// <summary>/// Defines the <see cref="CornerRadius"/> property./// </summary>public float CornerRadius{get { return (float)GetValue(CornerRadiusProperty); }set { SetValue(CornerRadiusProperty, value); }}/// <summary>/// Defines the <see cref="CornerRadiusProperty"/> property./// </summary>public static readonly StyledProperty<float> CornerRadiusProperty = AvaloniaProperty.Register<Grid, float>("CornerRadius");private GridLinesRenderer EnsureGridLinesRenderer(){if (ShowGridLines && _gridLinesRenderer == null){_gridLinesRenderer = new GridLinesRenderer();VisualChildren.Add(_gridLinesRenderer);}if (!ShowGridLines && _gridLinesRenderer != null){VisualChildren.Remove(_gridLinesRenderer);_gridLinesRenderer = null;}return _gridLinesRenderer;}/// <summary>/// ArrangeOverride/// </summary>/// <param name="arrangeSize"></param>/// <returns></returns>protected override Size ArrangeOverride(Size arrangeSize){var size = base.ArrangeOverride(arrangeSize);var gridLinesRenderer = EnsureGridLinesRenderer();gridLinesRenderer?.UpdateRenderBounds(arrangeSize);return size;}
}
这样,一个具有边框绘制功能的Grid就完成了。
四、进阶思考
有时Grid并不是所有单元格都用得上的,可能还涉及跨行跨列的情况,这时就需要根据每个子元素的空间占据大小来绘制边框了。我们可以记录Grid每个单元格的布局参数,再遍历每个子元素,用合并单元格的思路来绘制内部边框。
相关文章:
Avalonia开发实践(二)——开发带边框的Grid
一、开发背景 在实际开发工作中,常常会用到Grid进行布局。为了美观考虑,会给每个格子加上边框,如下图: 原生的Grid虽然有ShowGridLines属性可以控制显示格子之间的线,但线的样式不能定义,可以说此功能非常…...
Java泛型的定义与运用
泛型 泛型的作用从使用层面上来说是统一数据类型,防止将来的数据转换异常。从定义层面上来说,定义带泛型的类,方法等,将来使用的时候给泛型确定什么类型,泛型就会变成什么类型,凡是涉及到泛型的都会变成确…...
Java如何自定义注解及在SpringBoot中的应用
注解 注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说…...
微软 Edge 浏览器全解析
微软 Edge 是微软推出的一个现代化浏览器,继承了 Internet Explorer(IE)的部分功能,但在速度、安全性和兼容性方面做出了很大改进。下面是对微软 Edge 浏览器的详细解析,包括其特点、安装、配置和常见问题的解答。 微软 Edge 浏览器的特点 基于 Chromium 内核 Edge 浏览…...
C++ 八股(1)
C语言中strcpy为什么不安全?如何解决? 主要原因是缺乏对输入长度的边界检查,容易导致缓冲区溢出漏洞。 解决:可以使用strncpy函数替代,或者在程序最顶端加入代码段 #define _CRT_SECURE_NO_WARNINGS 缓冲区溢出 …...
超高精电容传感器PCAP01调试+LABVIEW数据可视化调试手记
PCAP01超高精电容传感芯片STM32LabView可视化 文章目录 PCAP01超高精电容传感芯片STM32LabView可视化一、PCAP01介绍1.1、PCAP01引脚定义1.2、电容测量1.3、温度测量1.4、PCAP典型测试电路 二、PCAP01的STM32驱动2.1、SPI协议配置2.2、PCAP01浮空电容测量内部温度测量操作流程 …...
5.更多
发现一个项目与 MkDocs 类似的项目 PyMdown 拓展文档 ,等待探索。 1.排版模仿 以下网站使用 MkDocs 构建 Material for MkDocs 的美化 - Charles Les Notebook (charleschile.com) Documentation - Home Assistant (home-assistant.io) Godot Docs – master bra…...
ConditionalOnJndi注解使用介绍、应用场景以及示例代码
概述 ConditionalOnJndi 是 Spring Framework 中的一个条件注解,用于在特定的 JNDI (Java Naming and Directory Interface) 环境条件下决定是否创建一个 bean 或配置一个 bean。JNDI 是 Java EE 规范中定义的一种用于访问命名和目录服务的 API,它允许 …...
Spring Cloud 引入
1.单体架构: 定义:所有的功能实现都打包成一个项目 带来的后果: ①后端服务器的压力越来越大,负载越来越高,甚至出现无法访问的情况 ②业务越来越复杂,为了满足用户的需求,单体应用也会越来越…...
自定义波形图View,LayoutInflater动态加载控件保存为本地图片
效果图: 页面布局: <?xml version"1.0" encoding"utf-8"?><LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:la…...
每日一道算法题 求最小公倍数
题目 求最小公倍数_牛客题霸_牛客网 (nowcoder.com) Python 辗转相除法 dividend,divisormap(int,input().split()) #被除数,除数 # remainder0 余数 # 最小公倍数 def lcm(dividend,divisor):# 最大公约数def gcd(dividend,divisor):if 0divisor:return divid…...
【OCC学习18】三维几何对象工具包:TKG3d
【OCC学习18】三维几何对象工具包:TKG3d loveoobaby 已于 2022-08-26 10:10:32 修改 阅读量1.2k 收藏 10 点赞数 1 分类专栏: OpenCascade学习笔记 文章标签: 学习 版权 OpenCascade学习笔记 专栏收录该内容 24 篇文章60 订阅 订阅专栏…...
【Unix】SunOS/Oracle Solaris系统介绍
一.SunOS系统介绍 SunOS 是由 Sun Microsystems 开发的 Unix 操作系统。它最初是为 Sun 的 SPARC 架构计算机设计的,后来也支持了 Intel x86 架构。SunOS 是基于 UNIX System V 4.1 版本,并且随着时间的发展,SunOS 经历了多个版本迭代&#…...
氛围感视频素材高级感的去哪里找啊?带氛围感的素材网站库分享
亲爱的创作者们,大家好!今天我们来聊聊视频创作中至关重要的一点——氛围感。一个好的视频,不仅要有视觉冲击力,还要能够触动观众的情感。那我们应该去哪里寻找这些充满氛围感且高级的视频素材呢?别急,我这…...
基于Java的学生选课系统
第1章 系统概述 1.1概述 背景:随着计算机网络技术的发展,Web 数据库技术已成为应用最为广泛的网站架构基础技术。学生选课系统作为教育单位不可缺少的部分,其内容对于学校的决策者和管理者至关重要。传统的人工管理方式存在效率低、保密性差等…...
802.11漫游流程简单解析与笔记_Part2_05_wpa_supplicant如何通过nl80211控制内核开始关联
最近在进行和802.11漫游有关的工作,需要对wpa_supplicant认证流程和漫游过程有更多的了解,所以通过阅读论文等方式,记录整理漫游相关知识。Part1将记录802.11漫游的基本流程、802.11R的基本流程、与认证和漫游都有关的三层秘钥基础。Part1将包…...
STM32的 DMA(直接存储器访问) 详解
STM32的DMA(Direct Memory Access,直接存储器存取)是一种在单片机中用于高效实现数据传输的技术。它允许外设设备直接访问RAM,不需要CPU的干预,从而释放CPU资源,提高CPU工作效率,本文基于STM32F…...
14-65 剑和诗人39 - 打造你自己的 Devin
绝密 Devin 架构 更具体地说,构建您自己的 AI 代理。 Devin 使用 GPT-4 ,而人们已经开始用 Claude-3-Opus 构建替代方案 Devin 的 UI 体验更好。 例如,它甚至看不到浏览器,但它确实存在于用户面前 此外,你可以随时与它“交谈”,就像与人交谈一样,它会在后…...
JavaScript 把CSDN博客内容存成PDF
F12 - 控制台 -命令行 输入执行:允许粘贴输入执行代码: (function () {use strict;var articleBox $("div.article_content");articleBox.removeAttr("style");var head_str "";var foot_str "";var older…...
uniapp——银行卡号脱敏
样式 代码 {{bankNumber.replace(/(\d{4})(?\d)/g, "●●●● ").replace(/(\d{2})(?\d{2}$)/, " $1")}} 将银行卡号按照每四位一组的方式进行处理,前面的变成 剩下的正常显示...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
