Showing a tooltip inside a datagrid - winforms

I'm trying to show a windows forms tooltip inside a datagrid to highlight an error. The problem I have is that everytime I call tooltip.Show("You have an error", datagrid, 0, 0), The tooltip is confined within the datagrids boundaries and doesn't go outside, which ultimately means the tooltip itself covers up the actual row where the error occurs.
I thought about tooltip.Show("You have an error", Form1, ?, ?) but I don't see an easy way to compute the offset of the datagrid on the form. Since all controls are docked, depending on how the user resizes the form, the location will change.
There is a caveat, the datagrid itself is not a Forms.DataGrid, instead it is an Infragistics UltraGrid which may do funny things itself, which are outside of my ability to alter.

It turns out that it's easy enough to get the location for the Show command from the UltraGrid by querying the UIElement associated with it. Here's what I'm doing:
private void ultraGrid1_BeforeCellUpdate(object sender, BeforeCellUpdateEventArgs e)
{
if (!DataFormat.CanEdit(e.Cell.Row.ListObject, e.Cell.Column.PropertyDescriptor))
{
var tip = new System.Windows.Forms.ToolTip();
tip.BackColor = Color.Orange;
tip.Show("unable to edit", this, e.Cell.GetUIElement().Rect.Left, e.Cell.GetUIElement().Rect.Top, 500);
e.Cancel = true;
}
}

Have you looked at these:
HOWTO:Create Advanced ToolTips For The WinGrid
BeforeDisplayDataErrorTooltip Event

Related

DataGridViewComboBoxCell back color changes

DataGridViewComboBoxCell displays normal when application launched
After selecting the value, background changes to black (see below) on the current row and subsequent rows. I have used default DataGridView and no font manipulation
I have tried changing color in CellFormatting and CellMouseClick events. But still the same behavior. Any ideas?
Apparently, this is documented bug with DataGridViewComboBoxColumn. Link with solution and workaround
https://nickstips.wordpress.com/2010/12/20/c-datagridviewcomboboxcolumn-drop-down-menu-appears-all-black/
C#: DataGridViewComboBoxColumn Drop Down Menu Appears All Black
December 20, 2010 — Nick Olsen
I ran into an issue today using the DataGridView where one of the columns defined as a DataGridViewComboBoxColumn appeared with the drop down menu completely black as shown below.
After some research I found out that there is a documented bug in the DataGridViewComboBoxColumn where this sometimes occurs if you are handling the EditingControlShowing event of the DataGridView. I am handling this event in order to wire up the SelectedIndexChanged event of the ComboBox embedded in the DataGridView cell.
On the bug report, Microsoft states that they will not be fixing this bug but thankfully, Debanjan1 has posted a workaround for this issue. If you simply set the CellStyle.BackColor property to the DataGridView.DefaultCellStyle.BackColor in the EditingControlShowing event, the problem goes away. This is shown below.
private void dataGridViewGLEntries_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cmbBx = e.Control as ComboBox;
if (cmbBx != null)
{
cmbBx.SelectedIndexChanged -= ComboBoxCell_SelectedIndexChanged;
cmbBx.SelectedIndexChanged += ComboBoxCell_SelectedIndexChanged;
// Fix the black background on the drop down menu
e.CellStyle.BackColor = this.dataGridViewGLEntries.DefaultCellStyle.BackColor;
}
}

How to detect styling applied to DataGridView via e.CellStyle.BackColor in the CellFormatting event?

If we've applied styling (e.CellStyle.BackColor say) to some rows via the CellFormatting event of a DataGridView, is it then possible to detect that styling at a later stage?
For example, currently we use a generic bloc of code to handle printing and exporting to Excel for any and all our DataGridViews. Until now the code hasn't catered for any styling.
So we want to add it in.
If we check the .DefaultCellStyle of the row or cell then our styling doesn't show up (it just shows as 0 or Black, which is completely wrong).
I assume that's because we've applied the styling via a CellFormatting event, instead of embedding it into the DefaultCellStyle.
Unfortunately I could not find a complete solution to your issue, only a work around.
Some experimenting with the CellFormatting event using the example from MSDN resulted in me seeing exactly what you were seeing - the BackColor was clearly being set but the CellStyle was not reflecting that. 1
The work around I found was to not use the DataGridViewCellFormattingEventArgs CellStyle property but to instead go straight to the grid. This has the downside that you now need to manually handle the case where you do not want to format the cell.
I've got some code below showing this - it is again just modifying the MSDN code:
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// If the column is the Artist column, check the
// value.
if (this.dataGridView1.Columns[e.ColumnIndex].Name == "Artist")
{
if (e.Value != null)
{
// Check for the string "pink" in the cell.
string stringValue = (string)e.Value;
stringValue = stringValue.ToLower();
if ((stringValue.IndexOf("pink") > -1))
{
// With the commented line below we cannot access the new style
//e.CellStyle.BackColor = Color.Pink;
// With this line we can!
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = Color.Pink;
}
else
{
// With the original MSDN code the else block to reset the
// cell style was not needed.
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = dataGridView1.DefaultCellStyle.BackColor;
}
}
}
}
1. My theory is that this is similar to the confusion people have over the .Refresh() method, where the DataGridView has two very distinct views of itself, one being the rectangle painted on screen and the other being the underlying data. With the .Refresh() method you only repaint the rectangle, you do not refresh the data. I think this is like that - the CellFormatting event only formats during painting and doesn't do anything to the grid styles themselves.
A possible solution would be to add a second handler to the generic printing block of code (just before the actual printing). This handler should be attached to the CellFormatting event and only save the e.cellstyle in a temporary storage (such as a dictionary of cellstyles).
All cellstyles applied during your original cellformatting will be readable in your generic printing code without the need of adjusting specific cellformatting-events that have been tied to the datagridview.
At the end of the printing you can remove the handler again.
See also Is there a way to force a DataGridView to fire its CellFormatting event for all cells?

MVVM WPF datagrid data entry problem

I have a datagrid in my MVVM application which, because of the way the client wants the data displayed, needs use template columns. They want some typical data-entry functionality though (pressing enter performs data checking, commits the row if valid, focuses on the first textbox of the next row; pressing tab focuses the next textbox...). Also, data is often imported from an external source into the grid, usually thousands of records at a time.
Right now, I have a Loaded event hooked up to each textbox which I am using to set focus after new rows are added. My problem is that the grid goes haywire when I import a lot of rows. As the user scrolls, the Loaded events are fired, and the grid becomes basically unusable. I disabled virtualization to prevent this, and find my grid taking up a gig of RAM in certain configurations, which is unacceptable. I can't figure out a way to get this grid to work the way they require without using a huge amount of memory. It seems as if I just need to be able to focus a textbox within a newly added row, but since the data validation is performed in the viewmodel, I don't have access to the new row in codebehind, so I can't just call "newtextbox.focus()" or whatever. I'm getting pretty desperate here, any suggestions would be massively appreciated.
Put an event listener in the code behind that can call your newtextbox.focus() (and whatever else you want to do). In the validation in the view model, fire the event with args that indicate what you want your grid to do.
Edit: OK, new approach. Try trapping the keystrokes, and on enter or tab do the things you want it to do.
This would be in your xaml
<Grid KeyUp="myDataGrid_KeyUp" >
This would go in your code-behind
private void myDataGrid_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
// do your enter stuff here, manipulate the view model for validation, etc.
}
else if (e.Key == Key.Tab)
{
// do your tab stuff here (get selected row, determine cell focus, focus on next cell)
}
}

Selecting an item in a ListView control ( winforms ) while not having the focus

I am trying to mimic the functionality of the address book in Outlook
So basically a user starts typing in some text in an edit control and a matching ListView Item is selected
private void txtSearchText_TextChanged(object sender, EventArgs e)
{
ListViewItem lvi =
this.listViewContacts.FindItemWithText(this.txtSearchText.Text,true, 0);
if (lvi != null)
{
listViewContacts.Items[lvi.Index].Selected = true;
listViewContacts.Select();
}
}
The problem with this is once the listview item gets selected the user cant keep typing into the text Box. Basically I want a way to highlight an item in the listview while still keeping the focus on the edit control
This is WINFORMS 2.0
Manually setting ListViewItem.BackColor is not good a solution, especially if you want the item to get the selected state, because it only works on unselected items. So you had to take care of several situations to make it look right in all cases (really select the item as soon as the ListView gets focus, undo the color changes, and so on...)
It seems the only good way is to use Ownerdraw or an extended ListView like ObjectListView.
I was looking for the same and I still hope for a better/smarter solution, or at least a nice and short Ownerdraw implementation.
Update
I found a better solution for me: I now use a DataGridView for the same purpose (which also has other advantages in my case, since the data comes from a db anyway, but it would also work without db). There the selection bar doesn't change color when loosing focus. You can try a few properties to make it look like a ListView:
dgv.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None;
dgv.ColumnHeadersVisible = false;
dgv.MultiSelect = false;
dgv.ReadOnly = true;
dgv.RowHeadersVisible = false;
dgv.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
dgv.StandardTab = true;
ok never mind it's doable by just manipulating the background colour of the selected item

Out of range error when closing pop-up

I have a Silverlight control that appears on top of another (picture a pop-up box). In this pop-up control, I have a scrollview (height = 250) with a stack panel (instance name = spMain, orientation = vertical) inside. Within the contents of the stack panel are several textboxes stacked on top of each other. When I tab from textbox to textbox, the scrollviewer automatically moves toward the bottom (I wrote code in an event handler that all textboxes are linked to that does that).
The problem I'm having is when I attempt to close out the pop-up control, I'm receiving an error stating that the value does not fall within the expected range.
if (sender is TextBox)
{
TextBox tb = (TextBox)sender;
try
{
// Code bombs out here when I attempt to close out the pop-up control
Point pt = tb.TransformToVisual(spMain).Transform(new Point());
if (pt.Y >= scrollViewerHeight - tb.ActualHeight)
{
svMain.UpdateLayout();
svMain.ScrollToVerticalOffset(scrollViewerHeight += pt.Y);
}
}
catch (ArgumentException aex)
{
// Don't want to eat the exception
string errorMessage = aex.Message;
System.Diagnostics.Debug.WriteLine(errorMessage);
}
}
I'm not surprised I'm getting the error, because it appears to make sense, but what I'm looking for is some sort of User Control Unloaded event or prevent the offending code from executing.
Does anyone have any ideas on how to go about this?
I think the problem lies with how you're closing the popup. Are you removing it from the visual tree or just setting its visibility to collapsed?
If (as I think your problem suggests) you're removing it from the visual tree entirely, you may be able to solve the immediate problem by collapsing the visibility of your text boxes first, and then removing the control. That assumes that your code above is being called as a result of a resize (or a potential resize) of the textboxes. Alternately, you could just empty them of content before you remove them also.
As for an Unloaded event, there isn't anything in the framework that'll do that for you. You could write a custom unload method for your control easily enough though, and just use that when you want to remove it.

Resources