本篇文章將介紹開發醫學影像膠片打印系統(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
設置好端口並發送,查看握手狀態和通訊包:

查看打印結果:


