帮酷LOGO
0 0 评论
文章标签:图像  USE  用户  VIEW  Viewer  image-viewer  
ImageViewerUserControl/KpImageViewerV2.png

介绍

ImageViewer UserControl 是我创建的用来填补我在表单上显示图像的空白的东西。 我想能放大,缩小,旋转我的图像和最好的,不必放大我的图像,以使它适合我的表单。

我已经找到了解决方案,比如在面板内拖动 PictureBox,可以能有很好的问题。 对于本文,我希望与那些可能拥有同样问题的人分享我的工作。

用户控件的属性

AllowDropbool用于启用或者禁用控件上的拖放功能的属性。
BackgroundColor颜色获取或者设置图片面板上的背景颜色的属性。
GifAnimationbool用于启用或者禁用 *.gif 文件的动画的属性。
GifFPS一个属性,用于调整 *.gif 文件的动画每秒的帧数。 范围介于 1和 30之间。
Image位图获取或者设置控件显示的图像的属性。
ImagePath字符串将物理路径设置为图像( c:image.jpg )的属性。
MenuColor颜色要调整整个菜单使用的颜色的属性。
MenuPanelColor颜色仅调整菜单面板所使用颜色的属性。
OpenButtonboolUserControl 菜单上启用或者禁用打开按钮的属性。
NavigationPanelColor颜色仅调整导航面板使用的颜色的属性。
PanelWidthint返回图像面板宽度的属性。
PanelHeightint返回图像面板高度的属性。
PreviewButtonbool启用或者禁用 UserControl 菜单上的预览切换按钮的属性。
PreviewPanelColor颜色仅调整预览面板使用的颜色的属性。
PreviewText字符串编辑预览标签文本的属性。
Scrollbarsbool用于启用或者禁用滚动条的属性。
TextColor颜色调整所有标签使用的颜色的属性。
NavigationTextColor颜色用于调整导航标签使用的颜色的属性。
PreviewTextColor颜色用于调整预览标签使用的颜色的属性。
Rotationint获取或者设置图像旋转角度为( 0.90.180或者 270度)的属性。
ShowPreviewbool要启用或者禁用预览面板的属性。
Zoomint要获取百分比缩放量的属性。
OriginalSize大小获取图像原始大小的属性。
CurrentSize大小获取图像当前大小的属性。

用户控件的事件

AfterRotation旋转图像后激发的事件。

可用属性:
Rotationint一个属性,以( 0.90.180或者 270度) 为单位获取图像旋转。
AfterZoom 在放大或者缩小图像后激发的事件。

可用属性:
Zoomint获取百分比的缩放量的属性。
InOutKpZoom返回 ZoomIn 操作或者 ZoomOut 操作的属性。

使用代码

任何 UserControl 一样,它很容易将它拖到窗体上。 要在你的工具箱中获取控件,请执行以下步骤:

  • 1: 右键单击工具箱,然后单击选择项。
  • 在. NET 框架组件中进行收费:在框架组件中单击Browse按钮。
  • 步骤 3: 浏览到提取的文件夹并选择" KP-ImageViewerV2.dll"。
  • 步骤 4: 确认 KpImageViewer 已经选中并单击 Ok"。

ImageViewer 有内置的打开图像按钮,可以使用它。 但是,如果这不是你想要的,可以通过编程设置图像并通过设置 OpenButton 属性来禁用打开的按钮

false
privatevoid Form1_Load(object sender, EventArgs e)
{
 kpImageViewer.OpenButton = false;
 kpImageViewer.Image = new Bitmap(@"C:chuckwallpaper.jpg");
}

此外,还可以使用 3个旋转函数。 非常直接:

privatevoid Form1_Load(object sender, EventArgs e)
{
 kpImageViewer.Rotate90(); // Rotates the Image 90 degrees clockwise kpImageViewer.Rotate180(); // Rotates the Image 180 degrees clockwise kpImageViewer.Rotate270(); // Rotates the Image 270 degrees clockwise}

用户控制代码

KpImageViewer 类派生自

System.Windows.Forms.UserControl
类该类使用 2个单独的类和另一个 UserControlDrawEngineDrawObject 是使用的类,UserControl 是一个 DoubleBufferedPanel
publicclass PanelDoubleBuffered : System.Windows.Forms.Panel
{
 public PanelDoubleBuffered()
 {
 this.DoubleBuffered = true;
 this.UpdateStyles();
 }
}
 publicpartialclass KpImageViewer : UserControl
 {
 private KP_DrawEngine drawEngine;
 private KP_DrawObject drawing;
. . .
 }

DrawEngine 负责将位图存储在与面板大小完全相同的内存中。 它将用于渲染内存中的图像并将它的绘制到面板。 在调整大小和宽度的情况下,DrawEngine 将重新创建内存位图以保持高度和宽度。

publicvoid InitControl()
{
 drawEngine.CreateDoubleBuffer(pbFull.CreateGraphics(), pbFull.Width, pbFull.Height);
}privatevoid KP_ImageViewerV2_Resize(object sender, EventArgs e)
{
 InitControl();
 drawing.AvoidOutOfScreen();
 UpdatePanels(true);
}

DrawObject 拥有 Viewer的所有实际功能。 它负责存储原始图像在内存,缩放,旋转,拖动和跳转到原始的( 在预览面板上单击的位置)。 这些函数由 KpImageViewer 类内部触发的事件调用。 作为示例,请参见鼠标函数的Fragment。 它们负责拖动和选择图像:

privatevoid pbFull_MouseDown(object sender, MouseEventArgs e)
{
 if (e.Button == MouseButtons.Left)
 {
 // Left Shift or Right Shift pressed? Or is select mode one?if (this.IsKeyPressed(0xA0) || this.IsKeyPressed(0xA1) || selectMode == true)
 {
 // Fancy cursor pbFull.Cursor = Cursors.Cross;
 shiftSelecting = true;
 // Initial selection ptSelectionStart.X = e.X;
 ptSelectionStart.Y = e.Y;
 // No selection end ptSelectionEnd.X = -1;
 ptSelectionEnd.Y = -1;
 }
 else {
 // Start dragging drawing.BeginDrag(new Point(e.X, e.Y));
 // Fancy cursorif (grabCursor!= null)
 {
 pbFull.Cursor = grabCursor;
 }
 }
 }
}privatevoid pbFull_MouseUp(object sender, MouseEventArgs e)
{
 // Am i dragging or selecting?if (shiftSelecting == true)
 {
 // Calculate my selection rectangle Rectangle rect = CalculateReversibleRectangle(ptSelectionStart, ptSelectionEnd);
 // Clear the selection rectangle ptSelectionEnd.X = -1;
 ptSelectionEnd.Y = -1;
 ptSelectionStart.X = -1;
 ptSelectionStart.Y = -1;
 // Stop selecting shiftSelecting = false;
 // Position of the panel to the screen Point ptPbFull = PointToScreen(pbFull.Location);
 // Zoom to my selection drawing.ZoomToSelection(rect, ptPbFull);
 // Refresh my screen & update my preview panel pbFull.Refresh();
 UpdatePanels(true);
 }
 else {
 // Stop dragging and update my panels drawing.EndDrag();
 UpdatePanels(true);
 // Fancy cursorif (dragCursor!= null)
 {
 pbFull.Cursor = dragCursor;
 }
 }
}privatevoid pbFull_MouseMove(object sender, MouseEventArgs e)
{
 // Am I dragging or selecting?if (shiftSelecting == true)
 {
 // Keep selecting ptSelectionEnd.X = e.X;
 ptSelectionEnd.Y = e.Y;
 Rectangle pbFullRect = new Rectangle(0, 0, pbFull.Width - 1, pbFull.Height - 1);
 // Am I still selecting within my panel?if (pbFullRect.Contains(new Point(e.X, e.Y)))
 {
 // If so, draw my Rubber Band Rectangle! Rectangle rect = CalculateReversibleRectangle(ptSelectionStart, ptSelectionEnd);
 DrawReversibleRectangle(rect);
 }
 }
 else {
 // Keep dragging drawing.Drag(new Point(e.X, e.Y));
 if (drawing.IsDragging)
 {
 UpdatePanels(false);
 }
 else {
 // I'm not dragging OR selecting// Make sure if left or right shift is pressed to change cursorif (this.IsKeyPressed(0xA0) || this.IsKeyPressed(0xA1) || selectMode == true)
 {
 // Fancy Cursorif (pbFull.Cursor!= Cursors.Cross)
 {
 pbFull.Cursor = Cursors.Cross;
 }
 }
 else {
 // Fancy Cursorif (pbFull.Cursor!= dragCursor)
 {
 pbFull.Cursor = dragCursor;
 }
 }
 }
 }
}

功能:AvoidOutOfScreen() 用于避免图像在面板外浮动。 它被编程以确保图像永远不离开左上角( X ): 0,Y: 0 )。

我在这里的主要问题是,当你拖动你的图像,你不希望X 或者Y 坐标高于零。 这本身不是问题,但是一旦你开始查看它,它就会出现

boundingBox.Left
boundingBox.Top 这些值将与 boundingBox.WidthboundingBox.Height 负相反。 将这些值相加将以不正确的值结束,并使图像不正确地拖动。

我需要一个函数来确保X,Y 坐标从不高于零,从不低于( 图像宽度- PanelWidth ) - ( ( 图像宽度- PanelWidth ) * 2 )

使用 480 x320的查看器和 1024图像,你将得到以下公式:

(1024 - 480 - ((1024 - 480) * 2)) = -544 

这意味着最小的X 值将为 -544,以避免在右侧得到浮动图像。

下面是另一个可视化示例。 这里的图像是 512 x384. ( 注意,这只是一个 rectangle,实际图像没有从屏幕上绘制)

在这里可以看到,X的最小值是 -234. 如果它会降低到你最后一个面板右边的空白处。

AvoidOutOfScreen() technique

publicvoid AvoidOutOfScreen()
{
 try {
 if (boundingRect.X >= 0)
 {
 boundingRect.X = 0;
 }
 elseif ((boundingRect.X <= (boundingRect.Width - panelWidth) -
 ((boundingRect.Width - panelWidth) * 2)))
 {
 if ((boundingRect.Width - panelWidth) -
 ((boundingRect.Width - panelWidth) * 2) <= 0)
 {
 boundingRect.X = (boundingRect.Width - panelWidth) -
 ((boundingRect.Width - panelWidth) * 2);
 }
 else {
 boundingRect.X = 0;
 }
 }
 if (boundingRect.Y >= 0)
 {
 boundingRect.Y = 0;
 }
 elseif ((boundingRect.Y <= (boundingRect.Height - panelHeight) -
 ((boundingRect.Height - panelHeight) * 2)))
 {
 if((boundingRect.Height - panelHeight) -
 ((boundingRect.Height - panelHeight) * 2) <= 0)
 {
 boundingRect.Y = (boundingRect.Height - panelHeight) -
 ((boundingRect.Height - panelHeight) * 2);
 }
 else {
 boundingRect.Y = 0;
 }
 }
 }
 catch (Exception ex)
 {
 System.Windows.Forms.MessageBox.Show("ImageViewer error:" + ex.ToString());
 }
}

功能:ZoomToSelection() 是版本 1.2的新特性,选择要放大的区域。 这里,我们计算出与我们所传递的选择匹配的缩放的位置和数量。 我们还将 Point 变量传递给面板 PointToScreen()的X,Y 坐标:

publicvoid ZoomToSelection(Rectangle selection, Point ptPbFull)
{
 int x = (selection.X - ptPbFull.X);
 int y = (selection.Y - ptPbFull.Y);
 int width = selection.Width;
 int height = selection.Height;
 // So, where did my selection start on the entire picture?int selectedX = (int)((double)(((double)boundingRect.X -
 ((double)boundingRect.X * 2)) + (double)x)/zoom);
 int selectedY = (int)((double)(((double)boundingRect.Y -
 ((double)boundingRect.Y * 2)) + (double)y)/zoom);
 int selectedWidth = width;
 int selectedHeight = height;
 // The selection width on the scale of the Original size!if (zoom <1.0 || zoom >1.0)
 {
 selectedWidth = Convert.ToInt32((double)width/zoom);
 selectedHeight = Convert.ToInt32((double)height/zoom);
 }
 // What is the highest possible zoomrate?double zoomX = ((double)panelWidth/(double)selectedWidth);
 double zoomY = ((double)panelHeight/(double)selectedHeight);
 double newZoom = Math.Min(zoomX, zoomY);
 // Avoid Int32 crashes!if (newZoom * 100<Int32.MaxValue && newZoom * 100>Int32.MinValue)
 {
 SetZoom(newZoom);
 selectedWidth = (int)((double)selectedWidth * newZoom);
 selectedHeight = (int)((double)selectedHeight * newZoom);
 // Center the selected areaint offsetX = 0;
 int offsetY = 0;
 if (selectedWidth < panelWidth)
 {
 offsetX = (panelWidth - selectedWidth)/2;
 }
 if (selectedHeight < panelHeight)
 {
 offsetY = (panelHeight - selectedHeight)/2;
 }
 boundingRect.X = (int)((int)((double)selectedX * newZoom) -
 ((int)((double)selectedX * newZoom) * 2)) + offsetX;
 boundingRect.Y = (int)((int)((double)selectedY * newZoom) -
 ((int)((double)selectedY * newZoom) * 2)) + offsetY;
 AvoidOutOfScreen();
 }
}

功能: 拖放 ! 现在支持( 新版 1.2 ) ! 当 AllowDrop 设置为 true 时,面板将接受将文件拖放到它上面。 为此,我在 UserControl 上重载了现有的AllowDrop 属性,如下所示:

publicoverridebool AllowDrop
{
 get {
 returnbase.AllowDrop;
 }
 set {
 this.pbFull.AllowDrop = value;
 base.AllowDrop = value;
 }
}

这里没有什么特别的,我只需要确保面板与 UserControl 本身的AllowDrop 值相同。 对于实际的拖放代码:

privatevoid pbFull_DragDrop(object sender, DragEventArgs e)
{
 try {
 // Get The file(s) you dragged into an array.// (We'll just pick the first image anyway) string[] FileList = (string[])e.Data.GetData(DataFormats.FileDrop, false);
 Image newBmp = null;
 for (int f = 0; f < FileList.Length; f++)
 {
 // Make sure the file exists!if (System.IO.File.Exists(FileList[f]))
 {
 string ext = (System.IO.Path.GetExtension(FileList[f])).ToLower();
 // Checking the extensions to be Image formatsif (ext == ".jpg" || ext == ".jpeg" || ext == ".gif" ||
 ext == ".wmf" || ext == ".emf" || ext == ".bmp" ||
 ext == ".png" || ext == ".tif" || ext == ".tiff")
 {
 try {
 // Try to load it into a bitmap newBmp = Bitmap.FromFile(FileList[f]);
 this.Image = (Bitmap)newBmp;
 // If succeeded stop the loopbreak;
 }
 catch {
 // Not an image? }
 }
 }
 }
 }
 catch (Exception ex)
 {
 System.Windows.Forms.MessageBox.Show("ImageViewer error:" + ex.ToString());
 }
}privatevoid pbFull_DragEnter(object sender, DragEventArgs e)
{
 try {
 if (e.Data.GetDataPresent(DataFormats.FileDrop))
 {
 // Drop the file e.Effect = DragDropEffects.Copy;
 }
 else {
 // I'm not going to accept this unknown format! e.Effect = DragDropEffects.None;
 }
 }
 catch (Exception ex)
 {
 System.Windows.Forms.MessageBox.Show("ImageViewer error:" + ex.ToString());
 }
}

Points of Interest

这是我第二次尝试创建这些功能的ImageViewer。 我第一次尝试将 PictureBox 拖到 Panel 上。 这最初似乎工作起来很好,但不能提供更大的图像放大系统( 堆大小问题)。 这里查看器无需额外内存即可无限缩放。

已知问题

  • 如果控件没有焦点,则滚动缩放或者缩小将不起作用。 ( 单击控件或者图像即可恢复焦点)。
  • 预览图像在打开动画 *.gif 文件( 这是目的) 时不会产生动画效果。
  • 不支持使用JPEG压缩的多页面tiff图像。 Microsoft GDI+ 没有提供任何解决这里问题的解决方案。

版本记录

版本 1.5.1: ( 12,2011 )
  • 修正了 FitToScreen 函数返回错误结果的问题
    • 删除了项目中的所有 static 代码( 导致这里问题)
    • 添加了两个附加属性 PanelWidthPanelHeight

版本 1.5.0: ( 1,2011 )

  • Gif动画重新制作
    • 已经删除 ImageAnimator,并且已经实现自定义锁定系统
    • 每秒的帧数现在可以由
      GifFPS
      属性指定
  • 修正了一些 *.gif 图像在旋转 180度时不再动画的问题
  • 修正了将多页tiff图像拖放到控件上时发生的崩溃
  • 新功能: 实现了完整的滚动条支持

版本 1.4.0: ( August 15 2011 )

  • 演示项目上传。拖放现在可以在演示中再次使用。

版本 1.4.0: (,2,2011 ) 完整更改列表:

  • 实现对动画 *.gif 文件的支持

版本 1.3.5: (,21,2010 ) 完整更改列表:

  • 修正了多页TIFF旋转问题

版本 1.3.4: (,19,2010 ) 完整更改列表:

  • 固定多页面TIFF旋转
  • 通过UNC路径固定打开的图像
  • 多页面菜单的固定定位
  • 在打开非图像格式时固定双 TryCatch ( 例如 *.txt )

版本 1.3.3: ( 6,2010 ) 完整更改列表:

  • 为 emf/wmf增加图像支持( 感谢 wsmwlh ) !
  • 拖放现在也接受 emf/wmf
  • TryCatch 在多页面检查( 在WMF文件上崩溃)
  • 打开多页Tiff图像后打开非图像格式为( *.txt 例如)的固定 NullReference

版本 1.3.2: ( 5,2010 ) 完整更改列表:

  • 修正了导航面板位置的一些问题

版本 1.3.1: ( 5,2010 ) 完整更改列表:

  • 当隐藏预览面板( 感谢wsmwlh提醒我) 时,修正了不适当的导航面板位置。

版本 1.3: ( 5,2010 ) 完整更改列表:

  • 添加了多页面TIFF支持( 请求)
  • 添加了多页面导航的附加属性
  • 清除解决方案( 删除重复的图像及其引用)

代价:因为资源图像太多( 但是它不是非常漂亮),所以 (,26,2010 ) Reuploaded演示项目&源文件源文件( Did

版本 1.2: (,23,2010 ) 完整更改列表:

  • 添加了Drag-and-Drop功能( 请求)
  • 添加了放大选定区域的可能性( 选择缩放)
  • 添加了一个新按钮和快捷方式( Shift + MouseClick ),用于选择缩放
  • 添加了单页TIF支持
  • 打开只读图像时固定了只读 Bug
  • 在大量代码中添加了注释以使它的更加清晰
  • 修正了几个小 Bug

版本 1.1.1: (,14,2010 ) 完整更改列表:

  • 预览面板隐藏后在绘图中固定的Bug
  • 修正了预览面板不正确折叠的Bug

版本 1.1: (,14,2010 ) 完整更改列表:

  • 添加了单个颜色更改的添加 Color 属性
    • 背景颜色( 图片面板)
    • 预览标签颜色
    • 菜单的单独颜色可能性
  • 在处理宽图像时修复 AvoidOutOfScreen() 中的Bug
  • 优化预览图像的渲染
  • 拖动现在可能在预览面板内
  • 现在可以启用或者禁用预览面板
    • 按钮供用户控制预览面板
    • 可以强制在代码内( 请参见属性: ShowPreview & PreviewButton )
  • 现在可以在
    ComboBox
    中输入特定的数字并按Enter键来缩放
  • 添加了一些漂亮的手动&拖动光标

版本 1.0: (,7,2010 ) 首次 public 发布版本

结束语

创建这是一个真正有趣的体验,我希望大多数人将它作为你的项目的有用控件。 我相信控制工作得很好,我还相信有足够的空间来改进。 对于那些想使用它的人,也提供了源代码。 我要求,如果找到了任何 Bug 并且/或者改进了代码,这样每个人都可以享受你的工作。 感谢你阅读,也许到下一篇文章。

片尾

i Almond Almond Almond Almond Almond Almond Flicker Flicker Flicker,用 GDI+ 和 C# free自由绘画 drawing。 UserControl 使用这里技术在面板上释放自由绘制。



文章标签:图像  USE  VIEW  用户  Viewer  image-viewer  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语