用c#实现:?
1 ct ?文件说明:
说明数据文件
说明图像文件(2进制 8位)
一张CT图像有 512x512 个像素点,在dicom文件中每个像素由2字节表示,所以每张图片约512KB大小。图像中每个像素都是整数,专业名称为 Hounsfield scale 或 CT Number,是描述物质的放射密度的量化值(参考Wikipedia)。上表为常见物质的HU值。
2、读取数据
原始数据:
Data.des中是xml格式数据:<whole>
<Patient>
<PatientName>zhang san</PatientName>
<PatientSex>m</PatientSex>
<PatientAge>60</PatientAge>
</Patient>
<Scan>
<Hospital>ShenYang Hospital</Hospital>
<WindowWidth>600</WindowWidth>
<WindowCenter>400</WindowCenter>
<ScanTime>2010/06/20 12:30:20</ScanTime>
<ImageWidth>512</ImageWidth>
<ImageHeight>512</ImageHeight>
<XPixelSpacing>0.05</XPixelSpacing>
<YPixelSpacing>0.05</YPixelSpacing>
<Interval>0.1</Interval>
</Scan>
<Image>
<Image1>D001_001.0.raw</Image1>
<Image2>D002_001.0.raw</Image2>
<Image3>D002_002.0.raw</Image3>
<Image4>D002_003.0.raw</Image4>
<Image5>D002_004.0.raw</Image5>
<Image6>D002_005.0.raw</Image6>
<Image7>D002_006.0.raw</Image7>
</Image>
</whole>
患者信息等数据是xml格式通过XmlDocument读取
用记事本打开 des 文件
通过标签描述数据本身
患者姓名 ?性别 ?年龄 扫描(scan)等信息
1 我们要把这个文件内容读取到我们程序中
首先我们构建一个对象imageInfo 来描述这些信息?
1、目标 把xml 信息读取到对象中来
2、借用一个xmldocument 对象进行处理
3 xmldocument ?完整描述一个xml文件!
xml 文件又着严格的层级结构!
建议大家利用第三方AI 工具生成这些代码,再阅读!
读取图像数据 raw,要把它转为bitmap
ps:https://learn.microsoft.com/zh-CN/dotnet/api/system.drawing.bitmap?view=dotnet-plat-ext-8.0
打开文件:
准备:
? ? ? ? private string fileName= "d:/temp/D001_001.0.raw"; ? ? ? ?//文件名
? ? ? ? private short[] imageData; ? ? ? //原始数据
? ? ? ? Bitmap bmpImage; ? ? ? ? ? ? ? ?//显示位图
? ? ? ? private int windowWidth = 300; //窗宽
? ? ? ? private int windowCenter = 100; //窗位
? ? ? ? private int imgWidth = 512; ? ? //图像宽度
? ? ? ? private int imgHeight = 512; ? ?//图像高度
1) File.Open
2)BinaryReader
读取的文件放到一个临时数组当中
3)byte[] tempData = new byte[imgWidth * imgHeight * 2];
4)把tempData ?数据读取到imageData 数组当中,这时候要位移运算 ? ? ? imageData[j] = (short)((short)tempData[j * 2 + 1] << 8 | tempData[j*2]);
5)灰度转换
windowCenter、windowCenter和医学有关,自己去查。
? ? ? ? public void Rendering()
? ? ? ? {
? ? ? ? ? ? //使用窗宽窗位,创建灰度查找表。图像存储的数据都是正值,而CT值的最小值是1024,查表时下标必须为正,
? ? ? ? ? ? //所以此处调整了窗宽窗位
? ? ? ? ? ? int offset = 1024;
? ? ? ? ? ? Byte[] lookUpTable = new Byte[4096]; ? ?//数组元素默认值为0
? ? ? ? ? ? int low = windowCenter - windowWidth / 2 + offset;
? ? ? ? ? ? int high = windowCenter + windowWidth / 2 + offset;
? ? ? ? ? ? for (int i = low; i <= high; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? lookUpTable[i] = (Byte)((i - (windowCenter - windowWidth / 2 + offset)) / (double)windowWidth * 255);
? ? ? ? ? ? }
? ? ? ? ? ? for (int i = high + 1; i < 4096; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? lookUpTable[i] = 255;
? ? ? ? ? ? }
? ? ? ? ? ? grayBmp = new byte[imgWidth * imgHeight];
? ? ? ? ? ? for (int i = 0; i < imgHeight; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int j = 0; j < imgWidth; j++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? short data = (short)(imageData[i * imgWidth + j]);
? ? ? ? ? ? ? ? ? ? grayBmp[i * imgWidth + j] = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? int temp = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? Color pixel = Color.FromArgb(temp, temp, temp);
? ? ? ? ? ? ? ? ? ? bmpImage.SetPixel(j, i, pixel); ? ? //速度很慢,使用下面的方法把数据一次性传过去
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
把图像文件信息封装到类当中:
{
[System.Serializable]
class MImage
{
private string fileName; //文件名
private short[] imageData; //原始数据
Bitmap bmpImage; //显示位图
private int windowWidth = 300; //窗宽
private int windowCenter = 100; //窗位
private int imgWidth = 512; //图像宽度
private int imgHeight = 512; //图像高度
public int ImgWidth
{
get { return imgWidth; }
}
public int ImgHeight
{
get { return imgHeight; }
}
public int WindowWidth
{
get { return windowWidth; }
set { windowWidth = value; }
}
public int WindowCenter
{
get { return windowCenter; }
set { windowCenter = value; }
}
public Bitmap BmpImage
{
get { return bmpImage; }
}
public MImage(string fileName)
{
this.fileName = fileName;
this.imgWidth = ImageInfo.ImageWidth;
this.imgHeight = ImageInfo.ImageHeight;
this.windowWidth = ImageInfo.WindowWidth;
this.windowCenter = ImageInfo.WindowCenter;
}
public Bitmap LoadImage()
{
}
}
}
6) 前端渲染? 通过graphics 把bitmap呈现在界面。
? ? ? ? ? ? Rectangle showRect = new Rectangle();
1 我们通过OnPaint 绘制图像?,搞个继承panel的自定义组件实现。
通过OnPaint 绘制图像
界面设计布局:
增加子面板的的例子代码:
for (int i = 1; i < 4; i++)
{
for (int x = 1; x < 4; x++)
{
Panel panel1 = new Panel();
// Initialize the Panel control.
panel1.Location = new Point(56 + i * 264, 72 + x * 152);
panel1.Size = new Size(264, 152);
// Set the Borderstyle for the Panel to three-dimensional.
panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
// Initialize the Label and TextBox controls.
// Add the Panel control to the form.
this.Controls.Add(panel1);
// Add the Label and TextBox controls to the Panel.
}
}
Winform ?menu+status+SplitContainer
?? ?
?? ?splicePage ? :作为左半边的面板 ?这个是自定义的 继承了Panel
?? ?
?? ?Child panel: ?MImageWnd
? ??
?? ?创建panel
?? ?1 ? MImageWnd mw= new ?MImageWnd()
?? ?
?? ?2 ? 追加到父容器splicePage ? 当中 ? this.Controls.add(mw)
?定位: 左 上位置
?? ?
?? ?设置childPanel ?的 height、width:平分splicePage ? 的长度 宽度
显示 与显示数据分离 ? ??
?? ?
?? ?List<MImageWnd > ?
?? ?
? ? 我们要干三件工作
?? ?
?? ?1 ?改变colNum ?.rowNum
?? ?
?? ?//清理现场的工作
?? ?2 ?清空wndList wndList 是我们记录子面板的集合 ?可以看成是前端的数据模型
?? ?
?? ?3、Controls ?remove MImageWnd?
? ? ? ? ? ?InitializeComponent();
? ? ? ? ? ?Rectangle rect = Screen.GetWorkingArea(this); ? //获得屏幕分辨率
? ? ? ? ? ?this.WindowState = FormWindowState.Maximized;
? ? ? ? ? ?this.mainSplit.SplitterDistance = (int)(this.mainSplit.Width * 4f / 5f);
? ? ? ? ? ?this.slicePage = new SlicePage();
? ? ? ? ? ?slicePage.Dock = DockStyle.Fill;
? ? ? ? ? ?this.mainSplit.Panel1.Controls.Add(slicePage);
? ? ? ? ? ?slicePage.CreateControls();
slicePage: 起到父容器及控制器作用:
{
public partial class SlicePage : Panel
{
List<MImageWnd> wndList = new List<MImageWnd>(); //窗口数组
List<MImage> imgList = new List<MImage>(); //图像数组
List<AnnoTool> annoList = new List<AnnoTool>(); //标注数组,和图像一一对应
int rowNum = 3; //子窗口行数
int columnNum = 3; //子窗口列数
VScrollBar sb = new VScrollBar();
int scrollBarWidth = 20;
public List<MImage> ImgList
{
get { return imgList; }
set { imgList = value; }
}
public List<AnnoTool> AnnoList
{
get { return annoList; }
set { annoList = value; }
}
public SlicePage()
{
sb.Minimum = 0;
sb.Maximum = 0;
sb.Scroll += new ScrollEventHandler(sb_Scroll);
}
public void LoadImage()
{
if (imgList == null)
{
return;
}
int num = imgList.Count < wndList.Count ? imgList.Count : wndList.Count;
for (int i = 0; i < num; i++)
{
wndList[i].SetImage(imgList[i], annoList[i]);
wndList[i].Invalidate();
wndList[i].Update();
// ((MainForm)(this.Parent.Parent.Parent)).ProcessProgressBar();
}
sb.Minimum = 0;
if (imgList.Count % columnNum != 0)
{
sb.Maximum = imgList.Count / columnNum;
}
else
sb.Maximum = imgList.Count / columnNum - 1;
sb.SmallChange = 1;
sb.LargeChange = rowNum;
sb.Value = 0;
}
public void Rotate()
{
foreach (MImageWnd wnd in wndList)
{
wnd.Rotate();
}
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// SlicePage
//
this.Resize += new System.EventHandler(this.SlicePage_Resize);
this.ResumeLayout(false);
}
void sb_Scroll(object sender, ScrollEventArgs e)
{
if (imgList == null)
{
return;
}
int start = e.NewValue * columnNum; //点击滚动条后,从此幅图像开始显示
int num = (imgList.Count - start) < wndList.Count ? (imgList.Count - start) : wndList.Count;
for (int i = 0; i < num; i++)
{
wndList[i].SetImage(imgList[i + start], annoList[i + start]);
wndList[i].Invalidate();
wndList[i].Update();
}
for (int i = num; i < wndList.Count; i++) //多余的窗口,清除它的图像
{
wndList[i].ClearImage();
}
}
public void Relayout(int row, int col)
{
rowNum = row;
columnNum = col;
foreach (MImageWnd wnd in wndList)
{
this.Controls.Remove(wnd);
}
this.wndList.Clear();
CreateControls();
ArrangeControls();
LoadImage(); //改变了窗口个数以后,重新加载图像
}
public void CreateControls()
{
for (int i = 0; i < rowNum * columnNum; i++)
{
MImageWnd wnd = new MImageWnd();
wnd.BorderStyle = BorderStyle.FixedSingle;
this.Controls.Add(wnd);
wndList.Add(wnd);
}
this.Controls.Add(sb);
}
private void ArrangeControls()
{
if (wndList.Count == 0)
return;
int width = (this.Width - scrollBarWidth) / columnNum;
int height = this.Height / rowNum;
for (int i = 0; i < rowNum * columnNum; i++)
{
wndList[i].Location = new Point(i % columnNum * width, i / columnNum * height);
wndList[i].ClientSize = new Size(width, height);
}
sb.Location = new Point(this.Width - scrollBarWidth, 0);
sb.Size = new Size(scrollBarWidth, this.Height);
}
//此两种写法的详细区别。
protected override void OnResize(EventArgs eventargs)
{
ArrangeControls();
base.OnResize(eventargs);
}
private void SlicePage_Resize(object sender, EventArgs e)
{
}
//public const int USER = 0x0400;
//public const int WM_TEST = USER + 101;
//protected override void DefWndProc(ref System.Windows.Forms.Message m)
//{
// switch (m.Msg)
// {
// case WM_TEST: //处理消息
// MessageBox.Show("haha");
// break;
// default:
// base.DefWndProc(ref m);//调用基类函数处理非自定义消息。
// break;
// }
//}
}
}
其中,重要的一点是把数据源设置到子面板当中
wndList[i].SetImage(imgList[i]);
wndList[i].Invalidate();
wndList[i].Update();
?为scrollBar留空间 ? int scrollBarWidth = 20;
查官网 scrollBar 用法
1 、创建 :VScrollBar sb = new VScrollBar();
?2 、设置 scrollBarWidth ?height
3 、add 到父容器
scrollBar5个属性:
value=0 ?: 滚动的刻度
Maximum= ? this.imglist.count%columnNum*rowNum!=0
? ? ? ? ? ? ? ?this.imglist.count/columnNum*rowNum
?? ??? ??? ? ??
?? ??? ??? ? ? this.imglist.count/columnNum*rowNum-1
Minimum=0
SmallChange=1 ? ?
LargeChange=rowNum
?
?? ?设置滚动事件
?? ?3*3 ??
?? ?改参数 ?2*2 ?、4*4
?? ?清除 数据模型、实际展示
?? ?
?? ?? ??
? ? ps:Delegate Class
?? ?类比c函数指针:
?? ?https://learn.microsoft.com/en-us/dotnet/csharp/delegates-overview
?? ?
?? ?
?? ? ?通过委托来实现事件处理的过程,通常需要以下4个步骤:
?? ?? 定义委托类型,并在发布者类中定义一个该类型的公有成员。
?? ?? 在订阅者类中定义委托处理方法。
?? ?? 订阅者对象将其事件处理方法链接到发布者对象的委托成员(一个委托类型的引用)上。
?? ?? 发布者对象在特定的情况下“激发”委托操作,从而自动调用订阅者对象的委托处理方法。
?? ?? ??
1 打开文件
根据dilalog 中的选择 动态读取xml 文件
xml文件当中的信息 转换为ImageInfo对象、设置? MIMage
我们已经能读取数据,也能形成九宫格,现在要组装到项目中:
form ? :读取文件 ?——》
?? ?xml----->imageInfo
?? ? 数据文件---》 ? MIMage
? ? ? ? ImageInfo info = new ImageInfo();
?? ? ? ?XmlDocument myxml = new XmlDocument();
? ? ?ps:? ? ?OpenFileDialog
?? ?? ? 来自 <https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.openfiledialog?view=windowsdesktop-7.0>?
? 更改openfileDialog过滤条件
? ? ?dlg.Filter = "图像描述文件 (*.des)|*.des|生数据 (*.raw)|*.raw";
?
调用?? ?
? ? ? ? if (dlg.ShowDialog() == DialogResult.OK)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? slicePage.ImgList.Clear();
? ? ? ? ? ? ? ? if (dlg.FilterIndex == 1)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ParseDesFile(dlg.FileName);
? ? ? ? ? ? ? ? ? ? //slicePage.LoadImage(); ?//重绘界面
?? ??? ??? ?}
? ? ? ? ? ? ?}
? ? ? ?
private void ParseDesFile(string fileName)
{
ImageInfo info = new ImageInfo();
XmlDocument myxml = new XmlDocument();
myxml.Load(fileName);
XmlNode root = myxml.DocumentElement;
XmlNode node = root["Patient"];
ImageInfo.PatientName = node["PatientName"].InnerText;
ImageInfo.PatientSex = Convert.ToChar(node["PatientSex"].InnerText);
ImageInfo.PatientAge = Convert.ToInt32(node["PatientAge"].InnerText);
node = root["Scan"];
ImageInfo.HospitalName = node["Hospital"].InnerText;
ImageInfo.WindowWidth = Convert.ToInt32(node["WindowWidth"].InnerText);
ImageInfo.WindowCenter = Convert.ToInt32(node["WindowCenter"].InnerText);
ImageInfo.ScanTime = node["ScanTime"].InnerText;
ImageInfo.ImageWidth = Convert.ToInt32(node["ImageWidth"].InnerText);
ImageInfo.ImageHeight = Convert.ToInt32(node["ImageHeight"].InnerText);
ImageInfo.XPixelSpacing = Convert.ToDouble(node["XPixelSpacing"].InnerText);
ImageInfo.YPixelSpacing = Convert.ToDouble(node["YPixelSpacing"].InnerText);
ImageInfo.Interval = Convert.ToDouble(node["Interval"].InnerText);
node = root["Image"];
int pos = fileName.LastIndexOf('\\');
string path = fileName.Substring(0, pos + 1);
foreach (XmlNode var in node.ChildNodes)
{
MImage img = new MImage(path + var.InnerText);
slicePage.ImgList.Add(img);
}
}
? ? Draw object:
?? ?1. Rectangle
?? ?2. Line
?? ?3. Ellipse
?? ?4. Text
1?? ?创建MImage、 imageInfo 2个文件,? 修改namespace ?MImage中增加loadImage方法
2 ? slicePage 起到连接数据与显示模型的桥梁的作用,我们在它当中增加字段与属性,让它认识MImage等
? ? ? ? private List<MImage> imgList = new List<MImage>(); ? ? ?//图像数组
?? ??? ?internal List<MImage> ImgList { get => imgList; set => imgList = value; }
?
控制层:
? ? ? ? /**
? ? ? ? ?* 把数据模型的数据塞给显示模型 ?这里不负责渲染 ?但调用invalate() update() 方法 引起渲染
? ? ? ? ?*?
? ? ? ? ?*/
? ? ? ? public void LoadImage() {
? ? ? ? ? ? for (int i = 0; i < 7; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? wndList[i].SetImage(imgList[i]);
? ? ? ? ? ? ? ? wndList[i].Invalidate();
? ? ? ? ? ? ? ? wndList[i].Update();
? ? ? ? ? ? }
? ? ? ? }
Mimage: 准备数据,把原始raw数据转换为bitmap数据:
? ? public Bitmap LoadImage()
? ? {
? ? ? ? imageData = new short[imgWidth * imgHeight];
? ? ? ? bmpImage = new System.Drawing.Bitmap(imgWidth, imgHeight);
? ? ? ? byte[] tempData = new byte[imgWidth * imgHeight * 2];
? ? ? ? BinaryReader sr = new BinaryReader(File.Open(fileName, FileMode.Open));
? ? ? ? //没有办法直接把数据读到short数组里面,所以借助byte数组转化一下。
? ? ? ? sr.Read(tempData, 0, imgWidth * imgHeight * 2);
? ? ? ? sr.Close();
? ? ? ? for (int i = 0; i < imgWidth * imgHeight; i++)
? ? ? ? {
? ? ? ? ? ? imageData[i] = (short)((short)tempData[2 * i + 1] << 8 | tempData[2 * i]);
? ? ? ? }
? ? ? ? Rendering();
? ? ? ? return bmpImage;
? ? }
? ? ? ? ? ? public void Rendering()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //使用窗宽窗位,创建灰度查找表。图像存储的数据都是正值,而CT值的最小值是1024,查表时下标必须为正,
? ? ? ? ? ? ? ? //所以此处调整了窗宽窗位
? ? ? ? ? ? ? ? int offset = 1024;
? ? ? ? ? ? ? ? Byte[] lookUpTable = new Byte[4096]; ? ?//数组元素默认值为0
? ? ? ? ? ? ? ? int low = windowCenter - windowWidth / 2 + offset;
? ? ? ? ? ? ? ? int high = windowCenter + windowWidth / 2 + offset;
? ? ? ? ? ? ? ? for (int i = low; i <= high; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? lookUpTable[i] = (Byte)((i - (windowCenter - windowWidth / 2 + offset)) / (double)windowWidth * 255);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? for (int i = high + 1; i < 4096; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? lookUpTable[i] = 255;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? //grayBmp = new byte[imgWidth * imgHeight];
? ? ? ? ? ? ? ? for (int i = 0; i < imgHeight; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? for (int j = 0; j < imgWidth; j++)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? short data = (short)(imageData[i * imgWidth + j]);
? ? ? ? ? ? ? ? ? ? ? ? //grayBmp[i * imgWidth + j] = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? ? ? int temp = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? ? ? Color pixel = Color.FromArgb(temp, temp, temp);
? ? ? ? ? ? ? ? ? ? ? ? bmpImage.SetPixel(j, i, pixel); ? ? //速度很慢,使用下面的方法把数据一次性传过去
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
//绘制数据? ? ?
? ? ? if (null != mImage)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? double tempFactor;
? ? ? ? ? ? ? ? if (this.Width / (double)this.Height > mImage.ImgWidth / (double)mImage.ImgHeight) ?//如果窗口较宽
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? tempFactor = this.Height / (double)mImage.ImgHeight * factor;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? tempFactor = this.Width / (double)mImage.ImgWidth * factor;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? showRect.X = (int)((this.Width - mImage.ImgWidth * tempFactor) / 2) + offset.X;
? ? ? ? ? ? ? ? showRect.Y = (int)((this.Height - mImage.ImgHeight * tempFactor) / 2) + offset.Y;
? ? ? ? ? ? ? ? showRect.Width = (int)(mImage.ImgWidth * tempFactor);
? ? ? ? ? ? ? ? showRect.Height = (int)(mImage.ImgHeight * tempFactor);
? ? ? ? ? ? ? ? e.Graphics.DrawImage(mImage.BmpImage, showRect);
? ? ? ? ? ? }
?? ?滚动事件:
?? ?
?? ? ? ?动态计算偏移量
?? ? ?重新设置Imgwnd 的图像数据源
?? ?
?? ?
?? ? ? ?加载的时候,loadimg
?? ?
?? ?
?? ? ? ? ? ? ? ?sb.Minimum = 0;
?? ? ? ? ? ? ? ?if (imgList.Count % columnNum != 0)
?? ? ? ? ? ? ? ?{
?? ? ? ? ? ? ? ? ? ?sb.Maximum = imgList.Count / columnNum;
?? ? ? ? ? ? ? ?}
?? ? ? ? ? ? ? ?else
?? ? ? ? ? ? ? ? ? ?sb.Maximum = imgList.Count / columnNum - 1;
?? ? ? ? ? ? ? ?sb.SmallChange = 1;
?? ? ? ? ? ? ? ?sb.LargeChange = rowNum;
?? ? ? ? ? ? ? ?sb.Value = 0;
?? ?
鼠标状态
?? ?
?? ? ? ?enum MOUSESTATE
?? ? ? ?{
?? ? ? ? ? ?NONE,
?? ? ? ? ? ?WL,
?? ? ? ? ? ?ZOOM,
?? ? ? ? ? ?MOVE,
?? ? ? ? ? ?LINE,
?? ? ? ? ? ?RECT,
?? ? ? ? ? ?ELLIPSE,
?? ? ? ? ? ?TEXT
?? ? ? ?}
??
?? ?
?? ?
?? ?图像旋转:
?? ? ? ? 这个基于矩阵变化
?? ? Matrix rotateMatrix = new Matrix();
?? ?
?? ?绘制的时候设置gr转换
?? ?
?? ?? ??
?? ?Wnd:
?? ? ? ? ? ?public void Rotate()
?? ? ? ? ? ?{
?? ? ? ? ? ? ? ?Point center = new Point();
?? ? ? ? ? ? ? ?center.X = (showRect.Left + showRect.Right) / 2;
?? ? ? ? ? ? ? ?center.Y = (showRect.Top + showRect.Bottom) / 2;
?? ? ? ? ? ? ? ?rotateMatrix.RotateAt(90, center);
?? ? ? ? ? ? ? ?Invalidate();
?? ? ? ? ? ? ? ?Update();
?? ? ? ? ? ?}
? ?
?? ?
?? ?slicePage 循环调用它
?? ?委托:
?? ?
?? ?using System;
?? ?using System.IO;
?? ?
?? ?namespace DelegateAppl
?? ?{
?? ? ? class PrintString
?? ? ? {
?? ? ? ? ?static FileStream fs;
?? ? ? ? ?static StreamWriter sw;
?? ? ? ? ?// 委托声明
?? ? ? ? ?public delegate void printString(string s);
?? ?
?? ? ? ? ?// 该方法打印到控制台
?? ? ? ? ?public static void WriteToScreen(string str)
?? ? ? ? ?{
?? ? ? ? ? ? Console.WriteLine("The String is: {0}", str);
?? ? ? ? ?}
?? ? ? ? ?// 该方法打印到文件
?? ? ? ? ?public static void WriteToFile(string s)
?? ? ? ? ?{
?? ? ? ? ? ? fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
?? ? ? ? ? ? sw = new StreamWriter(fs);
?? ? ? ? ? ? sw.WriteLine(s);
?? ? ? ? ? ? sw.Flush();
?? ? ? ? ? ? sw.Close();
?? ? ? ? ? ? fs.Close();
?? ? ? ? ?}
?? ? ? ? ?// 该方法把委托作为参数,并使用它调用方法
?? ? ? ? ?public static void sendString(printString ps)
?? ? ? ? ?{
?? ? ? ? ? ? ps("Hello World");
?? ? ? ? ?}
?? ? ? ? ?static void Main(string[] args)
?? ? ? ? ?{
?? ? ? ? ? ? printString ps1 = new printString(WriteToScreen);
?? ? ? ? ? ? printString ps2 = new printString(WriteToFile);
?? ? ? ? ? ? sendString(ps1);
?? ? ? ? ? ? sendString(ps2);
?? ? ? ? ? ? Console.ReadKey();
?? ? ? ? ?}
?? ? ? }
?? ?}
?? ?? ? 绘制线段:
1、MImageWnd中 规定位置字段,记录点击位置
private int start_x = -1;
private int start_y = -1;
private int end_x = -1;
private int end_y = -1;
2、构建 MLine 类
internal class MLine
{
private int start_x = -1;
private int start_y = -1;
private int end_x = -1;
private int end_y = -1;
public int Start_x { get=> start_x; set=> start_x=value; }
public int Start_y { get => start_y; set => start_y = value; }
public int End_x { get => end_x; set => end_x = value; }
public int End_Y { get => end_y; set => end_y = value; }
}
3、MImageWnd中 添加集合 List<MLine> lines
4、 划线方法 public void DrawLineInt(int x0, int y0, int x1, int y1, Graphics g)
{
......
}
? ??
?? ?事件定义、订阅、发布处理 的例子代码:
?? ?
using System;
?? ?namespace SimpleEvent
?? ?{
?? ? ?using System;
?? ? ?/***********发布器类***********/
?? ? ?public class EventTest
?? ? ?{
?? ? ? ?private int value;
?? ?
?? ? ? ?public delegate void NumManipulationHandler();
?? ?
?? ?
?? ? ? ?public event NumManipulationHandler ChangeNum;
?? ? ? ?protected virtual void OnNumChanged()
?? ? ? ?{
?? ? ? ? ?if ( ChangeNum != null )
?? ? ? ? ?{
?? ? ? ? ? ?ChangeNum(); /* 事件被触发 */
?? ? ? ? ?}else {
?? ? ? ? ? ?Console.WriteLine( "event not fire" );
?? ? ? ? ? ?Console.ReadKey(); /* 回车继续 */
?? ? ? ? ?}
?? ? ? ?}
?? ?
?? ?
?? ? ? ?public EventTest()
?? ? ? ?{
?? ? ? ? ?int n = 5;
?? ? ? ? ?SetValue( n );
?? ? ? ?}
?? ?
?? ?
?? ? ? ?public void SetValue( int n )
?? ? ? ?{
?? ? ? ? ?if ( value != n )
?? ? ? ? ?{
?? ? ? ? ? ?value = n;
?? ? ? ? ? ?OnNumChanged();
?? ? ? ? ?}
?? ? ? ?}
?? ? ?}
?? ?
?? ?
?? ? ?/***********订阅器类***********/
?? ?
?? ? ?public class subscribEvent
?? ? ?{
?? ? ? ?public void printf()
?? ? ? ?{
?? ? ? ? ?Console.WriteLine( "event fire" );
?? ? ? ? ?Console.ReadKey(); /* 回车继续 */
?? ? ? ?}
?? ? ?}
?? ?
?? ? ?/***********触发***********/
?? ? ?public class MainClass
?? ? ?{
?? ? ? ?public static void Main()
?? ? ? ?{
?? ? ? ? ?EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
?? ? ? ? ?subscribEvent v = new subscribEvent(); /* 实例化对象 */
?? ? ? ? ?e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
?? ? ? ? ?e.SetValue( 7 );
?? ? ? ? ?e.SetValue( 11 );
?? ? ? ?}
?? ? ?}
?? ?}
?? ?
?? ?e.KeyCode == Keys.Enter
?? ?