How to increase display time of DataGridView's Column header tooltips? - vb.net

I currently set the tooltips of my DGV column headers like so:
dgv.Columns(1).ToolTipText = "Some Text"
Currently, the text will be displayed for about 5 seconds. I want to increase it to 10 seconds. I am setting my tool tips like this because for each of my DataGridViews, I will have about 20 separate Tool Tip Headers for 20 columns.

As far as I know, there is no publicly exposed property that will allow you to change the ToolTip.AutoPopDelay. You will need to resort to Reflection and the DataGridView source code to hack a solution.
The DataGridView has a field named toolTipControl that is an instance of DataGridViewToolTip. DataGridViewToolTip has a field named toolTip that is a System.Windows.Forms.ToolTip. This field is initialized in the DataGridViewToolTip.Activate method.
Using this information, the following code can be constructed to change the AutoPopDelay.
Private Shared Sub SetDGVToolTipDelay(dgv As DataGridView)
Dim fi_toolTipControl As FieldInfo = GetType(DataGridView).GetField("toolTipControl", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim toolTipControl As Object = fi_toolTipControl.GetValue(dgv)
Dim fi_ToolTip As FieldInfo = fi_toolTipControl.FieldType.GetField("toolTip", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim tt As ToolTip = CType(fi_ToolTip.GetValue(toolTipControl), ToolTip)
If tt Is Nothing Then
tt = New ToolTip
tt.ShowAlways = True
tt.InitialDelay = 0
tt.UseFading = False
tt.UseAnimation = False
fi_ToolTip.SetValue(toolTipControl, tt)
End If
tt.AutoPopDelay = 10000 ' 10 seconds
End Sub
Just call SetDGVToolTipDelay and pass the DataGridView instance that you want to change the delay on.

Related

How to get Design > Name property of DGV column from Header Text property

I am trying to go through un-checked items within a CheckedListBox1 and based on the values returned hide relevant columns within DataGridView1 but the issue is that the values displayed in CheckedListBox1 are the HeaderText property of DGV column and not the Name property which is required for hiding the column within DGV.
See below code:
For Each checked_item As Object In CheckedListBox1.Items
If Not CheckedListBox1.CheckedItems.Contains(checked_item) Then
DataGridView1.Columns("").Visible = False
End If
Next
Is there a way to retrieve "Name" property of DGV column when referencing the column's HeaderText property?
You don't need the column name to hide the column. You need the column. The name is just a means to get the column. The issue is the way you're populating your CheckedListBox. Displaying the HeaderText makes perfect sense, because that's what the user actually sees. What you should be doing is putting the columns themselves into the CheckedListBox and just displaying the HeaderText. That way, the items are the columns, e.g.
Dim columns = DataGridView1.Columns.Cast(Of DataGridViewColumn)().ToArray()
With CheckedListBox1
.DataSource = columns
.DisplayMember = NameOf(DataGridViewColumn.HeaderText)
End With
The code you posted then becomes this:
For i = 0 To CheckedListBox1.Items.Count - 1
Dim column = DirectCast(CheckedListBox1.Items(i), DataGridViewColumn)
column.Visible = CheckedListBox1.GetItemChecked(i)
Next
Note that you should generally set the DataSource last when binding but that doesn't seem to work with a CheckedListBox, which doesn't offically support data-binding. For that reason, the DataSource is set first.
EDIT:
I'm adding this after the comment was added to the question about the ItemCheck event and the checking of the items at startup. The key here is to not actually act on the event until the list has been initialised, i.e. all the items have been initially checked. One way to do that would be like so:
Private isLoaded As Boolean = False
Private Sub Form1_Load(...) Handles MyBase.Load
'Bind the data and check the items in the CheckedListBox here.
isLoaded = True
End Sub
Private Sub CheckedListBox1_ItemCheck(...) CheckedListBox1.ItemCheck
If isLoaded Then
'Act here.
End If
End Sub
The other way to is to prevent event being raised by not handling it while the initialisation is taking place. That can be done in a couple of ways but I'll leave that to you as an exercise if that's what you want to do.
As the ItemCheck event is raised before the state of an item changes, you will need to treat the current item differently to the other items. My loop above would become this:
For i = 0 To CheckedListBox1.Items.Count - 1
Dim column = DirectCast(CheckedListBox1.Items(i), DataGridViewColumn)
'For the item that is being checked/unchecked, use its new state.
'For other items, use their current state.
column.Visible = If(i = e.Index,
e.NewValue = CheckState.Checked,
CheckedListBox1.GetItemChecked(i))
Next
That said, if all items are initially checked and all columns are initially visible, it's only the current item that you need to care about, so there's no need for a loop at all:
Dim column = DirectCast(CheckedListBox1.Items(e.Index), DataGridViewColumn)
column.Visible = (e.NewValue = CheckState.Checked)
Try this:
For Each checked_item As Object In CheckedListBox1.Items
Dim oCol As DataGridViewColumn = DataGridView1.Columns _
.Cast(Of DataGridViewColumn)() _
.Where(Function(x) x.HeaderText = checked_item).SingleOrDefault()
If oCol IsNot Nothing Then oCol.Visible = _
Not CheckedListBox1.CheckedItems.Contains(checked_item)
Next
Note: System.Linq is required!
[EDIT]
this code is executed in the .ItemCheck event. When the form starts up
I loop through all available columns, populate CheckedBoxList1 and as
default they are un-checked but I want them checked as I want the
columns to be visible at start
If you would like to change the visibility of column of currently selected item in CheckListBox (in ItemCheck event), use this:
Dim oCol As DataGridViewColumn = DataGridView1.Columns _
.Cast(Of DataGridViewColumn)() _
.Where(Function(x) x.HeaderText = checked_item).SingleOrDefault()
If oCol IsNot Nothing Then oCol.Visible = _
Not CheckedListBox1.CheckedItems.Contains(CheckedListBox1.SelectedItem)
Do you see the difference?
The main difference is: there's no foreach loop ;)

Visual Basic Iterative enabling of Textbox's

Si I'm working on an assignment where I have 10 RadioButtons indicating how many contesters I have, and depending on what I pick between 1 to 10, I need that many of my corresponding TextBoxes to be enabled so I could fill it with names!
Is there a way for me to make a For loop between 1 and the number I picked from the RadioButton and say something like
For i = 0 to Size
{
TextBox&i.Enabled = True
}
Since my TextBoxs are called TextBox1 to TextBox10
I know you can add strings together using &, but how can I do that for an object name?
As of right now I literally have the dumbest way of doing it, which is a click event inside each RadioButton that manually enables the correct number of TextBoxes...
Thank you in advance!
You can iterate over all controls like this:
For Each ctr In Me.Controls
Dim indx As String = ctr.Name
If TypeOf (ctr) Is Textbox Then
' Now compare the name with TextBox&i and do smth
End If
Next
It's not possible to just concatenate a string and use it as an object variable reference like that, but you can search the form's controls by their name property (which is a string) and do it that way. Here's an example:
Private Sub EnableTextBoxes(ByVal Size As Integer)
For i As Integer = 1 To Size
Dim matches() As Control = Me.Controls.Find("Textbox" & i.ToString, True)
If matches IsNot Nothing AndAlso matches.Length = 1 Then matches(0).Enabled = True
Next
End Sub

DataFilter Interferes with AutoComplete on UltraCombo Inside UltraGrid

I have an UltraCombo set inside an UltraGrid, with AutoComplete set to "Suggest". The UltraCombo has a DisplayMember of "Name" and a ValueMember of "ID". What I've found is that when I attach a DataFilter to the UltraCombo (I'd like to make it appear blank when the value is zero), if they type a digit that happens to match an ID and also starts a Name, it will do the autocomplete, but the underlying value is never changed. So no AfterUpdate or CellChange is triggered, and when you leave the cell, it reverts to blank. How can I have AutoComplete work and still show the zero-value as blank? Here's my code (Note, if you comment out the line where UltraCombo1.DataFilter is set, updates work fine, but you lose the DataFiltering):
Imports Infragistics.Win.UltraWinGrid
Imports Infragistics.Win
Public Class Form1
Public Sub New()
InitializeComponent()
Dim datatableCombo = New DataTable
datatableCombo.Columns.Add("ID", GetType(Integer))
datatableCombo.Columns.Add("Name", GetType(String))
datatableCombo.Rows.Add({1, "123"})
datatableCombo.Rows.Add({2, "234"})
datatableCombo.Rows.Add({3, "456"})
UltraCombo1.DataFilter = New MyDataFilter()
UltraCombo1.DataSource = datatableCombo
UltraCombo1.ValueMember = "ID"
UltraCombo1.DisplayMember = "Name"
Dim position As Integer = 0
UltraCombo1.DisplayLayout.Bands(0).Columns("ID").Hidden = False
UltraCombo1.DisplayLayout.Bands(0).Columns("ID").Header.VisiblePosition = position
position += 1
UltraCombo1.DisplayLayout.Bands(0).Columns("Name").Hidden = False
UltraCombo1.DisplayLayout.Bands(0).Columns("Name").Header.VisiblePosition = position
position += 1
Dim datatableGrid = New DataTable
datatableGrid.Columns.Add("ID", GetType(Integer))
datatableGrid.Columns.Add("Name", GetType(String))
UltraGrid1.DataSource = datatableGrid
UltraGrid1.DisplayLayout.GroupByBox.Hidden = True
UltraGrid1.DisplayLayout.Override.RowSelectors = DefaultableBoolean.True
UltraGrid1.DisplayLayout.Override.AllowAddNew = AllowAddNew.TemplateOnBottom
UltraGrid1.DisplayLayout.Bands(0).Columns("ID").EditorComponent = UltraCombo1
UltraGrid1.DisplayLayout.Bands(0).Columns("ID").CellClickAction = CellClickAction.EditAndSelectText
UltraGrid1.DisplayLayout.Bands(0).Columns("ID").Style = ColumnStyle.DropDownValidate
End Sub
Public Class MyDataFilter
Implements Infragistics.Win.IEditorDataFilter
Public Function Convert(ByVal convertArgs As Infragistics.Win.EditorDataFilterConvertArgs) As Object Implements Infragistics.Win.IEditorDataFilter.Convert
' Shouldn't affect anything?
convertArgs.Handled = False
Return Nothing
End Function
End Class
End Class
You need to set AutoCompleteMode to the grid column and not to the Ultracombo. When UltraCombo is set as EditorComponent the grid uses its editor. If you set AutoCompleteMode to the combo in this situation it does not have any effect in the grid.
If you set AutoCompleteMode to the grid's column you will not need also to set DataFilter to the combo.
Turns out this was a bug in Infragistics 11.2. I'm not sure at what point they fixed it, but it doesn't happen with version 15.2.

Storing datagridview name in a variable

I am working on user rights. I want to load the grid items checked using following code.
Dim l As Integer = 0, vrGridName As New DataGridView, vrGridItemIndex As Integer
taSaveTemplates.Connection.ConnectionString += ";password=" & vrSAPWD
Me.taSaveTemplates.Fill(Me.DsSaveTemplates.tblTemplates, lstTemplateID.Text)
'Load Grids according to data saved
Do While DsSaveTemplates.tblTemplates.Rows.Count > l
vrGridName.Name = DsSaveTemplates.tblTemplates.Rows(l).Item("GridName")
vrGridItemIndex = DsSaveTemplates.tblTemplates.Rows(l).Item("GridItemIndex")
vrGridName.Item(0, vrGridItemIndex).Value = True
l = l + 1
Loop
vrGridName stores the name of grid selected from DB and vrGridItemIndex stores the item that needs to be checked.
The problem is, when I run the code, it says Index is our of range.
I have checked, the vrGridName does not store the name of grid but stores
System.windows.datagridview
Please advise.
Thanks
Your code is treating a control reference as if it were a name (string variable), so you get the Type name (System.windows.datagridview) rather than the name of the control. Since the template DGV happarently has the name, use it:
Dim myGridName As String ' name is a String, not DGV
Dim myGridItemIndex As Integer
Dim myDGV As DataGridView ' no NEW - not creating a new one
' just a reference var
'...
' this is now a For/Each loop
For Each row As DataGridViewRow in DsSaveTemplates.tblTemplates.Rows
myGridName = row.Cell("GridName")
myGridItemIndex = row.Cell("GridItemIndex")
' assuming this code is in a Form:
' get a reference to the control
myDGV = CType(Me.Controls(myGridName), DataGridView)
' talk to it like a DGV:
myDGV.Item(0, myGridItemIndex).Value = True
Next
Note: Option Strict would likely require some conversions for the name and index
If the DGV(s) reside in container controls like Panels or Tabs, you have to "find" the control because they will be in that control's collection, not the Form's. Instead of myDGV = CType(Me.Controls(myGridName), DataGridView):
' have the form search for the ctl by name
' the TRUE param tells it to search child controls like panels
Dim tmpArry = Me.Controls.Find(myGridName, True)
' test for a return
If tmpArry.Length > 0 Then
' tmpArry will be Type Control, so cast it
myDGV = CType(tmpArry(0), DataGridView)
End If
This is usually better from the start so you do not have to remember how the form is laid out when coding.

Access a form's control by name

not sure whether the title of this post is accurate.
I'm trying to access windows form controls and their properties by "composing" their name within a loop, but I can't seem to find the related documentation. Using VB.net. Basically, say I have the following:
Dim myDt As New DataTable
Dim row As DataRow = myDt.NewRow()
row.Item("col01") = Me.label01.Text
row.Item("col02") = Me.label02.Text
'...
row.Item("colN") = Me.labelN.Text
I'd like to write a for loop instead of N separate instructions.
While it's simple enough to express the left-hand side of the assignments, I'm stumped when it comes to the right-hand side:
For i As Integer = 1 to N
row.Item(String.format("col{0:00}", i)) = ???
' ??? <- write "label" & i (zero-padded, like col) and use that string to access Me's control that has such name
Next
As an extra, I'd like to be able to pass the final ".Text" property as a string as well, for in some cases I need the value of the "Text" property, in other cases the value of the "Value" property; generally speaking, the property I'm interested in might be a function of i.
Cheers.
You could use the ControlsCollection.Find method with the searchAllChildren option set to true
For i As Integer = 1 to N
Dim ctrl = Me.Controls.Find(string.Format("label{0:00}", i), True)
if ctrl IsNot Nothing AndAlso ctrl.Length > 0 Then
row.Item(String.format("col{0:00}", i)) = ctrl(0).Text
End If
Next
An example on how to approach the problem using reflection to set a property that you identify using a string
Dim myLabel As Label = new Label()
Dim prop as PropertyInfo = myLabel.GetType().GetProperty("Text")
prop.SetValue(myLabel, "A Label.Text set with Reflection classes", Nothing)
Dim newText = prop.GetValue(myLabel)
Console.WriteLine(newText)