C#开发医学影像胶片打印系统(一):万能花式布局的实现思路
本篇文章将介绍开发医学影像胶片打印系统(printscu模式)遇到不规则排版时的一种思路,
一般来讲,医院打印胶片时都是整张胶片打印,但有时需要将多个病人或一个病人的多个检查打印在同一张胶片上,
这时候就需要不规则排版来满足打印需求,使胶片利用率最大化。
国际惯例,先看效果:
常规打印业务流程:
1、编辑布局模板
2、载入布局模板
3、选择标记模板
4、下载与选择影像
5、微调影像
6、超清预览、发送打印
编辑布局模板:
我们在一个Grid中,通过行数和列数循环创建带边框的Border来显示表格,并添加鼠标事件:
for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { Border border = new Border { Width = w, Height = h, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, Margin = new Thickness(j * w, i * h, 0, 0), BorderThickness = new Thickness(1), BorderBrush = ColorHandler.GetColorBrush("#CCCCCC"), Background = ColorHandler.GetColorBrush("#000000"), }; border.MouseEnter += Border_MouseEnter; border.MouseLeftButtonDown += Border_MouseLeftButtonDown; GridTempl.Children.Add(border); } }
点击单元格时将改变背景颜色,在鼠标按下时并移动鼠标,触发MouseEnter,选择多个单元格:
因为合并单元格是不能为不规则形状,所以多选的单元格整体必须为一个矩形,
因此多选时首先记录所有选中的单元格,然后通过坐标判断左上角和右下角的单元格位置,这样整体矩形的宽和高的范围就确定了,
在此矩形范围内的单元格将自动全部选中:
但也有特殊情况:如果矩形范围包含大小不一的单元格 这时候计算范围就会不准确:
通过以下几种情况来判断大单元格与小单元格的包含关系:
/// <summary> /// 筛选出已经合并的cell并计算最大选中范围 /// </summary> private void CheckCell() { List<Border> bors = new List<Border>(); for (int i = 0; i < GridTempl.Children.Count; i++) { Border border = (GridTempl.Children[i] as Border); if (((SolidColorBrush)border.Background).Color == Color.FromRgb(68, 68, 68)) { bors.Add(border); } } double cellMinLeft = bors[0].Margin.Left; double cellMaxLeft = 0; double cellMinTop = bors[0].Margin.Top; double cellMaxTop = 0; for (int i = 0; i < bors.Count; i++) { if (bors[i].Margin.Left < cellMinLeft) { cellMinLeft = bors[i].Margin.Left; } if (bors[i].Margin.Top < cellMinTop) { cellMinTop = bors[i].Margin.Top; } if (bors[i].Margin.Top + bors[i].Height > cellMaxTop) { cellMaxTop = bors[i].Margin.Top + bors[i].Height; } if (bors[i].Margin.Left + bors[i].Width > cellMaxLeft) { cellMaxLeft = bors[i].Margin.Left + bors[i].Width; } } for (int i = 0; i < GridTempl.Children.Count; i++) { Border otherBor = GridTempl.Children[i] as Border; if (bors.Contains(otherBor)) { continue; } //包含左上角 if (otherBor.Margin.Left > cellMinLeft && (otherBor.Margin.Left) < cellMaxLeft && otherBor.Margin.Top > cellMinTop && (otherBor.Margin.Top) < cellMaxTop) { otherBor.Background = ColorHandler.GetColorBrush("#444444"); CheckCell(); return; } //包含右上角 if (otherBor.Margin.Left + otherBor.Width > cellMinLeft && (otherBor.Margin.Left + otherBor.Width) < cellMaxLeft && otherBor.Margin.Top > cellMinTop && (otherBor.Margin.Top) < cellMaxTop) { otherBor.Background = ColorHandler.GetColorBrush("#444444"); CheckCell(); return; } //包含右下角 if (otherBor.Margin.Left + otherBor.Width > cellMinLeft && (otherBor.Margin.Left + otherBor.Width) < cellMaxLeft && (otherBor.Margin.Top + otherBor.Height) > cellMinTop && (otherBor.Margin.Top + otherBor.Height) < cellMaxTop) { otherBor.Background = ColorHandler.GetColorBrush("#444444"); CheckCell(); return; } //包含左下角 if (otherBor.Margin.Left > cellMinLeft && (otherBor.Margin.Left) < cellMaxLeft && (otherBor.Margin.Top + otherBor.Height) > cellMinTop && (otherBor.Margin.Top + otherBor.Height) < cellMaxTop) { otherBor.Background = ColorHandler.GetColorBrush("#444444"); CheckCell(); return; } //水平分割 if (otherBor.Margin.Left > cellMinLeft && (otherBor.Margin.Left) < cellMaxLeft && (otherBor.Margin.Top) <= cellMinTop && (otherBor.Margin.Top + otherBor.Height) >= cellMaxTop) { otherBor.Background = ColorHandler.GetColorBrush("#444444"); CheckCell(); return; } //垂直分割 if (otherBor.Margin.Left <= cellMinLeft && (otherBor.Margin.Left + otherBor.Width) >= cellMaxLeft && (otherBor.Margin.Top) > cellMinTop && (otherBor.Margin.Top + otherBor.Height) < cellMaxTop) { otherBor.Background = ColorHandler.GetColorBrush("#444444"); CheckCell(); return; } } }
通过递归填充单元格达到矩形范围的同行同列自动选择,接下来就可以合并所选择的单元格:
计算最大宽度和最大高度,并且使左上角的单元格等于最大宽高,以实现合并效果:
//计算最大宽度 double w = borderFirst.Width; for (int i = 0; i < bors.Count; i++) { if (bors[i] != borderFirst && borderFirst.Margin.Top == bors[i].Margin.Top) { w += bors[i].Width; } } //计算最大高度 double h = borderFirst.Height; for (int i = 0; i < bors.Count; i++) { if (bors[i] != borderFirst && borderFirst.Margin.Left == bors[i].Margin.Left) { h += bors[i].Height; } } borderFirst.Tag = Math.Round(h / borderFirst.Height) + "#" + Math.Round(w / borderFirst.Width); borderFirst.Width = w; borderFirst.Height = h;
看效果:
将布局通过自定义格式保存到本地文件,就可以在排版界面载入布局模板。
编辑标记模板:
选择常用Tag添加到胶片的四个角,以便在后面载入影像的时候读取标记信息:
读取检查列表和下载影像:
可以参考本系列教程文章:
C#开发PACS医学影像处理系统(五):查询病人信息列表
载入影像并微调(平移,缩放,自由旋转等二维操作):
使用1:1像素超清预览查看打印细节:
下载一个打印服务端模拟接受打印:
我这里使用的是模拟激光相机5.0版本,下载地址:https://www.fxxz.com/soft/47115.html
设置好端口并发送,查看握手状态和通讯包:
查看打印结果:
- 二十九、EFW框架开发的系统支持SaaS模式和实现思路
- Dcmtk在PACS开发中的应用(基础篇)打印影像(胶片)
- C#.NET 大型通用信息化系统集成快速开发平台 4.0 版本 - 标准省市县数据的公司选择窗口实现
- ( 六 )Seafile FUSE 虚拟文件系统开发---写接口实现思路
- 使用tinymini210开发远程的温度监控系统,实现C#和安卓客户端显示数据和控制
- C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 基于数据库资源的多语言实现
- C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 实现缓存预热
- 【安卓-自定义布局】安卓App开发思路 一步一个脚印(十二)实现自定义左右滚动的信息块-仿蘑菇街
- 信息管理系统开发架构 配置实现列表展示分析图形及编辑等 构建信息分析展示平台 C#快速开发架构
- AjaxPro2.0+jQuery+C#实现文章系统中的顶一下、踩一下的代码开发
- 三十、【C#.Net开发框架】WCFHosting服务主机的利用WCF服务通讯和实现思路
- C# ThreadPool 自定义线程管理池 实现多线程池管理 有助与开发多线程系统的线程运行情况监控。
- C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 访问频率限制功能实现、防止黑客扫描、防止恶意刷屏
- 用c#开发微信 (12) 微统计 - 阅读分享统计系统 2 业务逻辑实现
- 【安卓-自定义布局】安卓App开发思路 一步一个脚印(七)实现ViewPager无限循环与自动播放
- 【安卓-自定义布局】安卓App开发思路 一步一个脚印(十)实现内嵌在app中的webview 腾讯开源X5 高效安全
- C#实现无物理边距真正可打印区域的绘图\打印程序开发
- 利用自动深度学习,医务人员也可开发医学影像分类系统,《柳叶刀》子刊报道
- C#开发WinForm程序如何实现程序最小化到系统托盘
- C#日志系统之实时打印实现