Show image in tooltip of datagridview - c#

Background: I am working with winforms in c#. I do not want images to be shown in datagridview cells, i have stored only path in database and showing them in datagridview from database.
Problem: When user enters a cell a tooltip is poped-up. What I need is that when column index of current-cell is 2 then the tool tip should show an image from the path given in the current cell.
I have found This Article very good. But unable to get succeeded. I have following code
void CustomizedToolTip_Popup(object sender, PopupEventArgs e)
{
DataGridView parent = e.AssociatedControl as DataGridView;
if (parent.CurrentCell != null)
{
if (parent.CurrentCell.ColumnIndex == 2)
{
Bitmap bmpIn = new Bitmap(parent.CurrentCell.Value + "");
using (Graphics g = Graphics.FromImage(bmpIn))
{
Rectangle mr = new Rectangle(5, 5, 50, 50);
mr.Location = new Point(5, 5);
g.PageUnit = GraphicsUnit.Pixel;
g.DrawImage(bmpIn, mr);
}
}
}
}
I thought this code should draw image, but it is not drawing, and more than drawing I am uncertain about the location, even If I could draw, how to locate it in tootip. I have not been able to understand it from the article i mentioned. Below is the image of my datagridview.

I did something similar for a project.
Instead, I just used a form which I set to open on CellMouseOver, close on CellMouseLeave
frm_MouseOverPicture HoverZoom = new frm_MouseOverPicture();
private void dgv_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv_sender = sender as DataGridView;
DataGridViewCell dgv_MouseOverCell = dgv_sender.Rows[e.RowIndex].Cells[e.ColumnIndex];
//Get FilePath from dgv_MouseOverCell content
//Get x, y based on position relative to edge of screen
//x, y = top left point of HoverZoom form
HoverZoom.LoadPicture(FilePath);
HoverZoom.Location = new System.Drawing.Point(x, y);
HoverZoom.Show();
}
private void dgv_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
HoverZoom.Hide();
HoverZoom.ClearPicture();
}
Hope that is close enough to what you are looking for. I just made the form with no border and put a picture box over the whole thing.

Just add a picturebox to your form and set size mode to "StretchImage" and visible= false, then add the following events to your datagridview
private void metroGrid1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv_sender = sender as DataGridView;
DataGridViewCell dgv_MouseOverCell=null;
if (e.RowIndex > 0 && e.ColumnIndex > 0 && e.RowIndex <dgv_sender.RowCount && e.ColumnIndex<dgv_sender.ColumnCount)
{
dgv_MouseOverCell = dgv_sender.Rows[e.RowIndex].Cells[e.ColumnIndex];
}
if(dgv_MouseOverCell !=null)
if (e.ColumnIndex == 4) {
if (dgv_MouseOverCell.Value != null)
{
if (File.Exists(dgv_MouseOverCell.Value.ToString()))
{
Image img = Image.FromFile(dgv_MouseOverCell.Value.ToString());
pictureBox1.ImageLocation = dgv_MouseOverCell.Value.ToString();
pictureBox1.Location = new System.Drawing.Point(Cursor.Position.X - this.Location.X, Cursor.Position.Y - this.Location.Y);
pictureBox1.Visible = true;
}
}
}
}
private void metroGrid1_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
pictureBox1.Visible = false;
}
Click here to view image

void CustomizedToolTip_Popup(object sender, PopupEventArgs e)
{
DataGridView parent = e.AssociatedControl as DataGridView;
if (parent.CurrentCell != null)
{
if (parent.CurrentCell.ColumnIndex == 2)
{
string path = parent.CurrentCell.Value.ToString();
using (System.Drawing.Imaging.Metafile emf = new System.Drawing.Imaging.Metafile(path))
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(emf.Width, emf.Height))
{
bmp.SetResolution(emf.HorizontalResolution, emf.VerticalResolution);
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
{
g.DrawImage(emf,
new Rectangle(0, 0, emf.Width, emf.Height),
new Rectangle(0, 0, emf.Width, emf.Height),
GraphicsUnit.Pixel
);
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
}
}
}
}
Source
To use the System.Windows.Interop
"You have to download the .NET 3.0 runtime or later and install it to get
the assembly..."
"After .NET 3.0 is installed, it should appear in the Add References
list with component name as "WindowsBase". If it doesn't you can
always add it from the Browse tab in the Add References dialog.
(C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0 on my
box)"
Source

Related

How to get a DataGridView Row as Bitmap for a cursor icon?

I'm trying to get a DataGridViews row as Bitmap to use it as a cursors icon.
Sadly the DataGridViewRow object has no DrawToBitmap method.
I managed to get the bound of the row (RowRect) and get a Bitmap of the whole DataGridView (bmp). I think I next need to cut the row from the bitmap, but I have no idea how to do that.
Here is my starting code:
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
if (dataGridView1.SelectedRows.Count == 1)
{
if (e.Button == MouseButtons.Left)
{
rw = dataGridView1.SelectedRows[0];
Rectangle RowRect = dataGridView1.GetRowDisplayRectangle(rw.Index, true);
Bitmap bmp = new Bitmap(RowRect.Width, RowRect.Height);
dataGridView1.DrawToBitmap(bmp, new Rectangle(Point.Empty, bmp.Size));
Cursor cur = new Cursor(bmp.GetHicon());
Cursor.Current = cur;
rowIndexFromMouseDown = dataGridView1.SelectedRows[0].Index;
dataGridView1.DoDragDrop(rw, DragDropEffects.Move);
}
}
}
You need to grab the whole clientarea content first (your bitmap is too small!), then cut out the row rectangle. Also make sure to dispose of the created resources!
This should work:
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
if (dataGridView1.SelectedRows.Count == 1)
{
if (e.Button == MouseButtons.Left)
{
Size dgvSz = dataGridView1.ClientSize;
int rw = dataGridView1.SelectedRows[0].Index;
Rectangle RowRect = dataGridView1.GetRowDisplayRectangle(rw, true);
using (Bitmap bmpDgv = new Bitmap(dgvSz.Width, dgvSz.Height))
using (Bitmap bmpRow = new Bitmap(RowRect.Width, RowRect.Height))
{
dataGridView1.DrawToBitmap(bmpDgv , new Rectangle(Point.Empty, dgvSz));
using ( Graphics G = Graphics.FromImage(bmpRow ))
G.DrawImage(bmpDgv , new Rectangle(Point.Empty,
RowRect.Size), RowRect, GraphicsUnit.Pixel);
Cursor.Current.Dispose(); // not quite sure if this is needed
Cursor cur = new Cursor(bmpRow .GetHicon());
Cursor.Current = cur;
rowIndexFromMouseDown = dataGridView1.SelectedRows[0].Index;
dataGridView1.DoDragDrop(rw, DragDropEffects.Move);
}
}
}
}

Make selected DataGridView Cell content larger

I have a datagridview which is coding in c#.net
My requirement is, if I select any DataGridView cell the cell content should be visible larger as a popup, or I want to view the datagridview cell larger or fitted one when move my cursor to particular cell.
For text and numerics this may do what you need:
private void dataGridView1_CellPainting(object sender,
DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex < 0 | e.ColumnIndex < 0) return;
if (dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Selected)
{
e.Graphics.FillRectangle(SystemBrushes.Highlight, e.CellBounds);
e.Graphics.DrawString(e.Value.ToString(), new Font(e.CellStyle.Font.FontFamily,
e.CellStyle.Font.Size * 1.5f), SystemBrushes.HighlightText, e.CellBounds.Location);
e.Handled = true;
}
}
You may need to use e.FormattedValue instead of e.Value if you are using formats
You may also need to insert a test of the cell value's type..
This code this will enlarge the font by 50% while not in edit mode.
For images a different solution would be necessary - probably a popup Label or Panel; but that really depends on just what you want and what kind of images they are.. Icons I would leave alone, photos of users would profit from the enlarged display.
Of course, if the enlarged content doesn't actually fit in the Cell the popup solution would also be called for..
Upadate
Here is an extension which tests the Column Value/FormattedValue and for a Bitmap displays the Image in a popup Label:
Label imageLabel;
bool labelHide = false; //*** new
void showImageLabel(DataGridViewCellPaintingEventArgs e)
{
if (labelHide) return; //*** new
if (imageLabel == null) imageLabel = new Label();
imageLabel.Click += (sender, evt) =>
{ ((Label)sender).Hide(); labelHide = true; }; //*** new
imageLabel.Text = "";
imageLabel.Parent = dataGridView1;
imageLabel.Location = e.CellBounds.Location;
if (imageLabel.Image != null) imageLabel.Image.Dispose();
//Size size = ((Bitmap)e.Value).Size; //*** old
Size size = ((Bitmap)e.FormattedValue).Size; //*** new
Size newSize = new Size( (int)(size.Width * 1.5f), (int)(size.Height * 1.5f));
//Bitmap bmp = new Bitmap((Bitmap)e.Value, newSize); //*** old
Bitmap bmp = new Bitmap((Bitmap)e.FormattedValue, newSize); //*** new
imageLabel.Size = newSize;
imageLabel.Image = bmp;
imageLabel.Show();
}
private void dataGridView1_CellPainting(object sender,
DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex < 0 | e.ColumnIndex < 0) return;
if (e.Value == null) { if (imageLabel != null) imageLabel.Hide(); return; }
if (dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Selected)
{
//if (e.Value.GetType() == typeof(Bitmap)) //*** old
if (e.FormattedValue.GetType() == typeof(Bitmap)) //*** new
{
showImageLabel(e);
e.Handled = true; //*** old
if (labelHide) labelHide = false; else e.Handled = true; //*** new
return;
}
else if (imageLabel != null) imageLabel.Hide();
e.Graphics.FillRectangle(SystemBrushes.Highlight, e.CellBounds);
e.Graphics.DrawString(e.FormattedValue.ToString(),
new Font(e.CellStyle.Font.FontFamily, e.CellStyle.Font.Size * 1.5f),
SystemBrushes.HighlightText, e.CellBounds.Location);
e.Handled = true;
}
}
private void dataGridView1_RowLeave(object sender, DataGridViewCellEventArgs e)
{
if (imageLabel != null) imageLabel.Hide();
}
You may want to adapt the Location to be e.g. centered..
Update 2
I have now adapted the code to the case of retrieving Images directly as byte[] from a Database. In this case the Type of the Value Property is not Image. Instead one simlpy needs to check the FormattedValue.
If the displayed, resized Image is too large, it may cover the whole Cell and the Cell_Painting event won't get triggered. Therefore I have also added an one-liner for the RowLeave event to prevent that.
I have also added a few lines to allow the image to be clicked away.
Please change the lines I marked with //*** , add the event and check if it now works for you!
Here are two Screenshots:

Changing all cell border widths in DataGridViews

I have a data grid view and I want to make all of the cell borders thicker. Is there a property you can do this with?
You need to do a little custom painting by adding code to the CellPainting event handler. For setting the cells border to None, use CellBorderStyle:
dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None;
// CellPainting event handler for your dataGridView1
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == -1 && e.ColumnIndex > -1)
{
e.Handled = true;
using (Brush b = new SolidBrush(dataGridView1.DefaultCellStyle.BackColor))
{
e.Graphics.FillRectangle(b, e.CellBounds);
}
using (Pen p = new Pen(Brushes.Black))
{
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawLine(p, new Point(0, e.CellBounds.Bottom-1), new Point(e.CellBounds.Right, e.CellBounds.Bottom-1));
}
e.PaintContent(e.ClipBounds);
}
}

Panel and ScrollBar in Windows forms

I am using a panel to display a image in windows forms
I am drawing image in the panel in Panel_Paint event as follows:
Graphics g = panel1.CreateGraphics();
Image im = new Bitmap(#"../../Data/#3_Page2.PNG");
g.DrawImage(im,new Point(10,10));
Now, the image is drawn as i expected, with some part of the bottom of the image not displaying as its height is greater than forms height.
I have added the VScrollBar now. How do i make that panel to view the rest of the image with the help of VScrollBar.
You can use PictureBox with SizeMode set to AutoSize and AutoScroll property of Panel. That way the panel should add scrollbars if they are needed.
PictureBox pictureBox = new System.Windows.Forms.PictureBox();
pictureBox.Image = new Bitmap(#"../../Data/#3_Page2.PNG");
pictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
panel.AutoScroll = true;
panel.Controls.Add(this.pictureBox);
This solution works, however if your image is large enough, there is a little flicker when you scroll (however it's acceptable). First you have to add a VScrollBar right on the right of your panel and a HScrollBar right under the bottom of your panel. This demo requires you have a VScrollBar named vScrollBar1 and HScrollBar named hScrollBar1, a Button named buttonOpenImage to allow user to open some image, a Panel named panel1 used as the main area to draw the image:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//To prevent/eliminate flicker, do this
typeof(Panel).GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).SetValue(panel1, true, null);
//------------------------
UpdateScrollbarsState();
}
int imgWidth, imgHeight;
Image image;
Point leftTop = Point.Empty;
int lastV, lastH;
private void OpenImage(){
OpenFileDialog openFile = new OpenFileDialog();
openFile.FileOk += (s, e) => {
try {
image = Image.FromFile(openFile.FileName);
//calculate the physical size of the image based on the resolution and its logical size.
//We have to do this because the DrawImage is based on the physical size (not logical).
imgWidth = (int)(image.Width * 96f / image.HorizontalResolution + 0.5);
imgHeight = (int)(image.Height * 96f / image.VerticalResolution + 0.5);
lastV = lastH = 0;
UpdateScrollbarsState();
vScrollBar1.Value = 0;
hScrollBar1.Value = 0;
panel1.Invalidate();
}
catch {
image = null;
MessageBox.Show("Image file is invalid or corrupted!");
}
};
openFile.ShowDialog();
}
private void UpdateScrollbarsState() {
//We have to update all the info about Minimum and Maximum
vScrollBar1.Minimum = 0;
hScrollBar1.Minimum = 0;
vScrollBar1.Maximum = Math.Max(imgHeight-panel1.Height,0);
hScrollBar1.Maximum = Math.Max(imgWidth-panel1.Width,0);
vScrollBar1.Visible = vScrollBar1.Maximum > 0;
hScrollBar1.Visible = hScrollBar1.Maximum > 0;
if (vScrollBar1.Maximum == 0) {
leftTop.Y = 0;
lastV = 0;
}
if (hScrollBar1.Maximum == 0) {
leftTop.X = 0;
lastH = 0;
}
}
private void panel1_Paint(object sender, PaintEventArgs e) {
if (image == null) return;
e.Graphics.DrawImage(image, leftTop);
}
//The ValueChanged event handler of your vScrollBar1
private void vScrollBar1_ValueChanged(object sender, EventArgs e) {
if (!vScrollBar1.Visible) return;
leftTop.Offset(0, -vScrollBar1.Value + lastV);
lastV = vScrollBar1.Value;
panel1.Invalidate();
}
//The ValueChanged event handler of your hScrollBar1
private void hScrollBar1_ValueChanged(object sender, EventArgs e) {
if (!hScrollBar1.Visible) return;
leftTop.Offset(lastH - hScrollBar1.Value, 0);
lastH = hScrollBar1.Value;
panel1.Invalidate();
}
//handler for SizeChanged event of the panel. However if resizing
//the form causes the panel's size changing, you should attach this
//handler for form.
private void panel1_SizeChanged(object sender, EventArgs e) {
UpdateScrollbarsState();
}
//handler for the Click event of a button (click to open an image)
private void buttonOpenImage_Click(object sender, EventArgs e) {
OpenImage();
}
}

Vertical text in datagridview

I want to show the text in the header cells in vertical orientation. How can I do it?
Thanks
You can achieve the result you want using custom cell painting for the header.
In answer to your comment asking for a way to align the text with the bottom of the cell, I've added comments to my code. They are hopefully clear.
You need the following code (say in the Form_Load after initializing components)
dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
dataGridView1.ColumnHeadersHeight = 50;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader;
// Here we attach an event handler to the cell painting event
dataGridView1.CellPainting += new DataGridViewCellPaintingEventHandler(dataGridView1_CellPainting);
Next you need something like the following code:
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
// check that we are in a header cell!
if (e.RowIndex == -1 && e.ColumnIndex >= 0)
{
e.PaintBackground(e.ClipBounds, true);
Rectangle rect = this.dataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, true);
Size titleSize = TextRenderer.MeasureText(e.Value.ToString(), e.CellStyle.Font);
if (this.dataGridView1.ColumnHeadersHeight < titleSize.Width)
{
this.dataGridView1.ColumnHeadersHeight = titleSize.Width;
}
e.Graphics.TranslateTransform(0, titleSize.Width);
e.Graphics.RotateTransform(-90.0F);
// This is the key line for bottom alignment - we adjust the PointF based on the
// ColumnHeadersHeight minus the current text width. ColumnHeadersHeight is the
// maximum of all the columns since we paint cells twice - though this fact
// may not be true in all usages!
e.Graphics.DrawString(e.Value.ToString(), this.Font, Brushes.Black, new PointF(rect.Y - (dataGridView1.ColumnHeadersHeight - titleSize.Width) , rect.X));
// The old line for comparison
//e.Graphics.DrawString(e.Value.ToString(), this.Font, Brushes.Black, new PointF(rect.Y, rect.X));
e.Graphics.RotateTransform(90.0F);
e.Graphics.TranslateTransform(0, -titleSize.Width);
e.Handled = true;
}
}
A simpler and more effective renderer
Attach event either through designer, or with this line of code
dataGridView1.CellPainting += new DataGridView1_CellPainting(dataGridView1_CellPainting);
Event handler to draw rotated text
private void DataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) {
// Vertical text from column 0, or adjust below, if first column(s) to be skipped
if (e.RowIndex == -1 && e.ColumnIndex >= 0) {
e.PaintBackground(e.CellBounds, true);
e.Graphics.TranslateTransform(e.CellBounds.Left , e.CellBounds.Bottom);
e.Graphics.RotateTransform(270);
e.Graphics.DrawString(e.FormattedValue.ToString(),e.CellStyle.Font,Brushes.Black,5,5);
e.Graphics.ResetTransform();
e.Handled = true;
}
}
DataGrid d = new DataGrid();
d.Columns[0].HeaderStyle.VerticalAlign = VerticalAlign.Bottom;
If you are looking for gridview then you case like this :-
GridView gv = new GridView ();
gv.Columns[0].ItemStyle.VerticalAlign = VerticalAlign.Bottom;
If you are looking for datagridview then you case like this :-
Create an object of datagridview.
gv.Columns["ColumnName"].HeaderCell.Style.Alignment = DataGridViewContentAlignment.BottomCenter;
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == -1 && e.ColumnIndex >= 0)
{
e.PaintBackground(e.ClipBounds, true);
Rectangle rect =
this.dataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, true);
Size titleSize =
TextRenderer.MeasureText(e.Value.ToString(), e.CellStyle.Font);
if (this.dataGridView1.ColumnHeadersHeight <
titleSize.Width)
this.dataGridView1.ColumnHeadersHeight =
titleSize.Width;
e.Graphics.TranslateTransform(0, titleSize.Width);
e.Graphics.RotateTransform(-90.0F);
e.Graphics.DrawString(e.Value.ToString(), this.Font,
Brushes.Orange, new PointF(rect.Y, rect.X));
e.Graphics.RotateTransform(90.0F);
e.Graphics.TranslateTransform(0, -titleSize.Width);
e.Handled = true;
}
}
In addition, you could set the AutoSizeColumnsMode property of the
DataGridView to AllCellsExceptHeader in order to make the DataGridView
compact.

Categories