有一句英語名言——Picutures speak louder than words(圖片勝於千言萬語)。可見在描述一些細節問題,或者是用語言文字難以讓人產生“身臨其境”的時候,圖片就發揮了其作用。在WinForm中,DataGridView不僅僅是用於顯示文字、我們還可以顯示圖片。本章就和大伙兒討論DataGridView中嵌入顯示圖片的問題。
一、嵌入式顯示:
所謂“嵌入式”,就是說把圖片單獨存放在某個DataTable的字段中(該字段為byte[]類型)然后綁定到對應DataGridView的指定單元格中顯示出來,代碼大致如下:
[C#]
public partial class Form1 : Form
{
DataGridView dgv = null;
public Form1()
{
InitializeComponent();
//初始化動態產生數據表等字段
dgv = new DataGridView();
dgv.Parent = this;
dgv.Dock = DockStyle.Fill;
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Image", typeof(byte[]));
dt.Rows.Add("Test1", File.ReadAllBytes("d:\\test.jpg"));
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dgv.DataSource = dt;
}
}
[VB.NET]
Public Partial Class Form1
Inherits Form
Private dgv As DataGridView = Nothing
Public Sub New()
InitializeComponent()
'初始化動態產生數據表等字段
dgv = New DataGridView()
dgv.Parent = Me
dgv.Dock = DockStyle.Fill
Dim dt As New DataTable()
dt.Columns.Add("Name", GetType(String))
dt.Columns.Add("Image", GetType(Byte()))
dt.Rows.Add("Test1", File.ReadAllBytes("d:\test.jpg"))
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
dgv.DataSource = dt
End Sub
End Class
這樣做的好處在於:由於你的圖片文件被嵌入到了數據庫中(這里是模擬數據庫的效果,真實情況下你應該使用DataAdapter.Fill填充到DataTable中方可)。這樣不存在圖片丟失等情況;當然數據庫的容量也大大增加。
二、連接式圖片顯示:
這是我給起的名字,就是類似我“關於資源文件”中那個“Link when compile"效果差不多——只在動態綁定的時候根據一定規律(如存儲在數據庫中的絕對或者相對路徑)順序加載圖片,這樣圖片和數據庫必然是分離的。不過拷貝的時候注意路徑等問題。示例代碼如下:
[C#]
public partial class Form1 : Form
{
DataGridView dgv = null;
public Form1()
{
InitializeComponent();
//初始化動態產生數據表等字段
dgv = new DataGridView();
dgv.Parent = this;
dgv.Dock = DockStyle.Fill;
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Image", typeof(string));
dt.Rows.Add("Test1", "d:\\test.jpg");
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dgv.Columns.Add(new DataGridViewTextBoxColumn() { HeaderText = "Name", DataPropertyName = "Name" });
dgv.Columns.Add(new DataGridViewImageColumn() { HeaderText = "Image", DataPropertyName = "Image" });
dgv.CellFormatting += new DataGridViewCellFormattingEventHandler(dgv_CellFormatting);
dgv.DataSource = dt;
}
void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex==1 && e.RowIndex!=dgv.NewRowIndex)
{
e.Value = File.ReadAllBytes(dgv.Rows[e.RowIndex].Cells[1].Value.ToString());
}
}
}
[VB.NET]
Public Partial Class Form1
Inherits Form
Private dgv As DataGridView = Nothing
Public Sub New()
InitializeComponent()
'初始化動態產生數據表等字段
dgv = New DataGridView()
dgv.Parent = Me
dgv.Dock = DockStyle.Fill
Dim dt As New DataTable()
dt.Columns.Add("Name", GetType(String))
dt.Columns.Add("Image", GetType(String))
dt.Rows.Add("Test1", "d:\test.jpg")
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
dgv.Columns.Add(New DataGridViewTextBoxColumn() With { _
Key .HeaderText = "Name", _
Key .DataPropertyName = "Name" _
})
dgv.Columns.Add(New DataGridViewImageColumn() With { _
Key .HeaderText = "Image", _
Key .DataPropertyName = "Image" _
})
AddHandler dgv.CellFormatting, AddressOf New DataGridViewCellFormattingEventHandler(AddressOf dgv_CellFormatting)
dgv.DataSource = dt
End Sub
Private Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs)
If e.ColumnIndex = 1 AndAlso e.RowIndex <> dgv.NewRowIndex Then
e.Value = File.ReadAllBytes(dgv.Rows(e.RowIndex).Cells(1).Value.ToString())
End If
End Sub
End Class
以上代碼和第一種”嵌入式“差不多,不過還請注意一下重要差別:
1)自定義了各個Columns,而不是自動生成(這是因為DataTable中都是string類型,包括存儲圖片的路徑,如果單用自動生成的話則無法生成Image類型的列)。
2)人為使用了CellFormatting事件,該事件在呈現整個DataGridView的時候被出發。此時該事件的e的RowIndex將從0~最后一行(實際在DataTable中不存在),ColumnIndex從0~最后一列逐個遍歷。如If中判斷,其中e.ColumnIndex=1表示第一列,e.RowIndex不等於dgv.NewRowIndex表示不等於最后一行。同時給e.Value(實際單元格中內容)直接賦予其byte數組即可。
附帶說明一下:你把DataGridView的AllowUserToAddRows設置為False,那么“e.RowIndex <> dgv.NewRowIndex”完全可以省略。
有興趣的話,可以嘗試結合我“關於資源文件”的方法,先把圖片嵌入到資源文件中,然后在DataTable中某列添加各個對應資源文件的相對路徑和名稱,然后動態加載。
三、圖片大小調整:
有時我們喜歡統一把圖片“按比例縮放”,不喜歡很大或者很小的圖片——怎么辦?我們可以采取把圖片的byte數組讀入一個Memory中,然后加載到Image,同時創建新的BitMap(利用其構造函數定義統一的Size),然后存儲到另外一個MemoryStream中並且使用其ToArray方法與DataGridView進行綁定。
對於“嵌入式”,示例代碼如下:
[C#]
public partial class Form1 : Form
{
DataGridView dgv = null;
public Form1()
{
InitializeComponent();
//初始化動態產生數據表等字段
dgv = new DataGridView();
dgv.Parent = this;
dgv.Dock = DockStyle.Fill;
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Image", typeof(byte[]));
dt.Rows.Add("Test1", File.ReadAllBytes("d:\\test.jpg"));
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dgv.CellFormatting += new DataGridViewCellFormattingEventHandler(dgv_CellFormatting);
dgv.AllowUserToAddRows = false;
dgv.DataSource = dt;
}
void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 1)
{
using (MemoryStream oldms = new MemoryStream((byte[])e.Value))
{
Image img = Image.FromStream(oldms);
Bitmap bt = new Bitmap(img,new Size(100, 100));
using (MemoryStream newms = new MemoryStream())
{
bt.Save(newms,ImageFormat.Jpeg);
e.Value = newms.ToArray();
}
}
}
}
}
[VB.NET]
Public Partial Class Form1
Inherits Form
Private dgv As DataGridView = Nothing
Public Sub New()
InitializeComponent()
'初始化動態產生數據表等字段
dgv = New DataGridView()
dgv.Parent = Me
dgv.Dock = DockStyle.Fill
Dim dt As New DataTable()
dt.Columns.Add("Name", GetType(String))
dt.Columns.Add("Image", GetType(Byte()))
dt.Rows.Add("Test1", File.ReadAllBytes("d:\test.jpg"))
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
AddHandler dgv.CellFormatting,AddressOf New DataGridViewCellFormattingEventHandler(AddressOf dgv_CellFormatting)
dgv.AllowUserToAddRows = False
dgv.DataSource = dt
End Sub
Private Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs)
If e.ColumnIndex = 1 Then
Using oldms As New MemoryStream(DirectCast(e.Value, Byte()))
Dim img As Image = Image.FromStream(oldms)
Dim bt As New Bitmap(img, New Size(100, 100))
Using newms As New MemoryStream()
bt.Save(newms, ImageFormat.Jpeg)
e.Value = newms.ToArray()
End Using
End Using
End If
End Sub
End Class
注意我還是沿用了CellFormatting事件,並且請觀察該事件內部代碼。如果是“連接式”的,只需更改第二列的類型,並且在CellFormatting中讀取該列存儲的圖片路徑,轉化為byte即可。
[C#]
public partial class Form1 : Form
{
DataGridView dgv = null;
public Form1()
{
InitializeComponent();
//初始化動態產生數據表等字段
dgv = new DataGridView();
dgv.Parent = this;
dgv.Dock = DockStyle.Fill;
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Image", typeof(string));
dt.Rows.Add("Test1", "d:\\test.jpg");
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dgv.Columns.Add(new DataGridViewTextBoxColumn() { HeaderText = "Name", DataPropertyName = "Name" });
dgv.Columns.Add(new DataGridViewImageColumn() { HeaderText = "Image", DataPropertyName = "Image" });
dgv.CellFormatting += new DataGridViewCellFormattingEventHandler(dgv_CellFormatting);
dgv.AllowUserToAddRows = false;
dgv.DataSource = dt;
}
void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 1)
{
byte[] bytes = File.ReadAllBytes(e.Value.ToString());
using (MemoryStream oldms = new MemoryStream(bytes))
{
Image img = Image.FromStream(oldms);
Bitmap bt = new Bitmap(img,new Size(100, 100));
using (MemoryStream newms = new MemoryStream())
{
bt.Save(newms,ImageFormat.Jpeg);
e.Value = newms.ToArray();
}
}
}
}
}
[VB.NET]
Public Partial Class Form1
Inherits Form
Private dgv As DataGridView = Nothing
Public Sub New()
InitializeComponent()
'初始化動態產生數據表等字段
dgv = New DataGridView()
dgv.Parent = Me
dgv.Dock = DockStyle.Fill
Dim dt As New DataTable()
dt.Columns.Add("Name", GetType(String))
dt.Columns.Add("Image", GetType(String))
dt.Rows.Add("Test1", "d:\test.jpg")
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
dgv.Columns.Add(New DataGridViewTextBoxColumn() With { _
Key .HeaderText = "Name", _
Key .DataPropertyName = "Name" _
})
dgv.Columns.Add(New DataGridViewImageColumn() With { _
Key .HeaderText = "Image", _
Key .DataPropertyName = "Image" _
})
AddHandler dgv.CellFormatting,AddressOf New DataGridViewCellFormattingEventHandler(AddressOf dgv_CellFormatting)
dgv.AllowUserToAddRows = False
dgv.DataSource = dt
End Sub
Private Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs)
If e.ColumnIndex = 1 Then
Dim bytes As Byte() = File.ReadAllBytes(e.Value.ToString())
Using oldms As New MemoryStream(bytes)
Dim img As Image = Image.FromStream(oldms)
Dim bt As New Bitmap(img, New Size(100, 100))
Using newms As New MemoryStream()
bt.Save(newms, ImageFormat.Jpeg)
e.Value = newms.ToArray()
End Using
End Using
End If
End Sub
End Class