关于在DataGridView中嵌入图片……
有一句英语名言——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