# [C# 開發技巧系列]C#如何實現圖片查看器
## **本專題概要**
* **一、引言**
* **二、實現思路**
* **三、實現效果**
* **四、小結**
## **一、引言**
因為最近在MSDN中的論壇和CSDN論壇都看到有些朋友問到如何用C#實現一個像Windows自帶的圖片查看器的功能等類似的問題(當然還有如何如何旋轉圖片的,如何通過按鈕來變換圖片的功能等),所以為了幫助大家更好地解決類似的這樣的問題,所以這篇文章將簡單介紹下如何使用C#來實現一個圖片查看器的功能的,該工具保存的功能有:
1. **可以通過“上一張” “下一張”這樣的按鈕來輪換瀏覽圖片**
2. **實現對圖片的旋轉**
3. **實現對旋轉后圖片的保存功能。本程序不僅提供旋轉90/180/270這樣的實現,同時提供一個方法來完成旋轉任意角度的實現**
4. **該程序未實現Windows圖片查看圖片縮放的功能,這部分的功能主要要點是改變圖片在PictureBox控件中的高度和寬度就可以的**
## **二、實現思路**
## **2.1 圖片輪換瀏覽功能的實現**
首先分析下第一個功能點的實現,要實現圖片的輪換瀏覽,我們可以根據下面的思路來實現:
* 第一步、獲得目錄下所有圖片的集合,此時使用**Directory.GetFiles()**來獲得目錄下所有文件,然后再對該集合進行篩選,篩選出是圖片的文件,代碼用擴展名進行篩選的
* 第二步、獲得所有圖片集合之后,實現圖片輪換就需要改變這個集合的索引就可以實現上一張和下一張的功能了
* 第三步、需要考慮到最后一張或者第一張的情況下,再點擊下一張或上一張圖片來輪換成第一張或最后一張
思路就是上面的,有了上面的思路之后,就讓我們看看具體的代碼來對照理解下:
```
**// 第一步** // 獲得預覽圖片文件路徑下的圖片集合
public static List<string> GetImgCollection(string path)
{
string[] imgarray = Directory.GetFiles(path);
var result = from imgstring in imgarray
where imgstring.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
imgstring.EndsWith("png", StringComparison.OrdinalIgnoreCase)||
imgstring.EndsWith("bmp", StringComparison.OrdinalIgnoreCase)
select imgstring;
return result.ToList();
}
**// 第二步** // 獲得打開圖片在圖片集合中的索引
private int GetIndex(string imagepath)
{
int index = 0;
for (int i = 0; i < imgArray.Count; i++)
{
if (imgArray[i].Equals(imagepath))
{
index = i;
break;
}
}
return index;
}
// 切換圖片的方法
private void SwitchImg(int index)
{
newbitmap = Image.FromFile(imgArray[index]);
picBoxView.Image = newbitmap;
imgPath = imgArray[index];
}
**// 第三步** // 上一張圖片
private void btnPre_Click(object sender, EventArgs e)
{
int index = GetIndex(imgPath);
// 釋放上一張圖片的資源,避免保存的時候出現ExternalException異常
newbitmap.Dispose();
if (index == 0)
{
SwitchImg(imgArray.Count - 1);
}
else
{
SwitchImg(index - 1);
}
}
// 下一張圖片
private void btnNext_Click(object sender, EventArgs e)
{
int index = GetIndex(imgPath);
// 釋放上一張圖片的資源,避免保存的時候出現ExternalException異常
// 經常在調用Save方法的時候都會出現 一個GDI一般性錯誤,主要原因是文件沒有被釋放,當保存到原位置時,就會出現該異常,要避免這個錯誤就要釋放圖片占有的資源
newbitmap.Dispose();
if (index != imgArray.Count - 1)
{
SwitchImg(index + 1);
}
else
{
SwitchImg(0);
}
}
```
## 2.2 圖片旋轉功能的實現
上面的代碼實現了第一個功能點的問題了,下面就解釋下如何實現第二個功能點——圖片旋轉的問題:
對于Windows自帶的圖片查看器,它旋轉的角度只能順時針旋轉90或逆時針旋轉90度,這個功能實現起來可以說非常簡單,只需要使用**Image.RotateFlip(RotateFlipType)**方法就可以完成的,有些朋友也想對圖片實現旋轉任意角度,對于這個問題源碼中也有具體的實現,大家可以從文章的最后下載源碼進行查看,這里就不貼出具體代碼的,下面就看看如何實現Windows自帶的圖片查看器的旋轉功能的代碼:
```
// 順時針旋轉90度旋轉圖片
private void btnRotate_Click(object sender, EventArgs e)
{
picBoxView.SizeMode = PictureBoxSizeMode.Zoom;
// 順時針旋轉90度的另外一種實現
newbitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
picBoxView.Image = newbitmap;
isRotate = true;
//newbitmap = (Image)ImageManager.RotateImg(bitmap, 90f, Color.Transparent); ;
//bitmap.Dispose();
//picBoxView.Image = newbitmap;
}
// 逆時針旋轉90度
private void btncounterclockwiseRotate_Click(object sender, EventArgs e)
{
picBoxView.SizeMode = PictureBoxSizeMode.Zoom;
// 逆時針旋轉90度的另外實現
newbitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
picBoxView.Image = newbitmap;
isRotate = true;
// 下面是旋轉任意角度的代碼
//newbitmap = (Image)ImageManager.RotateImg(bitmap, 360f-90f, Color.Transparent); ;
//bitmap.Dispose();
//picBoxView.Image = newbitmap;
}
```
## 2.3 對旋轉圖片的保存功能的實現
最后就是針對旋轉圖片保存的實現了,此時我參考了Windows自帶圖片查看器的實現方式,因為我用Windows自帶圖片查看器瀏覽圖片的實現,當我旋轉圖片時,它并不是實時地保存到旋轉的圖片的,而是當我關閉Windows自帶圖片查看器的時候,旋轉的圖片才保存到文件中的,有了這個思路之后,我就把我保存的代碼邏輯放在窗體的關閉的事件處理程序中來實現的,此時保存的功能我們只需要調用**Image.Save(path)**方法就可以完成對圖片的保存,下面就看看具體代碼的實現的:
```
// 關閉窗體后保存旋轉后的圖片到文件中
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (imgPath == null || isRotate == false)
{
return;
}
// 保存旋轉后的圖片
switch (Path.GetExtension(imgPath).ToLower())
{
case ".png":
newbitmap.Save(imgPath, ImageFormat.Png);
newbitmap.Dispose();
break;
case ".jpg":
newbitmap.Save(imgPath);
newbitmap.Dispose();
break;
default:
newbitmap.Save(imgPath, ImageFormat.Bmp);
newbitmap.Dispose();
break;
}
}
```
## **三、實現效果**
上面已經介紹了實現該程序的一個思路的,朋友是不是迫不及待的想看到到底自定義圖片查看器是什么樣子的呢?下面就通過一個動畫來讓大家更形象地看到程序的運行效果的:

## 四、小結
到這里該文章的內容就介紹結束了,希望大家如果遇到類似的問題可以很快從這篇博客中得到解決,另外附帶下MSDN中這個問題的鏈接:
[http://social.msdn.microsoft.com/Forums/zh-CN/visualcshartzhchs/thread/89d09d59-ab82-4e41-896f-daab68edbd10](http://social.msdn.microsoft.com/Forums/zh-CN/visualcshartzhchs/thread/89d09d59-ab82-4e41-896f-daab68edbd10)
本專題源碼下載:[圖片查看器](http://code.msdn.microsoft.com/C-7a732fd7/file/83153/2/C%23%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%9F%A5%E7%9C%8B%E5%99%A8.zip)
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結