Datagridview cell with background image - c#

I have created a DataGridView, one of the cells (DataGridViewTextBoxCell) I would like to have a background image. To do this I have used the following on the CellPainting event.
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
var image = Resources.item_qty_white;
e.PaintBackground(e.ClipBounds, false);
e.Graphics.DrawImageUnscaled(image, 1410, e.CellBounds.Top);
}
This works well and the image is on ever row in the position I want. However, the cell it is sitting in has a DataGridViewTextBoxCell with a numeric value. The image floats on top of this value and thus is hidden. I guess the ideal solution would be to make the DataGridViewTextBoxCell be "TopMost" but I couldn't figure out how to do this.
I then decided to try and make the background image partial transparent so the value underneath would be visual, so I changed my CellPainting code to below.
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
var image = Resources.item_qty_white;
e.PaintBackground(e.ClipBounds, false);
image.MakeTransparent(Color.White);
e.Graphics.DrawImageUnscaled(image, 1410, e.CellBounds.Top);
}
This again worked and I could see the value with the background image surrounding it as I would like. However the next issue arise when I tried to update the value of the cell. Once I did that the previous value was visible and the new value that I was trying to set was overlapping it. I am now stuck.
Any advice/guidance would be very appreciated.

You have to set e.Handled = true to prevent the system from painting. The below code works as you expected.
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex != -1 && e.ColumnIndex == columnIndex)
{
if ((e.PaintParts & DataGridViewPaintParts.Background) != DataGridViewPaintParts.None)
{
e.Graphics.DrawImage(Resources.Image1, e.CellBounds);
}
if (!e.Handled)
{
e.Handled = true;
e.PaintContent(e.CellBounds);
}
}
}

Related

The program hangs when I try to replace image?

I have the following code:
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
DataGridView dgv = sender as DataGridView;
if (dgv.Columns[e.ColumnIndex].Name.Equals("edit"))
{
string status = dataGridView1.Rows[e.RowIndex].Cells["status"].Value.ToString();
if (status == "1")
{
dgv.Rows[e.RowIndex].Cells["edit"].Value = Properties.Resources.edit_disable;
}
}
}
When I try to replace image here:
dgv.Rows[e.RowIndex].Cells["edit"].Value = Properties.Resources.edit_disable;
Program hangs and image and is rendered infinity
You selected the wrong event for changing an Image. The event dataGridView1_CellFormatting is fired when an image changes, so if you use this event to change an image, you are getting into an infinite loop.
Since your code is querying the cell's Value, you might want to switch to a different event, which is fired when the row / cell data changes or binds, such as DataGridView.DataBindingComplete or dataGridView1.RowsAdded:
private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
string status = dataGridView1.Rows[e.RowIndex].Cells["status"].Value.ToString();
if (status == "1")
{
dgv.Rows[e.RowIndex].Cells["edit"].Value = Properties.Resources.edit_disable;
}
}

C# ovalshape on the front on listview

I have listview and it doesn't support image in second column. I try create ovalshape, but it olways is on the background of listview. So nobody can see it. I try ovalShape1.BringToFront();, but it doesn't work. Anybody can help me?
Here is a simple example with an external variable.
I assume you have added your images to an ImageList imageList1.
int imgIndex = 0;
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
if (e.ColumnIndex == 1)
e.Graphics.DrawImage(imageList1.Images[imgIndex], e.Bounds);
else e.DrawDefault = true;
}
private void button1_Click(object sender, EventArgs e)
{
imgIndex++;
listView1.Invalidate();
}
You would more likely want to get the imageindex from data in the ListViewItem separately for each Row, maybe like this:
//..
e.Graphics.DrawImage(imageList1.Images[Convert.ToInt16( e.SubItem.Text) ], e.Bounds);
//..
As usual error checking is up to you.
Also note the Invalidate, which triggers the Paint and the subsequent DrawSubItem event, necessary after you have changed the imageindex. When the underlying values change, the system will take care of that.

Hightlight Listbox item on mouse over event

I am attempting to change a listview item's background colour when a mouse hovers over it
I have a mouse hover event, but how can I add a "highlight" effect upon a mouse hovering over the item?
private void pinnedAppsListBox_MouseHover(object sender, EventArgs e)
{
}
Use this:
private void pinnedAppsListBox_MouseHover(object sender, EventArgs e){
Point point = pinnedAppsListBox.PointToClient(Cursor.Position);
int index = pinnedAppsListBox.IndexFromPoint(point);
if (index < 0) return;
//Do any action with the item
pinnedAppsListBox.GetItemRectangle(index).Inflate(1,2);
}
Go to the ListView's ItemMouseHover event and add then set the property "BackColor" of the Item.
private void listView1_ItemMouseHover(object sender, ListViewItemMouseHoverEventArgs e)
{
e.Item.BackColor = Color.Black;
}
Declare this Global variable
Use this Listview Item variable to keep track of what item was hovered on
ListViewItem lvHoveredItem;
Set the following function to turn on DoubleBuffering for your control to prevent flickering:
public static void SetDoubleBuffering(System.Windows.Forms.Control control, bool value)
{
System.Reflection.PropertyInfo controlProperty = typeof(System.Windows.Forms.Control)
.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
controlProperty.SetValue(control, value, null);
}
Where your control is loaded call this function
SetDoubleBuffering(lvTaskList, true);
Then use this code in the mousemove event of your listview
private void lvTaskList_MouseMove(object sender, MouseEventArgs e)
{
//Set the Color you want the list Item to be when mouse is over
Color oItemColor = Color.Lavender;
Color oOriginalColor = Color.blue; //Your original color
//get the Item the Mouse is currently hover
ListViewItem lvCurrentItem = lvTaskList.GetItemAt(e.X, e.Y);
if ((lvCurrentItem != null) && (lvCurrentItem != lvHoveredItem))
{
lvCurrentItem.BackColor = oItemColor;
if(lvHoveredItem != null)
{
lvHoveredItem.BackColor = oOriginalColor ;
}
lvHoveredItem = lvCurrentItem;
return;
}
if (lvCurrentItem == null)
{
if (lvHoveredItem != null)
{
lvHoveredItem.BackColor = oOriginalColor;
}
}
}
You can also add the MouseLeave Event
private void lvTaskList_MouseLeave(object sender, EventArgs e)
{
Color oOriginalColor = Color.Blue; //Your original color
//When the mouse leave the control. If a ListViewItem was highlighted then set it's original color back
if (lvHoveredItem != null)
{
lvHoveredItem.BackColor = oOriginalColor ;
}
lvHoveredItem = null;
}
If you're using a ListBox, it is quite more difficult to handle, you will need to set a MouseHover event for the ListBox and determine which item is being hovered on and then draw it manually.
See this answer.
However if you're using a ListView, you can easily add an ItemMouseHover event like this:
private void pinnedAppsListView_MouseHover(object sender, EventArgs e)
{
e.Item.BackColor = Color.Lime;
}
I have seen this question many times without a good answer.
There is no good answer that I know, but, I did it, using some hints elsewhere.
I did this using Lazarus, but you should be able to adapt it to your language.
Get the item. You may want to set variables to catch these individually.
You may also want to get the state of your mouse buttons at mousedown first.
If (ListView.GetItemAt(X,Y) <> nil) then // do this with an if else
// Next, you can get the bounding rect:
ListView.GetItemAt(X,Y).DisplayRect(drSelectBounds);
//Option: If Button up or down then
// you may have to catch this elsewhere, such as for a drag operation.
// Create and set a boolean variable:
HighLightOn := True;
ListView.Repaint; // clears previous hightlights
ListView.Canvas.Brush.Color := clBtnFace; // or your color of choice
ListView.Canvas.FillRect(Rect);
// If you are moving around in an area where GetItem is nil,
// then do this to stop flicker and remove the highlight:
If (ListView.GetItemAt(X,Y) = nil) // do this with an if else
If HighLightOn then
begin
SelectedList.Repaint;
HighLightOn := False;
end;
// If a highlight gets left behind,
// you may need to repeat this elsewhere, such as in a component exit.
// This is the basic gist of the issue.
// There can be a lot of options or things to look for,
// so you code could get more complicated.
// I am not suggesting this is the best way to implement it,
// but it is easy. Part of this code only works inside your app!

How can I stop cells from being entered when a datagridview row is selected by a row header click?

Whenever a user clicks the row header, which selects the whole row (and highlights it blue), the cell in the first column is actually entered. That is, if you start typing stuff, the text goes in that cell. I want to prevent this. Would it be possible to have no datagridview cells being entered when one or many rows are selected?
I also need the solution to prevent cells being entered during multiple row selections caused by clicking and dragging on the row headers.
Any ideas on how I could achieve this?
Thanks
Isaac
Set your DataGridView's ReadOnly property to true or false, depending on whether one or more rows have been selected or if the DGV's 'CellState' has changed.
Add the following two events for your DataGridView:
private void dataGridView1_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) {
if (e.StateChanged == DataGridViewElementStates.Selected) {
Console.WriteLine("TRUE");
dataGridView1.ReadOnly = true;
}
}
private void dataGridView1_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e) {
if (e.StateChanged == DataGridViewElementStates.Selected) {
Console.WriteLine("false");
dataGridView1.ReadOnly = false;
}
}
This worked for me in my tests but I wouldn't be surprised if there were hidden 'gotchas.'
An alternative solution may be something like this:
private void dataGrid_CellEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && dataGrid.Rows[e.RowIndex].Selected)
return;
}
Based on the answer of "Jay R" I adjusted the code a bit to not lose readonly flags of cells.
private void dataGridView1_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
{
if (e.StateChanged == DataGridViewElementStates.Selected)
e.Row.DataGridView.EditMode = DataGridViewEditMode.EditProgrammatically;
}
private void dataGridView1_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
{
if (e.StateChanged == DataGridViewElementStates.Selected)
// adjust the edit mode to your "default" edit mode if you have to
e.Cell.DataGridView.EditMode = DataGridViewEditMode.EditOnEnter;
}

Need help editing multiple cells in a datagridview

I am trying to support editing multiple cells on a datagridview. I am nearly complete, as it correctly copies the contents to other cells when the editing is done. What I am working on now is capturing the first key pressed.
When I am editing just one cell, using EditOnKeystrokeOrF2 works fine. However, when multiple cells are selected, I am capturing the Keydown event and manually calling BeginEdit. When I do that, however, the pressed key isn't included in the edit.
How can I get that first key pressed into my cell?
I did some additional experimenting and found a way to make this happen. It is a bit sloppy, but it works.
private int _keyValue;
private Boolean _checkKeyValue = false;
private void Grid1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
DataGridViewCell cell = Grid1.Rows[e.RowIndex].Cells[e.ColumnIndex];
if (_checkKeyValue)
{
_checkKeyValue = false;
if (value != -1)
{
cell.Value = _keyValue;
}
}
}
private void Grid1_KeyDown(object sender, KeyEventArgs e)
{
if (Grid1.SelectedCells.Count > 1)
{
_checkKeyValue = true;
_keyValue = (int)e.KeyValue;
Grid1.BeginEdit(false);
}
}
By registering for the CellBeginEdit event, I can plop the value in there. I do some other processing of the _keyValue to make it a number, but that isn't relevant to the rest of this.
May be it is sufficent to mark the key as not handled.
private void dataGridView_KeyDown(Object sender, KeyEventArgs keyEventArgs)
{
keyEventArgs.Handled = false;
}

Categories