C#開發醫學影像膠片列印系統(一):萬能花式佈局的實現思路

喬克灬叔叔發表於2021-04-27

本篇文章將介紹開發醫學影像膠片列印系統(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

設定好埠併傳送,檢視握手狀態和通訊包:

檢視列印結果:

 

 

相關文章